From 4898db80cb7539a06e234c65aaaac85883209e38 Mon Sep 17 00:00:00 2001 From: rish9101 Date: Mon, 20 Jul 2020 01:12:28 +0530 Subject: Add post-process functionality in write_with_gap --- src/afl-fuzz-run.c | 61 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 2a1664e2..f8317863 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -142,18 +142,58 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, s32 fd = afl->fsrv.out_fd; u32 tail_len = len - skip_at - skip_len; + /* We first copy the mem into a new memory region removing the gaps + and then carry out any post-processing work on them. Then copy them out to + shared-mem or write to file */ + + void *mem_trimmed = + ck_alloc(skip_at + tail_len + + 1); // 1 extra size allocated to remove chance of overflow + + if (skip_at) { memcpy(mem_trimmed, mem, skip_at); } + + if (tail_len) { + + memcpy(mem_trimmed + skip_at, (u8 *)mem + skip_at + skip_len, tail_len); + + } + + ssize_t new_size = skip_at + tail_len; + void * new_mem = mem_trimmed; + u8 * new_buf = NULL; + + if (unlikely(afl->custom_mutators_count)) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (el->afl_custom_post_process) { + + new_size = + el->afl_custom_post_process(el->data, new_mem, new_size, &new_buf); + + } + + new_mem = new_buf; + + }); + + } + if (afl->fsrv.shmem_fuzz) { - if (skip_at) { memcpy(afl->fsrv.shmem_fuzz, mem, skip_at); } + if ((new_buf)) { + + memcpy(afl->fsrv.shmem_fuzz, new_buf, new_size); + + } - if (tail_len) { + else { - memcpy(afl->fsrv.shmem_fuzz + skip_at, (u8 *)mem + skip_at + skip_len, - tail_len); + memcpy(afl->fsrv.shmem_fuzz, mem_trimmed, new_size); } - *afl->fsrv.shmem_fuzz_len = len - skip_len; + *afl->fsrv.shmem_fuzz_len = new_size; #ifdef _DEBUG if (afl->debug) { @@ -197,18 +237,19 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, } - if (skip_at) { ck_write(fd, mem, skip_at, afl->fsrv.out_file); } + if (new_buf) { - u8 *memu8 = mem; - if (tail_len) { + ck_write(fd, new_buf, new_size, afl->fsrv.out_file); + + } else { - ck_write(fd, memu8 + skip_at + skip_len, tail_len, afl->fsrv.out_file); + ck_write(fd, mem_trimmed, new_size, afl->fsrv.out_file); } if (!afl->fsrv.out_file) { - if (ftruncate(fd, len - skip_len)) { PFATAL("ftruncate() failed"); } + if (ftruncate(fd, new_size)) { PFATAL("ftruncate() failed"); } lseek(fd, 0, SEEK_SET); } else { -- cgit 1.4.1 From 2fa31dab60e76ee1a4b77d2d98d58e0e35455880 Mon Sep 17 00:00:00 2001 From: rish9101 Date: Thu, 23 Jul 2020 23:48:26 +0530 Subject: Remove reduntant copying from write_with_gap function --- src/afl-fuzz-run.c | 66 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index f8317863..7d68083d 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -142,35 +142,55 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, s32 fd = afl->fsrv.out_fd; u32 tail_len = len - skip_at - skip_len; - /* We first copy the mem into a new memory region removing the gaps - and then carry out any post-processing work on them. Then copy them out to - shared-mem or write to file */ - - void *mem_trimmed = - ck_alloc(skip_at + tail_len + - 1); // 1 extra size allocated to remove chance of overflow - - if (skip_at) { memcpy(mem_trimmed, mem, skip_at); } - - if (tail_len) { - - memcpy(mem_trimmed + skip_at, (u8 *)mem + skip_at + skip_len, tail_len); - - } + /* + This memory is used to carry out the post_processing(if present) after copying + the testcase by removing the gaps + */ + u8 mem_trimmed[skip_at + tail_len + + 1]; // 1 extra size to remove chance of overflow ssize_t new_size = skip_at + tail_len; - void * new_mem = mem_trimmed; + void * new_mem = mem; u8 * new_buf = NULL; + bool post_process_skipped = true; + if (unlikely(afl->custom_mutators_count)) { + new_mem = mem_trimmed; + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { if (el->afl_custom_post_process) { + // We copy into the mem_trimmed only if we actually have custom mutators + // *with* post_processing installed + + if (post_process_skipped) { + + if (skip_at) { memcpy(mem_trimmed, (u8 *)mem, skip_at); } + + if (tail_len) { + + memcpy(mem_trimmed + skip_at, (u8 *)mem + skip_at + skip_len, + tail_len); + + } + + post_process_skipped = false; + + } + new_size = el->afl_custom_post_process(el->data, new_mem, new_size, &new_buf); + if (unlikely(!new_buf && (new_size <= 0))) { + + FATAL("Custom_post_process failed (ret: %lu)", + (long unsigned)new_size); + + } + } new_mem = new_buf; @@ -181,7 +201,9 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, if (afl->fsrv.shmem_fuzz) { - if ((new_buf)) { + if (!post_process_skipped) { + + // If we did post_processing, copy directly from the new_buf bufer memcpy(afl->fsrv.shmem_fuzz, new_buf, new_size); @@ -189,7 +211,9 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, else { - memcpy(afl->fsrv.shmem_fuzz, mem_trimmed, new_size); + memcpy(afl->fsrv.shmem_fuzz, mem, skip_at); + + memcpy(afl->fsrv.shmem_fuzz, mem + skip_at + skip_len, tail_len); } @@ -237,13 +261,15 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, } - if (new_buf) { + if (!post_process_skipped) { ck_write(fd, new_buf, new_size, afl->fsrv.out_file); } else { - ck_write(fd, mem_trimmed, new_size, afl->fsrv.out_file); + ck_write(fd, mem, skip_at, afl->fsrv.out_file); + + ck_write(fd, mem + skip_at + skip_len, tail_len, afl->fsrv.out_file); } -- cgit 1.4.1 From 565da10a8f46e9910ac5edecb1c5e68ee8c66b0d Mon Sep 17 00:00:00 2001 From: Rishi Ranjan Date: Wed, 29 Jul 2020 01:05:05 +0530 Subject: Minor change to write_with_gap --- src/afl-fuzz-run.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 7d68083d..e4ddab1b 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -144,12 +144,12 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, /* This memory is used to carry out the post_processing(if present) after copying - the testcase by removing the gaps + the testcase by removing the gaps. This can break though */ - u8 mem_trimmed[skip_at + tail_len + + u8 mem_trimmed[len - skip_len + 1]; // 1 extra size to remove chance of overflow - ssize_t new_size = skip_at + tail_len; + ssize_t new_size = len - skip_len; void * new_mem = mem; u8 * new_buf = NULL; -- cgit 1.4.1 From 35a448ee921158c586177ff8fe0cd82da4345f68 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 30 Jul 2020 09:20:22 +0200 Subject: enhance for custom trim buffer --- src/afl-fuzz-run.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 01963f8f..691d32f8 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -134,6 +134,8 @@ void write_to_testcase(afl_state_t *afl, void *mem, u32 len) { } +#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size + /* The same, but with an adjustable gap. Used for trimming. */ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, @@ -146,8 +148,7 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, This memory is used to carry out the post_processing(if present) after copying the testcase by removing the gaps. This can break though */ - u8 mem_trimmed[len - skip_len + - 1]; // 1 extra size to remove chance of overflow + u8 *mem_trimmed = ck_maybe_grow(BUF_PARAMS(out_scratch), len - skip_len + 1); ssize_t new_size = len - skip_len; void * new_mem = mem; @@ -286,6 +287,8 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, } +#undef BUF_PARAMS + /* Calibrate a new test case. This is done when processing the input directory to warn about flaky or otherwise problematic test cases early on; and when new paths are discovered to detect variable behavior and so on. */ -- cgit 1.4.1 From fc401f1acc61b73f328a16ac10bed268134c495e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 30 Jul 2020 11:51:13 +0200 Subject: fix post process check --- src/afl-fuzz-run.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 691d32f8..44d3c522 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -185,7 +185,7 @@ static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, new_size = el->afl_custom_post_process(el->data, new_mem, new_size, &new_buf); - if (unlikely(!new_buf && (new_size <= 0))) { + if (unlikely(!new_buf || (new_size <= 0))) { FATAL("Custom_post_process failed (ret: %lu)", (long unsigned)new_size); -- cgit 1.4.1 From ea9ba53cdbc6d175f3f055c9a308668ebaacda1e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 30 Jul 2020 17:09:22 +0200 Subject: fix oob reads, code-format --- examples/aflpp_driver/aflpp_driver.c | 168 ++++++++++++++++++------------ examples/aflpp_driver/aflpp_driver_test.c | 16 +-- src/afl-fuzz-queue.c | 55 +++++----- src/afl-fuzz-redqueen.c | 3 +- test/test-floatingpoint.c | 8 +- 5 files changed, 146 insertions(+), 104 deletions(-) (limited to 'src') diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c index eca3dcd1..86c7a69f 100644 --- a/examples/aflpp_driver/aflpp_driver.c +++ b/examples/aflpp_driver/aflpp_driver.c @@ -14,12 +14,15 @@ cat << EOF > test_fuzzer.cc #include #include extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'H') if (size > 1 && data[1] == 'I') if (size > 2 && data[2] == '!') __builtin_trap(); return 0; + } + EOF # Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang. clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c @@ -57,46 +60,46 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. #include "config.h" #ifdef _DEBUG -#include "hash.h" + #include "hash.h" #endif // Platform detection. Copied from FuzzerInternal.h #ifdef __linux__ -#define LIBFUZZER_LINUX 1 -#define LIBFUZZER_APPLE 0 -#define LIBFUZZER_NETBSD 0 -#define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 0 + #define LIBFUZZER_LINUX 1 + #define LIBFUZZER_APPLE 0 + #define LIBFUZZER_NETBSD 0 + #define LIBFUZZER_FREEBSD 0 + #define LIBFUZZER_OPENBSD 0 #elif __APPLE__ -#define LIBFUZZER_LINUX 0 -#define LIBFUZZER_APPLE 1 -#define LIBFUZZER_NETBSD 0 -#define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 0 + #define LIBFUZZER_LINUX 0 + #define LIBFUZZER_APPLE 1 + #define LIBFUZZER_NETBSD 0 + #define LIBFUZZER_FREEBSD 0 + #define LIBFUZZER_OPENBSD 0 #elif __NetBSD__ -#define LIBFUZZER_LINUX 0 -#define LIBFUZZER_APPLE 0 -#define LIBFUZZER_NETBSD 1 -#define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 0 + #define LIBFUZZER_LINUX 0 + #define LIBFUZZER_APPLE 0 + #define LIBFUZZER_NETBSD 1 + #define LIBFUZZER_FREEBSD 0 + #define LIBFUZZER_OPENBSD 0 #elif __FreeBSD__ -#define LIBFUZZER_LINUX 0 -#define LIBFUZZER_APPLE 0 -#define LIBFUZZER_NETBSD 0 -#define LIBFUZZER_FREEBSD 1 -#define LIBFUZZER_OPENBSD 0 + #define LIBFUZZER_LINUX 0 + #define LIBFUZZER_APPLE 0 + #define LIBFUZZER_NETBSD 0 + #define LIBFUZZER_FREEBSD 1 + #define LIBFUZZER_OPENBSD 0 #elif __OpenBSD__ -#define LIBFUZZER_LINUX 0 -#define LIBFUZZER_APPLE 0 -#define LIBFUZZER_NETBSD 0 -#define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 1 + #define LIBFUZZER_LINUX 0 + #define LIBFUZZER_APPLE 0 + #define LIBFUZZER_NETBSD 0 + #define LIBFUZZER_FREEBSD 0 + #define LIBFUZZER_OPENBSD 1 #else -#error "Support for your platform has not been implemented" + #error "Support for your platform has not been implemented" #endif -int __afl_sharedmem_fuzzing = 1; -extern unsigned int *__afl_fuzz_len; +int __afl_sharedmem_fuzzing = 1; +extern unsigned int * __afl_fuzz_len; extern unsigned char *__afl_fuzz_ptr; // libFuzzer interface is thin, so we don't include any libFuzzer headers. @@ -105,11 +108,11 @@ __attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); // Notify AFL about persistent mode. static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; -int __afl_persistent_loop(unsigned int); +int __afl_persistent_loop(unsigned int); // Notify AFL about deferred forkserver. static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; -void __afl_manual_init(); +void __afl_manual_init(); // Use this optionally defined function to output sanitizer messages even if // user asks to close stderr. @@ -122,98 +125,121 @@ static FILE *output_file; // Experimental feature to use afl_driver without AFL's deferred mode. // Needs to run before __afl_auto_init. __attribute__((constructor(0))) static void __decide_deferred_forkserver(void) { + if (getenv("AFL_DRIVER_DONT_DEFER")) { + if (unsetenv("__AFL_DEFER_FORKSRV")) { + perror("Failed to unset __AFL_DEFER_FORKSRV"); abort(); + } + } + } // If the user asks us to duplicate stderr, then do it. static void maybe_duplicate_stderr() { + char *stderr_duplicate_filename = getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); - if (!stderr_duplicate_filename) - return; + if (!stderr_duplicate_filename) return; FILE *stderr_duplicate_stream = freopen(stderr_duplicate_filename, "a+", stderr); if (!stderr_duplicate_stream) { + fprintf( stderr, "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); abort(); + } + output_file = stderr_duplicate_stream; + } // Most of these I/O functions were inspired by/copied from libFuzzer's code. static void discard_output(int fd) { + FILE *temp = fopen("/dev/null", "w"); - if (!temp) - abort(); + if (!temp) abort(); dup2(fileno(temp), fd); fclose(temp); + } -static void close_stdout() { discard_output(STDOUT_FILENO); } +static void close_stdout() { + + discard_output(STDOUT_FILENO); + +} // Prevent the targeted code from writing to "stderr" but allow sanitizers and // this driver to do so. static void dup_and_close_stderr() { + int output_fileno = fileno(output_file); int output_fd = dup(output_fileno); - if (output_fd <= 0) - abort(); + if (output_fd <= 0) abort(); FILE *new_output_file = fdopen(output_fd, "w"); - if (!new_output_file) - abort(); - if (!__sanitizer_set_report_fd) - return; - __sanitizer_set_report_fd((void*)output_fd); + if (!new_output_file) abort(); + if (!__sanitizer_set_report_fd) return; + __sanitizer_set_report_fd((void *)output_fd); discard_output(output_fileno); + } // Close stdout and/or stderr if user asks for it. static void maybe_close_fd_mask() { + char *fd_mask_str = getenv("AFL_DRIVER_CLOSE_FD_MASK"); - if (!fd_mask_str) - return; + if (!fd_mask_str) return; int fd_mask = atoi(fd_mask_str); - if (fd_mask & 2) - dup_and_close_stderr(); - if (fd_mask & 1) - close_stdout(); + if (fd_mask & 2) dup_and_close_stderr(); + if (fd_mask & 1) close_stdout(); + } // Define LLVMFuzzerMutate to avoid link failures for targets that use it // with libFuzzer's LLVMFuzzerCustomMutator. size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { - //assert(false && "LLVMFuzzerMutate should not be called from afl_driver"); + + // assert(false && "LLVMFuzzerMutate should not be called from afl_driver"); return 0; + } // Execute any files provided as parameters. static int ExecuteFilesOnyByOne(int argc, char **argv) { + unsigned char *buf = malloc(MAX_FILE); for (int i = 1; i < argc; i++) { + int fd = open(argv[i], O_RDONLY); if (fd == -1) continue; ssize_t length = read(fd, buf, MAX_FILE); if (length > 0) { + printf("Reading %zu bytes from %s\n", length, argv[i]); LLVMFuzzerTestOneInput(buf, length); printf("Execution successful.\n"); + } + } + free(buf); return 0; + } int main(int argc, char **argv) { + printf( "======================= INFO =========================\n" "This binary is built for AFL-fuzz.\n" @@ -226,36 +252,39 @@ int main(int argc, char **argv) { "afl-fuzz will run N iterations before " "re-spawning the process (default: 1000)\n" "======================================================\n", - argv[0], argv[0], argv[0]); + argv[0], argv[0], argv[0]); output_file = stderr; maybe_duplicate_stderr(); maybe_close_fd_mask(); - if (LLVMFuzzerInitialize) - LLVMFuzzerInitialize(&argc, &argv); + if (LLVMFuzzerInitialize) LLVMFuzzerInitialize(&argc, &argv); // Do any other expensive one-time initialization here. uint8_t dummy_input[64] = {0}; - memcpy(dummy_input, (void*)AFL_PERSISTENT, sizeof(AFL_PERSISTENT)); - memcpy(dummy_input + 32, (void*)AFL_DEFER_FORKSVR, sizeof(AFL_DEFER_FORKSVR)); + memcpy(dummy_input, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT)); + memcpy(dummy_input + 32, (void *)AFL_DEFER_FORKSVR, + sizeof(AFL_DEFER_FORKSVR)); int N = INT_MAX; if (argc == 2 && argv[1][0] == '-') - N = atoi(argv[1] + 1); - else if(argc == 2 && (N = atoi(argv[1])) > 0) - printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N); + N = atoi(argv[1] + 1); + else if (argc == 2 && (N = atoi(argv[1])) > 0) + printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N); else if (argc > 1) { -// if (!getenv("AFL_DRIVER_DONT_DEFER")) { - __afl_sharedmem_fuzzing = 0; - __afl_manual_init(); -// } + + // if (!getenv("AFL_DRIVER_DONT_DEFER")) { + + __afl_sharedmem_fuzzing = 0; + __afl_manual_init(); + // } return ExecuteFilesOnyByOne(argc, argv); exit(0); + } assert(N > 0); -// if (!getenv("AFL_DRIVER_DONT_DEFER")) + // if (!getenv("AFL_DRIVER_DONT_DEFER")) __afl_manual_init(); // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization @@ -264,17 +293,26 @@ int main(int argc, char **argv) { int num_runs = 0; while (__afl_persistent_loop(N)) { + #ifdef _DEBUG - fprintf(stderr, "CLIENT crc: %016llx len: %u\n", hash64(__afl_fuzz_ptr, *__afl_fuzz_len, 0xa5b35705), *__afl_fuzz_len); + fprintf(stderr, "CLIENT crc: %016llx len: %u\n", + hash64(__afl_fuzz_ptr, *__afl_fuzz_len, 0xa5b35705), + *__afl_fuzz_len); fprintf(stderr, "RECV:"); for (int i = 0; i < *__afl_fuzz_len; i++) fprintf(stderr, "%02x", __afl_fuzz_ptr[i]); - fprintf(stderr,"\n"); + fprintf(stderr, "\n"); #endif if (*__afl_fuzz_len) { + num_runs++; LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len); + } + } + printf("%s: successfully executed %d input(s)\n", argv[0], num_runs); + } + diff --git a/examples/aflpp_driver/aflpp_driver_test.c b/examples/aflpp_driver/aflpp_driver_test.c index 83278f5c..e4567bbf 100644 --- a/examples/aflpp_driver/aflpp_driver_test.c +++ b/examples/aflpp_driver/aflpp_driver_test.c @@ -6,18 +6,20 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - fprintf(stderr, "FUNC crc: %016llx len: %lu\n", hash64((u8*)Data, (unsigned int) Size, (unsigned long long int) 0xa5b35705), Size); - - if (Size < 5) - return 0; + fprintf(stderr, "FUNC crc: %016llx len: %lu\n", + hash64((u8 *)Data, (unsigned int)Size, + (unsigned long long int)0xa5b35705), + Size); + + if (Size < 5) return 0; if (Data[0] == 'F') if (Data[1] == 'A') if (Data[2] == '$') if (Data[3] == '$') - if (Data[4] == '$') - abort(); - + if (Data[4] == '$') abort(); + return 0; } + diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 38e95ac8..71874283 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -139,7 +139,8 @@ static u8 check_if_text(struct queue_entry *q) { // non-overlong 2-byte if (((0xC2 <= buf[offset + 0] && buf[offset + 0] <= 0xDF) && - (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF))) { + (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF)) && + len - offset > 1) { offset += 2; utf8++; @@ -149,18 +150,19 @@ static u8 check_if_text(struct queue_entry *q) { } // excluding overlongs - if ((buf[offset + 0] == 0xE0 && - (0xA0 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && - (0x80 <= buf[offset + 2] && - buf[offset + 2] <= 0xBF)) || // straight 3-byte - (((0xE1 <= buf[offset + 0] && buf[offset + 0] <= 0xEC) || - buf[offset + 0] == 0xEE || buf[offset + 0] == 0xEF) && - (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && - (0x80 <= buf[offset + 2] && - buf[offset + 2] <= 0xBF)) || // excluding surrogates - (buf[offset + 0] == 0xED && - (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0x9F) && - (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF))) { + if ((len - offset > 2) && + ((buf[offset + 0] == 0xE0 && + (0xA0 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && + (0x80 <= buf[offset + 2] && + buf[offset + 2] <= 0xBF)) || // straight 3-byte + (((0xE1 <= buf[offset + 0] && buf[offset + 0] <= 0xEC) || + buf[offset + 0] == 0xEE || buf[offset + 0] == 0xEF) && + (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && + (0x80 <= buf[offset + 2] && + buf[offset + 2] <= 0xBF)) || // excluding surrogates + (buf[offset + 0] == 0xED && + (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0x9F) && + (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF)))) { offset += 3; utf8++; @@ -170,19 +172,20 @@ static u8 check_if_text(struct queue_entry *q) { } // planes 1-3 - if ((buf[offset + 0] == 0xF0 && - (0x90 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && - (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) && - (0x80 <= buf[offset + 3] && - buf[offset + 3] <= 0xBF)) || // planes 4-15 - ((0xF1 <= buf[offset + 0] && buf[offset + 0] <= 0xF3) && - (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && - (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) && - (0x80 <= buf[offset + 3] && buf[offset + 3] <= 0xBF)) || // plane 16 - (buf[offset + 0] == 0xF4 && - (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0x8F) && - (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) && - (0x80 <= buf[offset + 3] && buf[offset + 3] <= 0xBF))) { + if ((len - offset > 3) && + ((buf[offset + 0] == 0xF0 && + (0x90 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && + (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) && + (0x80 <= buf[offset + 3] && + buf[offset + 3] <= 0xBF)) || // planes 4-15 + ((0xF1 <= buf[offset + 0] && buf[offset + 0] <= 0xF3) && + (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF) && + (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) && + (0x80 <= buf[offset + 3] && buf[offset + 3] <= 0xBF)) || // plane 16 + (buf[offset + 0] == 0xF4 && + (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0x8F) && + (0x80 <= buf[offset + 2] && buf[offset + 2] <= 0xBF) && + (0x80 <= buf[offset + 3] && buf[offset + 3] <= 0xBF)))) { offset += 4; utf8++; diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 57e60c3d..a2e8f992 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -269,8 +269,7 @@ static long long strntoll(const char *str, size_t sz, char **end, int base) { long long ret; const char *beg = str; - for (; beg && sz && *beg == ' '; beg++, sz--) - ; + for (; beg && sz && *beg == ' '; beg++, sz--) {}; if (!sz || sz >= sizeof(buf)) { diff --git a/test/test-floatingpoint.c b/test/test-floatingpoint.c index d1709b90..3a699595 100644 --- a/test/test-floatingpoint.c +++ b/test/test-floatingpoint.c @@ -17,10 +17,10 @@ int main(void) { int len = __AFL_FUZZ_TESTCASE_LEN; if (len != sizeof(float)) return 1; - /* 15 + 1/2 = 15.5 */ - /* 15 + 1/2 + 1/8 = 15.625 */ - /* 15 + 1/2 + 1/8 + 1/32 = 15.65625 */ - /* 15 + 1/2 + 1/8 + 1/32 + 1/128 = 15.6640625 */ + /* 15 + 1/2 = 15.5 */ + /* 15 + 1/2 + 1/8 = 15.625 */ + /* 15 + 1/2 + 1/8 + 1/32 = 15.65625 */ + /* 15 + 1/2 + 1/8 + 1/32 + 1/128 = 15.6640625 */ if ((-*magic == 15.0 + 0.5 + 0.125 + 0.03125 + 0.0078125)) abort(); } -- cgit 1.4.1 From 8e809d8593d9230c123aa22c8cd0b695e54d7c68 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Thu, 30 Jul 2020 17:51:32 +0200 Subject: added NULL check --- src/afl-common.c | 6 ++++-- src/afl-fuzz-redqueen.c | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-common.c b/src/afl-common.c index c023789b..367dec72 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -145,7 +145,8 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { char **new_argv = ck_alloc(sizeof(char *) * (argc + 4)); u8 * tmp, *cp = NULL, *rsl, *own_copy; - memcpy(new_argv + 3, argv + 1, (int)(sizeof(char *)) * argc); + memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1)); + new_argv[argc - 1] = NULL; new_argv[2] = *target_path_p; new_argv[1] = "--"; @@ -226,7 +227,8 @@ char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { char **new_argv = ck_alloc(sizeof(char *) * (argc + 3)); u8 * tmp, *cp = NULL, *rsl, *own_copy; - memcpy(new_argv + 2, argv + 1, (int)(sizeof(char *)) * argc); + memcpy(&new_argv[2], &argv[1], (int)(sizeof(char *)) * (argc - 1)); + new_argv[argc - 1] = NULL; new_argv[1] = *target_path_p; diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index a2e8f992..d86190a6 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -319,6 +319,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u8 *orig_buf, u8 *buf, u32 len, u8 do_reverse, u8 *status) { + if (!buf) { FATAL("BUG: buf was NULL. Please report this.\n"); } + u64 *buf_64 = (u64 *)&buf[idx]; u32 *buf_32 = (u32 *)&buf[idx]; u16 *buf_16 = (u16 *)&buf[idx]; -- cgit 1.4.1 From 320f26d26f7e0cbe093e6f5af5f27f180bc31a1b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 30 Jul 2020 19:00:41 +0200 Subject: add -b option to afl-fuzz --- docs/Changelog.md | 1 + include/afl-fuzz.h | 3 ++- src/afl-fuzz-init.c | 22 +++++++++++++++++----- src/afl-fuzz-state.c | 1 + src/afl-fuzz.c | 19 ++++++++++++++++--- 5 files changed, 37 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 1e7a1c1d..dcaf64a7 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -15,6 +15,7 @@ sending a mail to . - afl-fuzz: - added -F option to allow -M main fuzzers to sync to foreign fuzzers, e.g. honggfuzz or libfuzzer + - added -b option to bind to a specific CPU - eliminated CPU affinity race condition for -S/-M runs - expanded havoc mode added, on no cycle finds add extra splicing and MOpt into the mix diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 1c1be711..bc3f65b6 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -545,7 +545,8 @@ typedef struct afl_state { u64 total_bitmap_size, /* Total bit count for all bitmaps */ total_bitmap_entries; /* Number of bitmaps counted */ - s32 cpu_core_count; /* CPU core count */ + s32 cpu_core_count, /* CPU core count */ + cpu_to_bind; /* bind to specific CPU */ #ifdef HAVE_AFFINITY s32 cpu_aff; /* Selected CPU core */ diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 65ad0c9f..ad92dff6 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -53,6 +53,13 @@ void bind_to_free_cpu(afl_state_t *afl) { u8 cpu_used[4096] = {0}, lockfile[PATH_MAX] = ""; u32 i; + if (afl->cpu_to_bind != -1) { + + i = afl->cpu_to_bind; + goto set_cpu; + + } + if (afl->sync_id) { s32 lockfd, first = 1; @@ -295,20 +302,23 @@ void bind_to_free_cpu(afl_state_t *afl) { try: + if (afl->cpu_to_bind != -1) + FATAL("bind to CPU #%d failed!", afl->cpu_to_bind); + #if !defined(__ANDROID__) - for (i = cpu_start; i < afl->cpu_core_count; i++) { + for (i = cpu_start; i < afl->cpu_core_count; i++) { - if (!cpu_used[i]) { break; } + if (!cpu_used[i]) { break; } - } + } if (i == afl->cpu_core_count) { #else - for (i = afl->cpu_core_count - cpu_start - 1; i > -1; i--) - if (!cpu_used[i]) break; + for (i = afl->cpu_core_count - cpu_start - 1; i > -1; i--) + if (!cpu_used[i]) break; if (i == -1) { #endif @@ -327,6 +337,8 @@ void bind_to_free_cpu(afl_state_t *afl) { OKF("Found a free CPU core, try binding to #%u.", i); +set_cpu: + afl->cpu_aff = i; #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 66280ed1..e2d62bc6 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -94,6 +94,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->havoc_div = 1; /* Cycle count divisor for havoc */ afl->stage_name = "init"; /* Name of the current fuzz stage */ afl->splicing_with = -1; /* Splicing with which test case? */ + afl->cpu_to_bind = -1; #ifdef HAVE_AFFINITY afl->cpu_aff = -1; /* Selected CPU core */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 5bedf6e1..e33a4bbd 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -143,6 +143,8 @@ static void usage(afl_state_t *afl, u8 *argv0, int more_help) { //" -B bitmap.txt - mutate a specific test case, use the out/fuzz_bitmap //" "file\n" " -C - crash exploration mode (the peruvian rabbit thing)\n" + " -b cpu_id - bind the fuzzing process to the specified CPU core " + "(0-...)\n" " -e ext - file extension for the fuzz test input file (if " "needed)\n\n", argv0, EXEC_TIMEOUT, MEM_LIMIT, FOREIGN_SYNCS_MAX); @@ -271,9 +273,9 @@ int main(int argc, char **argv_orig, char **envp) { afl->shmem_testcase_mode = 1; // we always try to perform shmem fuzzing - while ((opt = getopt(argc, argv, - "+c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:")) > - 0) { + while ((opt = getopt( + argc, argv, + "+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:")) > 0) { switch (opt) { @@ -281,6 +283,17 @@ int main(int argc, char **argv_orig, char **envp) { afl->infoexec = optarg; break; + case 'b': { /* bind CPU core */ + + if (afl->cpu_to_bind != -1) FATAL("Multiple -b options not supported"); + + if (sscanf(optarg, "%u", &afl->cpu_to_bind) < 0 || optarg[0] == '-') + FATAL("Bad syntax used for -b"); + + break; + + } + case 'c': { afl->shm.cmplog_mode = 1; -- cgit 1.4.1 From cd576fa59d1b413433beef1009668f4d9b22c965 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 31 Jul 2020 10:42:43 +0200 Subject: fixes --- docs/binaryonly_fuzzing.md | 4 ++-- examples/afl_frida/GNUmakefile | 23 +++++++++++++++++++++++ examples/afl_frida/Makefile | 25 ++----------------------- examples/defork/forking_target | Bin 19520 -> 0 bytes src/afl-fuzz.c | 2 ++ 5 files changed, 29 insertions(+), 25 deletions(-) create mode 100644 examples/afl_frida/GNUmakefile delete mode 100755 examples/defork/forking_target (limited to 'src') diff --git a/docs/binaryonly_fuzzing.md b/docs/binaryonly_fuzzing.md index 111147e2..a3d3330f 100644 --- a/docs/binaryonly_fuzzing.md +++ b/docs/binaryonly_fuzzing.md @@ -15,9 +15,9 @@ high enough. Otherwise try retrowrite, afl-dyninst and if these fail too then standard qemu_mode with AFL_ENTRYPOINT to where you need it. - If your a target is library use examples/afl_frida/. + If your target is a library use examples/afl_frida/. - If your target is non-linux then use unicorn_mode/ + If your target is non-linux then use unicorn_mode/. ## QEMU diff --git a/examples/afl_frida/GNUmakefile b/examples/afl_frida/GNUmakefile new file mode 100644 index 00000000..c154f3a4 --- /dev/null +++ b/examples/afl_frida/GNUmakefile @@ -0,0 +1,23 @@ +ifdef DEBUG + OPT=-O0 -D_DEBUG=\"1\" +else + OPT=-O3 -funroll-loops +endif + +all: afl-frida libtestinstr.so + +libfrida-gum.a: + @echo Download and extract frida-gum-devkit-VERSION-PLATFORM.tar.xz for your platform from https://github.com/frida/frida/releases/latest + @exit 1 + +afl-frida: afl-frida.c libfrida-gum.a + $(CC) -g $(OPT) -o afl-frida -Wno-format -Wno-pointer-sign -I. -fpermissive -fPIC afl-frida.c ../../afl-llvm-rt.o libfrida-gum.a -ldl -lresolv -pthread + +libtestinstr.so: libtestinstr.c + $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c + +clean: + rm -f afl-frida *~ core *.o libtestinstr.so + +deepclean: clean + rm -f libfrida-gum.a frida-gum* diff --git a/examples/afl_frida/Makefile b/examples/afl_frida/Makefile index c154f3a4..0b306dde 100644 --- a/examples/afl_frida/Makefile +++ b/examples/afl_frida/Makefile @@ -1,23 +1,2 @@ -ifdef DEBUG - OPT=-O0 -D_DEBUG=\"1\" -else - OPT=-O3 -funroll-loops -endif - -all: afl-frida libtestinstr.so - -libfrida-gum.a: - @echo Download and extract frida-gum-devkit-VERSION-PLATFORM.tar.xz for your platform from https://github.com/frida/frida/releases/latest - @exit 1 - -afl-frida: afl-frida.c libfrida-gum.a - $(CC) -g $(OPT) -o afl-frida -Wno-format -Wno-pointer-sign -I. -fpermissive -fPIC afl-frida.c ../../afl-llvm-rt.o libfrida-gum.a -ldl -lresolv -pthread - -libtestinstr.so: libtestinstr.c - $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c - -clean: - rm -f afl-frida *~ core *.o libtestinstr.so - -deepclean: clean - rm -f libfrida-gum.a frida-gum* +all: + @echo please use GNU make, thanks! diff --git a/examples/defork/forking_target b/examples/defork/forking_target deleted file mode 100755 index 0f7a04fc..00000000 Binary files a/examples/defork/forking_target and /dev/null differ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e33a4bbd..54db1efb 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -163,11 +163,13 @@ static void usage(afl_state_t *afl, u8 *argv0, int more_help) { "AFL_BENCH_UNTIL_CRASH: exit soon when the first crashing input has been found\n" "AFL_CUSTOM_MUTATOR_LIBRARY: lib with afl_custom_fuzz() to mutate inputs\n" "AFL_CUSTOM_MUTATOR_ONLY: avoid AFL++'s internal mutators\n" + "AFL_CYCLE_SCHEDULES: after completing a cycle, switch to a different -p schedule\n" "AFL_DEBUG: extra debugging output for Python mode trimming\n" "AFL_DEBUG_CHILD_OUTPUT: do not suppress stdout/stderr from target\n" "AFL_DISABLE_TRIM: disable the trimming of test cases\n" "AFL_DUMB_FORKSRV: use fork server without feedback from target\n" "AFL_EXIT_WHEN_DONE: exit when all inputs are run and no new finds are found\n" + "AFL_EXPAND_HAVOC_NOW: immediately enable expand havoc mode (default: after 60 minutes and a cycle without finds)\n" "AFL_FAST_CAL: limit the calibration stage to three cycles for speedup\n" "AFL_FORCE_UI: force showing the status screen (for virtual consoles)\n" "AFL_HANG_TMOUT: override timeout value (in milliseconds)\n" -- cgit 1.4.1 From 630d2a934b2b7a9978452fb79aa36b51e0cce315 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 31 Jul 2020 14:36:58 +0200 Subject: less gotos --- src/afl-fuzz-mutators.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index b288cf9f..b30106a0 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -308,20 +308,23 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, unsuccessful trimming and skip it, instead of aborting the trimming. */ ++afl->trim_execs; - goto unsuccessful_trimming; } - write_to_testcase(afl, retbuf, retlen); + if (likely(retlen)) { - fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); - ++afl->trim_execs; + write_to_testcase(afl, retbuf, retlen); - if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; } + fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); + ++afl->trim_execs; + + if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; } - cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); + cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); - if (cksum == q->exec_cksum) { + } + + if (likely(retlen && cksum == q->exec_cksum)) { q->len = retlen; memcpy(in_buf, retbuf, retlen); @@ -349,8 +352,6 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, } else { - unsuccessful_trimming: - /* Tell the custom mutator that the trimming was unsuccessful */ afl->stage_cur = mutator->afl_custom_post_trim(mutator->data, 0); if (unlikely(afl->stage_cur < 0)) { -- cgit 1.4.1 From 729445b64f8156560ce1158a582d9528b0a39bf9 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 31 Jul 2020 18:17:03 +0200 Subject: Bind cpu (#480) * silence compiletime warning * refactored cpu binding * formatted code --- llvm_mode/compare-transform-pass.so.cc | 2 +- src/afl-fuzz-init.c | 223 ++++++++++++++++----------------- 2 files changed, 111 insertions(+), 114 deletions(-) (limited to 'src') diff --git a/llvm_mode/compare-transform-pass.so.cc b/llvm_mode/compare-transform-pass.so.cc index 2f165ea6..d389651c 100644 --- a/llvm_mode/compare-transform-pass.so.cc +++ b/llvm_mode/compare-transform-pass.so.cc @@ -358,7 +358,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, Value * VarStr; bool HasStr1 = getConstantStringInfo(Str1P, Str1); bool HasStr2 = getConstantStringInfo(Str2P, Str2); - uint64_t constStrLen, constSizedLen, unrollLen; + uint64_t constStrLen, unrollLen, constSizedLen = 0; bool isMemcmp = !callInst->getCalledFunction()->getName().compare(StringRef("memcmp")); bool isSizedcmp = isMemcmp || diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index ad92dff6..1e4f8dee 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -28,10 +28,9 @@ #ifdef HAVE_AFFINITY -/* Build a list of processes bound to specific cores. Returns -1 if nothing - can be found. Assumes an upper bound of 4k CPUs. */ +/* bind process to a specific cpu. Returns 0 on failure. */ -void bind_to_free_cpu(afl_state_t *afl) { +static u8 bind_cpu(afl_state_t *afl, s32 cpuid) { #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) cpu_set_t c; @@ -41,25 +40,108 @@ void bind_to_free_cpu(afl_state_t *afl) { psetid_t c; #endif - if (afl->cpu_core_count < 2) { return; } + afl->cpu_aff = cpuid; + + #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) + + CPU_ZERO(&c); + CPU_SET(cpuid, &c); + + #elif defined(__NetBSD__) + + c = cpuset_create(); + if (c == NULL) { PFATAL("cpuset_create failed"); } + cpuset_set(cpuid, c); + + #elif defined(__sun) + + pset_create(&c); + if (pset_assign(c, cpuid, NULL)) { PFATAL("pset_assign failed"); } + + #endif + + #if defined(__linux__) + + return (sched_setaffinity(0, sizeof(c), &c) == 0); + + #elif defined(__FreeBSD__) || defined(__DragonFly__) + + return (pthread_setaffinity_np(pthread_self(), sizeof(c), &c) == 0); + + #elif defined(__NetBSD__) + + if (pthread_setaffinity_np(pthread_self(), cpuset_size(c), c)) { + + cpuset_destroy(c); + return 0; + + } + + cpuset_destroy(c); + return 1; + + #elif defined(__sun) + + if (pset_bind(c, P_PID, getpid(), NULL)) { + + pset_destroy(c); + return 0; + + } + + pset_destroy(c); + return 1; + + #else + + // this will need something for other platforms + // TODO: Solaris/Illumos has processor_bind ... might worth a try + WARNF("Cannot bind to CPU yet on this platform."); + return 1; + + #endif + +} + +/* Build a list of processes bound to specific cores. Returns -1 if nothing + can be found. Assumes an upper bound of 4k CPUs. */ + +void bind_to_free_cpu(afl_state_t *afl) { + + u8 cpu_used[4096] = {0}; + u8 lockfile[PATH_MAX] = ""; + u32 i; if (afl->afl_env.afl_no_affinity) { + if (afl->cpu_to_bind != -1) { + + FATAL("-b and AFL_NO_AFFINITY are mututally exclusive."); + + } + WARNF("Not binding to a CPU core (AFL_NO_AFFINITY set)."); return; } - u8 cpu_used[4096] = {0}, lockfile[PATH_MAX] = ""; - u32 i; - if (afl->cpu_to_bind != -1) { - i = afl->cpu_to_bind; - goto set_cpu; + if (!bind_cpu(afl, afl->cpu_to_bind)) { + + FATAL( + "Could not bind to requested CPU %d! Make sure you passed a valid " + "-b.", + afl->cpu_to_bind); + + } + + return; } + if (afl->cpu_core_count < 2) { return; } + if (afl->sync_id) { s32 lockfd, first = 1; @@ -300,135 +382,50 @@ void bind_to_free_cpu(afl_state_t *afl) { size_t cpu_start = 0; - try: - - if (afl->cpu_to_bind != -1) - FATAL("bind to CPU #%d failed!", afl->cpu_to_bind); - #if !defined(__ANDROID__) for (i = cpu_start; i < afl->cpu_core_count; i++) { - if (!cpu_used[i]) { break; } - - } - - if (i == afl->cpu_core_count) { - #else - for (i = afl->cpu_core_count - cpu_start - 1; i > -1; i--) - if (!cpu_used[i]) break; - if (i == -1) { - - #endif - - SAYF("\n" cLRD "[-] " cRST - "Uh-oh, looks like all %d CPU cores on your system are allocated to\n" - " other instances of afl-fuzz (or similar CPU-locked tasks). " - "Starting\n" - " another fuzzer on this machine is probably a bad plan, but if " - "you are\n" - " absolutely sure, you can set AFL_NO_AFFINITY and try again.\n", - afl->cpu_core_count); - FATAL("No more free CPU cores"); - - } - - OKF("Found a free CPU core, try binding to #%u.", i); - -set_cpu: + /* for some reason Android goes backwards */ - afl->cpu_aff = i; - - #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) - - CPU_ZERO(&c); - CPU_SET(i, &c); - - #elif defined(__NetBSD__) - - c = cpuset_create(); - if (c == NULL) PFATAL("cpuset_create failed"); - cpuset_set(i, c); - - #elif defined(__sun) - -pset_create(&c); -if (pset_assign(c, i, NULL)) PFATAL("pset_assign failed"); + for (i = afl->cpu_core_count - 1; i > -1; i--) { #endif - #if defined(__linux__) + if (!cpu_used[i]) { continue; } - if (sched_setaffinity(0, sizeof(c), &c)) { + OKF("Found a free CPU core, try binding to #%u.", i); - if (cpu_start == afl->cpu_core_count) { + if (bind_cpu(afl, i)) { - PFATAL("sched_setaffinity failed for CPU %d, exit", i); + /* Success :) */ + break; } - WARNF("sched_setaffinity failed to CPU %d, trying next CPU", i); + WARNF("setaffinity failed to CPU %d, trying next CPU", i); cpu_start++; - goto try - ; } - #elif defined(__FreeBSD__) || defined(__DragonFly__) + if (lockfile[0]) unlink(lockfile); - if (pthread_setaffinity_np(pthread_self(), sizeof(c), &c)) { + if (i == afl->cpu_core_count || i == -1) { - if (cpu_start == afl->cpu_core_count) - PFATAL("pthread_setaffinity failed for cpu %d, exit", i); - WARNF("pthread_setaffinity failed to CPU %d, trying next CPU", i); - cpu_start++; - goto try - ; + SAYF("\n" cLRD "[-] " cRST + "Uh-oh, looks like all %d CPU cores on your system are allocated to\n" + " other instances of afl-fuzz (or similar CPU-locked tasks). " + "Starting\n" + " another fuzzer on this machine is probably a bad plan, but if " + "you are\n" + " absolutely sure, you can set AFL_NO_AFFINITY and try again.\n", + afl->cpu_core_count); + FATAL("No more free CPU cores"); } - #elif defined(__NetBSD__) - -if (pthread_setaffinity_np(pthread_self(), cpuset_size(c), c)) { - - if (cpu_start == afl->cpu_core_count) - PFATAL("pthread_setaffinity failed for cpu %d, exit", i); - WARNF("pthread_setaffinity failed to CPU %d, trying next CPU", i); - cpu_start++; - goto try - ; - -} - -cpuset_destroy(c); - - #elif defined(__sun) - -if (pset_bind(c, P_PID, getpid(), NULL)) { - - if (cpu_start == afl->cpu_core_count) - PFATAL("pset_bind failed for cpu %d, exit", i); - WARNF("pset_bind failed to CPU %d, trying next CPU", i); - cpu_start++; - goto try - ; - -} - -pset_destroy(c); - - #else - - // this will need something for other platforms - // TODO: Solaris/Illumos has processor_bind ... might worth a try - - #endif - - if (lockfile[0]) unlink(lockfile); - // we leave the environment variable to ensure a cleanup for other processes - } #endif /* HAVE_AFFINITY */ -- cgit 1.4.1 From 16b674c6523e151368443948b27c5e4aa6a3c7ee Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 31 Jul 2020 18:35:43 +0200 Subject: fix find free cpu --- src/afl-fuzz-init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 1e4f8dee..396a20f0 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -394,7 +394,7 @@ void bind_to_free_cpu(afl_state_t *afl) { #endif - if (!cpu_used[i]) { continue; } + if (cpu_used[i]) { continue; } OKF("Found a free CPU core, try binding to #%u.", i); -- cgit 1.4.1 From 54d96685808bd7b47de30f5028727d94b5369962 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 31 Jul 2020 17:21:17 +0000 Subject: Haiku set explicitly to performance. No command line to set through afl-system-config (the only one is a GUI). --- include/afl-fuzz.h | 5 +++++ src/afl-fuzz.c | 5 +++++ 2 files changed, 10 insertions(+) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index bc3f65b6..b82ddb4a 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -82,6 +82,11 @@ #include #endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */ +#if defined(__HAIKU__) + #include + #include +#endif + /* For systems that have sched_setaffinity; right now just Linux, but one can hope... */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 54db1efb..326ccc1c 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1057,6 +1057,11 @@ int main(int argc, char **argv_orig, char **envp) { bind_to_free_cpu(afl); #endif /* HAVE_AFFINITY */ + #ifdef __HAIKU__ + /* Prioritizes performance over power saving */ + set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY); + #endif + afl->fsrv.trace_bits = afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); -- cgit 1.4.1 From 491cee669f858bfea2b0db2fe540200b0f2625e6 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 3 Aug 2020 11:15:12 +0200 Subject: fix #483 --- src/afl-fuzz-redqueen.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index d86190a6..b58c8537 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -673,14 +673,15 @@ static u8 rtn_extend_encoding(afl_state_t *afl, struct cmp_header *h, for (i = 0; i < its_len; ++i) { - if (pattern[idx + i] != buf[idx + i] || - o_pattern[idx + i] != orig_buf[idx + i] || *status == 1) { + if (pattern[i] != buf[idx + i] || + o_pattern[i] != orig_buf[idx + i] || *status == 1) { break; } - buf[idx + i] = repl[idx + i]; + buf[idx + i] = repl[i]; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } } @@ -726,7 +727,7 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } for (idx = 0; idx < len && fails < 8; ++idx) { - + if (unlikely(rtn_extend_encoding(afl, h, o->v0, o->v1, orig_o->v0, idx, orig_buf, buf, len, &status))) { -- cgit 1.4.1 From 409e4ae945ab5aeb31b1e3a1497ce5fc65226f07 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 3 Aug 2020 13:13:32 +0200 Subject: fix expand havoc for ..._only modes --- docs/Changelog.md | 1 + examples/persistent_demo/persistent_demo_new.c | 4 +-- llvm_mode/afl-llvm-rt.o.c | 48 +++++++++++++++----------- src/afl-fuzz-redqueen.c | 8 ++--- src/afl-fuzz.c | 3 +- test/test-cmplog.c | 22 +++++------- 6 files changed, 46 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 8ab3fdf4..ae7377f2 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -19,6 +19,7 @@ sending a mail to . - eliminated CPU affinity race condition for -S/-M runs - expanded havoc mode added, on no cycle finds add extra splicing and MOpt into the mix + - fixed a bug in redqueen for strings - llvm_mode: - now supports llvm 12! - fixes for laf-intel float splitting (thanks to mark-griffin for diff --git a/examples/persistent_demo/persistent_demo_new.c b/examples/persistent_demo/persistent_demo_new.c index 5f347667..7f878c0c 100644 --- a/examples/persistent_demo/persistent_demo_new.c +++ b/examples/persistent_demo/persistent_demo_new.c @@ -31,8 +31,8 @@ /* this lets the source compile without afl-clang-fast/lto */ #ifndef __AFL_FUZZ_TESTCASE_LEN - ssize_t fuzz_len; - unsigned char fuzz_buf[1024000]; +ssize_t fuzz_len; +unsigned char fuzz_buf[1024000]; #define __AFL_FUZZ_TESTCASE_LEN fuzz_len #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index c2859d9c..88abcbe0 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -859,26 +859,34 @@ __attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { - // For stability analysis, if you want to know to which function unstable - // edge IDs belong to - uncomment, recompile+install llvm_mode, recompile - // the target. libunwind and libbacktrace are better solutions. - // Set AFL_DEBUG_CHILD_OUTPUT=1 and run afl-fuzz with 2>file to capture - // the backtrace output - /* - uint32_t unstable[] = { ... unstable edge IDs }; - uint32_t idx; - char bt[1024]; - for (idx = 0; i < sizeof(unstable)/sizeof(uint32_t); i++) { - if (unstable[idx] == __afl_area_ptr[*guard]) { - int bt_size = backtrace(bt, 256); - if (bt_size > 0) { - char **bt_syms = backtrace_symbols(bt, bt_size); - if (bt_syms) - fprintf(stderr, "DEBUG: edge=%u caller=%s\n", unstable[idx], bt_syms[0]); - } - } - } - */ + // For stability analysis, if you want to know to which function unstable + // edge IDs belong to - uncomment, recompile+install llvm_mode, recompile + // the target. libunwind and libbacktrace are better solutions. + // Set AFL_DEBUG_CHILD_OUTPUT=1 and run afl-fuzz with 2>file to capture + // the backtrace output + /* + uint32_t unstable[] = { ... unstable edge IDs }; + uint32_t idx; + char bt[1024]; + for (idx = 0; i < sizeof(unstable)/sizeof(uint32_t); i++) { + + if (unstable[idx] == __afl_area_ptr[*guard]) { + + int bt_size = backtrace(bt, 256); + if (bt_size > 0) { + + char **bt_syms = backtrace_symbols(bt, bt_size); + if (bt_syms) + fprintf(stderr, "DEBUG: edge=%u caller=%s\n", unstable[idx], + bt_syms[0]); + + } + + } + + } + + */ __afl_area_ptr[*guard]++; diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index b58c8537..cb4c78df 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -673,15 +673,15 @@ static u8 rtn_extend_encoding(afl_state_t *afl, struct cmp_header *h, for (i = 0; i < its_len; ++i) { - if (pattern[i] != buf[idx + i] || - o_pattern[i] != orig_buf[idx + i] || *status == 1) { + if (pattern[i] != buf[idx + i] || o_pattern[i] != orig_buf[idx + i] || + *status == 1) { break; } buf[idx + i] = repl[i]; - + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } } @@ -727,7 +727,7 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } for (idx = 0; idx < len && fails < 8; ++idx) { - + if (unlikely(rtn_extend_encoding(afl, h, o->v0, o->v1, orig_o->v0, idx, orig_buf, buf, len, &status))) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 326ccc1c..da30797c 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1304,7 +1304,8 @@ int main(int argc, char **argv_orig, char **envp) { afl->expand_havoc = 1; break; case 1: - if (afl->limit_time_sig == 0) { + if (afl->limit_time_sig == 0 && !afl->custom_only && + !afl->python_only) { afl->limit_time_sig = -1; afl->limit_time_puppet = 0; diff --git a/test/test-cmplog.c b/test/test-cmplog.c index 75efd887..b077e3ab 100644 --- a/test/test-cmplog.c +++ b/test/test-cmplog.c @@ -5,23 +5,19 @@ #include #include int main(int argc, char *argv[]) { - char buf[1024]; + + char buf[1024]; ssize_t i; - if ((i = read(0, buf, sizeof(buf) - 1)) < 24) - return 0; + if ((i = read(0, buf, sizeof(buf) - 1)) < 24) return 0; buf[i] = 0; - if (buf[0] != 'A') - return 0; - if (buf[1] != 'B') - return 0; - if (buf[2] != 'C') - return 0; - if (buf[3] != 'D') - return 0; - if (memcmp(buf + 4, "1234", 4) || memcmp(buf + 8, "EFGH", 4)) - return 0; + if (buf[0] != 'A') return 0; + if (buf[1] != 'B') return 0; + if (buf[2] != 'C') return 0; + if (buf[3] != 'D') return 0; + if (memcmp(buf + 4, "1234", 4) || memcmp(buf + 8, "EFGH", 4)) return 0; if (strncmp(buf + 12, "IJKL", 4) == 0 && strcmp(buf + 16, "DEADBEEF") == 0) abort(); return 0; + } -- cgit 1.4.1 From 38bed607d1f52ad7ede7792fe01163358a703953 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 3 Aug 2020 20:50:47 +0200 Subject: code format --- src/afl-fuzz-init.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 396a20f0..2c17ffbb 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -490,9 +490,13 @@ void read_foreign_testcases(afl_state_t *afl, int first) { if (nl_cnt == 0) { - if (first) + if (first) { + WARNF("directory %s is currently empty", afl->foreign_syncs[iter].dir); + + } + continue; } @@ -540,11 +544,15 @@ void read_foreign_testcases(afl_state_t *afl, int first) { if (st.st_size > MAX_FILE) { - if (first) + if (first) { + WARNF( "Test case '%s' is too big (%s, limit is %s), skipping", fn2, stringify_mem_size(val_buf[0], sizeof(val_buf[0]), st.st_size), stringify_mem_size(val_buf[1], sizeof(val_buf[1]), MAX_FILE)); + + } + ck_free(fn2); continue; -- cgit 1.4.1 From c8354d751606e0f7a0364685958036bb7031e35a Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 4 Aug 2020 23:22:42 +0200 Subject: new rand mode for data offsets that prefer low offset values --- include/afl-fuzz.h | 24 ++++++++++++++++++ src/afl-fuzz-one.c | 73 +++++++++++++++++++++++++++++------------------------- 2 files changed, 63 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index b82ddb4a..dac99a76 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1001,6 +1001,30 @@ static inline u32 rand_below(afl_state_t *afl, u32 limit) { } +/* we prefer lower range values here */ +/* this is only called with normal havoc, not MOpt, to have an equalizer for + expand havoc mode */ +static inline u32 rand_below_datalen(afl_state_t *afl, u32 limit) { + + switch (rand_below(afl, 3)) { + + case 2: + return (rand_below(afl, limit) % rand_below(afl, limit)) % + rand_below(afl, limit); + break; + case 1: + return rand_below(afl, limit) % rand_below(afl, limit); + break; + case 0: + return rand_below(afl, limit); + break; + + } + + return 1; // cannot be reached + +} + static inline s64 rand_get_seed(afl_state_t *afl) { if (unlikely(afl->fixed_seed)) { return afl->init_seed; } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 1f0bf30e..77bce7d0 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1921,14 +1921,14 @@ havoc_stage: /* Flip a single bit somewhere. Spooky! */ - FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); + FLIP_BIT(out_buf, rand_below_datalen(afl, temp_len << 3)); break; case 1: /* Set byte to interesting value. */ - out_buf[rand_below(afl, temp_len)] = + out_buf[rand_below_datalen(afl, temp_len)] = interesting_8[rand_below(afl, sizeof(interesting_8))]; break; @@ -1940,12 +1940,12 @@ havoc_stage: if (rand_below(afl, 2)) { - *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = + *(u16 *)(out_buf + rand_below_datalen(afl, temp_len - 1)) = interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]; } else { - *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = SWAP16( + *(u16 *)(out_buf + rand_below_datalen(afl, temp_len - 1)) = SWAP16( interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]); } @@ -1960,12 +1960,12 @@ havoc_stage: if (rand_below(afl, 2)) { - *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = + *(u32 *)(out_buf + rand_below_datalen(afl, temp_len - 3)) = interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]; } else { - *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = SWAP32( + *(u32 *)(out_buf + rand_below_datalen(afl, temp_len - 3)) = SWAP32( interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]); } @@ -1976,14 +1976,16 @@ havoc_stage: /* Randomly subtract from byte. */ - out_buf[rand_below(afl, temp_len)] -= 1 + rand_below(afl, ARITH_MAX); + out_buf[rand_below_datalen(afl, temp_len)] -= + 1 + rand_below(afl, ARITH_MAX); break; case 5: /* Randomly add to byte. */ - out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); + out_buf[rand_below_datalen(afl, temp_len)] += + 1 + rand_below(afl, ARITH_MAX); break; case 6: @@ -1994,13 +1996,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below(afl, temp_len - 1); + u32 pos = rand_below_datalen(afl, temp_len - 1); *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below(afl, temp_len - 1); + u32 pos = rand_below_datalen(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); *(u16 *)(out_buf + pos) = @@ -2018,13 +2020,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below(afl, temp_len - 1); + u32 pos = rand_below_datalen(afl, temp_len - 1); *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below(afl, temp_len - 1); + u32 pos = rand_below_datalen(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); *(u16 *)(out_buf + pos) = @@ -2042,13 +2044,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below(afl, temp_len - 3); + u32 pos = rand_below_datalen(afl, temp_len - 3); *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below(afl, temp_len - 3); + u32 pos = rand_below_datalen(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); *(u32 *)(out_buf + pos) = @@ -2066,13 +2068,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below(afl, temp_len - 3); + u32 pos = rand_below_datalen(afl, temp_len - 3); *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below(afl, temp_len - 3); + u32 pos = rand_below_datalen(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); *(u32 *)(out_buf + pos) = @@ -2088,7 +2090,8 @@ havoc_stage: why not. We use XOR with 1-255 to eliminate the possibility of a no-op. */ - out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); + out_buf[rand_below_datalen(afl, temp_len)] ^= + 1 + rand_below(afl, 255); break; case 11 ... 12: { @@ -2105,7 +2108,7 @@ havoc_stage: del_len = choose_block_len(afl, temp_len - 1); - del_from = rand_below(afl, temp_len - del_len + 1); + del_from = rand_below_datalen(afl, temp_len - del_len + 1); memmove(out_buf + del_from, out_buf + del_from + del_len, temp_len - del_from - del_len); @@ -2129,7 +2132,7 @@ havoc_stage: if (actually_clone) { clone_len = choose_block_len(afl, temp_len); - clone_from = rand_below(afl, temp_len - clone_len + 1); + clone_from = rand_below_datalen(afl, temp_len - clone_len + 1); } else { @@ -2138,7 +2141,7 @@ havoc_stage: } - clone_to = rand_below(afl, temp_len); + clone_to = rand_below_datalen(afl, temp_len); new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); @@ -2156,8 +2159,9 @@ havoc_stage: } else { memset(new_buf + clone_to, - rand_below(afl, 2) ? rand_below(afl, 256) - : out_buf[rand_below(afl, temp_len)], + rand_below(afl, 2) + ? rand_below(afl, 256) + : out_buf[rand_below_datalen(afl, temp_len)], clone_len); } @@ -2186,8 +2190,8 @@ havoc_stage: copy_len = choose_block_len(afl, temp_len - 1); - copy_from = rand_below(afl, temp_len - copy_len + 1); - copy_to = rand_below(afl, temp_len - copy_len + 1); + copy_from = rand_below_datalen(afl, temp_len - copy_len + 1); + copy_to = rand_below_datalen(afl, temp_len - copy_len + 1); if (rand_below(afl, 4)) { @@ -2200,8 +2204,9 @@ havoc_stage: } else { memset(out_buf + copy_to, - rand_below(afl, 2) ? rand_below(afl, 256) - : out_buf[rand_below(afl, temp_len)], + rand_below(afl, 2) + ? rand_below(afl, 256) + : out_buf[rand_below_datalen(afl, temp_len)], copy_len); } @@ -2233,7 +2238,7 @@ havoc_stage: if (extra_len > temp_len) { break; } - insert_at = rand_below(afl, temp_len - extra_len + 1); + insert_at = rand_below_datalen(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, extra_len); @@ -2247,7 +2252,7 @@ havoc_stage: if (extra_len > temp_len) { break; } - insert_at = rand_below(afl, temp_len - extra_len + 1); + insert_at = rand_below_datalen(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len); @@ -2258,7 +2263,7 @@ havoc_stage: } else { // case 16 u32 use_extra, extra_len, - insert_at = rand_below(afl, temp_len + 1); + insert_at = rand_below_datalen(afl, temp_len + 1); u8 *ptr; /* Insert an extra. Do the same dice-rolling stuff as for the @@ -2362,8 +2367,8 @@ havoc_stage: copy_len = choose_block_len(afl, new_len - 1); if (copy_len > temp_len) copy_len = temp_len; - copy_from = rand_below(afl, new_len - copy_len + 1); - copy_to = rand_below(afl, temp_len - copy_len + 1); + copy_from = rand_below_datalen(afl, new_len - copy_len + 1); + copy_to = rand_below_datalen(afl, temp_len - copy_len + 1); memmove(out_buf + copy_to, new_buf + copy_from, copy_len); @@ -2372,9 +2377,9 @@ havoc_stage: u32 clone_from, clone_to, clone_len; clone_len = choose_block_len(afl, new_len); - clone_from = rand_below(afl, new_len - clone_len + 1); + clone_from = rand_below_datalen(afl, new_len - clone_len + 1); - clone_to = rand_below(afl, temp_len); + clone_to = rand_below_datalen(afl, temp_len); u8 *temp_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); @@ -2523,7 +2528,7 @@ retry_splicing: /* Split somewhere between the first and last differing byte. */ - split_at = f_diff + rand_below(afl, l_diff - f_diff); + split_at = f_diff + rand_below_datalen(afl, l_diff - f_diff); /* Do the thing. */ -- cgit 1.4.1 From f30ca1476c2d4d08d46fe9657ad4aa1d828eb578 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 5 Aug 2020 11:17:15 +0200 Subject: fix short write --- include/afl-fuzz.h | 4 ++-- llvm_mode/afl-clang-fast.c | 10 ++++++---- src/afl-fuzz-queue.c | 2 ++ src/afl-fuzz-run.c | 17 ++++++++++++++--- 4 files changed, 24 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 2324efa5..bb1bb314 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -986,7 +986,7 @@ uint64_t rand_next(afl_state_t *afl); static inline u32 rand_below(afl_state_t *afl, u32 limit) { - if (limit <= 1) return 0; + if (limit <= 1) return 0; /* The boundary not being necessarily a power of 2, we need to ensure the result uniformity. */ @@ -1008,7 +1008,7 @@ static inline u32 rand_below(afl_state_t *afl, u32 limit) { expand havoc mode */ static inline u32 rand_below_datalen(afl_state_t *afl, u32 limit) { - if (limit <= 1) return 0; + if (limit <= 1) return 0; switch (rand_below(afl, 3)) { diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 16f2c9c0..3038df30 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -161,8 +161,8 @@ static void find_obj(u8 *argv0) { static void edit_params(u32 argc, char **argv, char **envp) { - u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, - preprocessor_only = 0; + u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, + preprocessor_only = 0; u8 have_pic = 0; u8 *name; @@ -400,7 +400,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue; if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; - + if (!strcmp(cur, "-E")) preprocessor_only = 1; cc_params[cc_par_cnt++] = cur; @@ -566,8 +566,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "none"; } - + if (preprocessor_only) { + /* In the preprocessor_only case (-E), we are not actually compiling at all but requesting the compiler to output preprocessed sources only. We must not add the runtime in this case because the compiler will @@ -575,6 +576,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { systems that rely on a separate source preprocessing step. */ cc_params[cc_par_cnt] = NULL; return; + } #ifndef __ANDROID__ diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 71874283..f35df914 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -112,8 +112,10 @@ static u8 check_if_text(struct queue_entry *q) { u8 buf[MAX_FILE]; s32 fd, len = q->len, offset = 0, ascii = 0, utf8 = 0, comp; + if (len >= MAX_FILE) len = MAX_FILE - 1; if ((fd = open(q->fname, O_RDONLY)) < 0) return 0; if ((comp = read(fd, buf, len)) != len) return 0; + buf[len] = 0; close(fd); while (offset < len) { diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 44d3c522..ed4a1081 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -819,16 +819,27 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { fd = open(q->fname, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); } + + u32 written = 0; + while (written < q->len) { + + ssize_t result = write(fd, in_buf, q->len - written); + if (result > 0) written += result; + + } + } else { unlink(q->fname); /* ignore errors */ fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600); - } + if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); } - if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); } + ck_write(fd, in_buf, q->len, q->fname); + + } - ck_write(fd, in_buf, q->len, q->fname); close(fd); memcpy(afl->fsrv.trace_bits, afl->clean_trace, afl->fsrv.map_size); -- cgit 1.4.1 From e2434cf8c6db86e1e7b67cb3b73e417c2a7fd3bd Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 6 Aug 2020 23:27:50 +0200 Subject: remove datalen in havoc --- examples/aflpp_driver/aflpp_driver.c | 35 ++++++++++------- llvm_mode/afl-llvm-rt.o.c | 2 +- src/afl-fuzz-one.c | 73 +++++++++++++++++------------------- 3 files changed, 56 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c index 6ec37cda..90f9cf99 100644 --- a/examples/aflpp_driver/aflpp_driver.c +++ b/examples/aflpp_driver/aflpp_driver.c @@ -66,7 +66,7 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. #endif #ifndef MAP_FIXED_NOREPLACE -#define MAP_FIXED_NOREPLACE 0x100000 + #define MAP_FIXED_NOREPLACE 0x100000 #endif #define MAX_DUMMY_SIZE 256000 @@ -106,10 +106,10 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. #error "Support for your platform has not been implemented" #endif -int __afl_sharedmem_fuzzing = 1; -extern unsigned int * __afl_fuzz_len; -extern unsigned char *__afl_fuzz_ptr; -extern unsigned char *__afl_area_ptr; +int __afl_sharedmem_fuzzing = 1; +extern unsigned int * __afl_fuzz_len; +extern unsigned char * __afl_fuzz_ptr; +extern unsigned char * __afl_area_ptr; extern struct cmp_map *__afl_cmp_map; // libFuzzer interface is thin, so we don't include any libFuzzer headers. @@ -249,17 +249,21 @@ static int ExecuteFilesOnyByOne(int argc, char **argv) { } __attribute__((constructor(10))) void __afl_protect(void) { - __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, - MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + __afl_area_ptr = (unsigned char *)mmap( + (void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, + MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); if ((uint64_t)__afl_area_ptr == -1) - __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); + __afl_area_ptr = (unsigned char *)mmap((void *)0x10000, MAX_DUMMY_SIZE, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); if ((uint64_t)__afl_area_ptr == -1) - __afl_area_ptr = (unsigned char*) mmap(NULL, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); - __afl_cmp_map = (struct cmp_map *) __afl_area_ptr; -} + __afl_area_ptr = + (unsigned char *)mmap(NULL, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + __afl_cmp_map = (struct cmp_map *)__afl_area_ptr; +} int main(int argc, char **argv) { @@ -272,7 +276,8 @@ int main(int argc, char **argv) { " %s INPUT_FILE1 [INPUT_FILE2 ... ]\n" "To fuzz with afl-fuzz execute this:\n" " afl-fuzz [afl-flags] -- %s [-N]\n" - "afl-fuzz will run N iterations before re-spawning the process (default: 1000)\n" + "afl-fuzz will run N iterations before re-spawning the process (default: " + "1000)\n" "======================================================\n", argv[0], argv[0]); @@ -280,9 +285,11 @@ int main(int argc, char **argv) { maybe_duplicate_stderr(); maybe_close_fd_mask(); if (LLVMFuzzerInitialize) { + fprintf(stderr, "Running LLVMFuzzerInitialize ...\n"); LLVMFuzzerInitialize(&argc, &argv); fprintf(stderr, "continue...\n"); + } // Do any other expensive one-time initialization here. diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index d67862f8..0d498de7 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -101,7 +101,7 @@ __thread u32 __afl_cmp_counter; int __afl_sharedmem_fuzzing __attribute__((weak)); -struct cmp_map *__afl_cmp_map = (struct cmp_map *) __afl_area_initial; +struct cmp_map *__afl_cmp_map = (struct cmp_map *)__afl_area_initial; /* Running in persistent mode? */ diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 77bce7d0..1f0bf30e 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1921,14 +1921,14 @@ havoc_stage: /* Flip a single bit somewhere. Spooky! */ - FLIP_BIT(out_buf, rand_below_datalen(afl, temp_len << 3)); + FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); break; case 1: /* Set byte to interesting value. */ - out_buf[rand_below_datalen(afl, temp_len)] = + out_buf[rand_below(afl, temp_len)] = interesting_8[rand_below(afl, sizeof(interesting_8))]; break; @@ -1940,12 +1940,12 @@ havoc_stage: if (rand_below(afl, 2)) { - *(u16 *)(out_buf + rand_below_datalen(afl, temp_len - 1)) = + *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]; } else { - *(u16 *)(out_buf + rand_below_datalen(afl, temp_len - 1)) = SWAP16( + *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = SWAP16( interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]); } @@ -1960,12 +1960,12 @@ havoc_stage: if (rand_below(afl, 2)) { - *(u32 *)(out_buf + rand_below_datalen(afl, temp_len - 3)) = + *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]; } else { - *(u32 *)(out_buf + rand_below_datalen(afl, temp_len - 3)) = SWAP32( + *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = SWAP32( interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]); } @@ -1976,16 +1976,14 @@ havoc_stage: /* Randomly subtract from byte. */ - out_buf[rand_below_datalen(afl, temp_len)] -= - 1 + rand_below(afl, ARITH_MAX); + out_buf[rand_below(afl, temp_len)] -= 1 + rand_below(afl, ARITH_MAX); break; case 5: /* Randomly add to byte. */ - out_buf[rand_below_datalen(afl, temp_len)] += - 1 + rand_below(afl, ARITH_MAX); + out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); break; case 6: @@ -1996,13 +1994,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below_datalen(afl, temp_len - 1); + u32 pos = rand_below(afl, temp_len - 1); *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below_datalen(afl, temp_len - 1); + u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); *(u16 *)(out_buf + pos) = @@ -2020,13 +2018,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below_datalen(afl, temp_len - 1); + u32 pos = rand_below(afl, temp_len - 1); *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below_datalen(afl, temp_len - 1); + u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); *(u16 *)(out_buf + pos) = @@ -2044,13 +2042,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below_datalen(afl, temp_len - 3); + u32 pos = rand_below(afl, temp_len - 3); *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below_datalen(afl, temp_len - 3); + u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); *(u32 *)(out_buf + pos) = @@ -2068,13 +2066,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below_datalen(afl, temp_len - 3); + u32 pos = rand_below(afl, temp_len - 3); *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below_datalen(afl, temp_len - 3); + u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); *(u32 *)(out_buf + pos) = @@ -2090,8 +2088,7 @@ havoc_stage: why not. We use XOR with 1-255 to eliminate the possibility of a no-op. */ - out_buf[rand_below_datalen(afl, temp_len)] ^= - 1 + rand_below(afl, 255); + out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); break; case 11 ... 12: { @@ -2108,7 +2105,7 @@ havoc_stage: del_len = choose_block_len(afl, temp_len - 1); - del_from = rand_below_datalen(afl, temp_len - del_len + 1); + del_from = rand_below(afl, temp_len - del_len + 1); memmove(out_buf + del_from, out_buf + del_from + del_len, temp_len - del_from - del_len); @@ -2132,7 +2129,7 @@ havoc_stage: if (actually_clone) { clone_len = choose_block_len(afl, temp_len); - clone_from = rand_below_datalen(afl, temp_len - clone_len + 1); + clone_from = rand_below(afl, temp_len - clone_len + 1); } else { @@ -2141,7 +2138,7 @@ havoc_stage: } - clone_to = rand_below_datalen(afl, temp_len); + clone_to = rand_below(afl, temp_len); new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); @@ -2159,9 +2156,8 @@ havoc_stage: } else { memset(new_buf + clone_to, - rand_below(afl, 2) - ? rand_below(afl, 256) - : out_buf[rand_below_datalen(afl, temp_len)], + rand_below(afl, 2) ? rand_below(afl, 256) + : out_buf[rand_below(afl, temp_len)], clone_len); } @@ -2190,8 +2186,8 @@ havoc_stage: copy_len = choose_block_len(afl, temp_len - 1); - copy_from = rand_below_datalen(afl, temp_len - copy_len + 1); - copy_to = rand_below_datalen(afl, temp_len - copy_len + 1); + copy_from = rand_below(afl, temp_len - copy_len + 1); + copy_to = rand_below(afl, temp_len - copy_len + 1); if (rand_below(afl, 4)) { @@ -2204,9 +2200,8 @@ havoc_stage: } else { memset(out_buf + copy_to, - rand_below(afl, 2) - ? rand_below(afl, 256) - : out_buf[rand_below_datalen(afl, temp_len)], + rand_below(afl, 2) ? rand_below(afl, 256) + : out_buf[rand_below(afl, temp_len)], copy_len); } @@ -2238,7 +2233,7 @@ havoc_stage: if (extra_len > temp_len) { break; } - insert_at = rand_below_datalen(afl, temp_len - extra_len + 1); + insert_at = rand_below(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, extra_len); @@ -2252,7 +2247,7 @@ havoc_stage: if (extra_len > temp_len) { break; } - insert_at = rand_below_datalen(afl, temp_len - extra_len + 1); + insert_at = rand_below(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len); @@ -2263,7 +2258,7 @@ havoc_stage: } else { // case 16 u32 use_extra, extra_len, - insert_at = rand_below_datalen(afl, temp_len + 1); + insert_at = rand_below(afl, temp_len + 1); u8 *ptr; /* Insert an extra. Do the same dice-rolling stuff as for the @@ -2367,8 +2362,8 @@ havoc_stage: copy_len = choose_block_len(afl, new_len - 1); if (copy_len > temp_len) copy_len = temp_len; - copy_from = rand_below_datalen(afl, new_len - copy_len + 1); - copy_to = rand_below_datalen(afl, temp_len - copy_len + 1); + copy_from = rand_below(afl, new_len - copy_len + 1); + copy_to = rand_below(afl, temp_len - copy_len + 1); memmove(out_buf + copy_to, new_buf + copy_from, copy_len); @@ -2377,9 +2372,9 @@ havoc_stage: u32 clone_from, clone_to, clone_len; clone_len = choose_block_len(afl, new_len); - clone_from = rand_below_datalen(afl, new_len - clone_len + 1); + clone_from = rand_below(afl, new_len - clone_len + 1); - clone_to = rand_below_datalen(afl, temp_len); + clone_to = rand_below(afl, temp_len); u8 *temp_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); @@ -2528,7 +2523,7 @@ retry_splicing: /* Split somewhere between the first and last differing byte. */ - split_at = f_diff + rand_below_datalen(afl, l_diff - f_diff); + split_at = f_diff + rand_below(afl, l_diff - f_diff); /* Do the thing. */ -- cgit 1.4.1 From fb14e55cc960e03443c1c7b608287398a0cf0d3e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 7 Aug 2020 09:05:40 +0200 Subject: fix -N description --- src/afl-fuzz.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index da30797c..c73a76c6 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -115,8 +115,8 @@ static void usage(afl_state_t *afl, u8 *argv0, int more_help) { " if using QEMU, just use -c 0.\n\n" "Fuzzing behavior settings:\n" - " -N - do not unlink the fuzzing input file (only for " - "devices etc.!)\n" + " -N - do not unlink the fuzzing input file (for devices " + "etc.)\n" " -d - quick & dirty mode (skips deterministic steps)\n" " -n - fuzz without instrumentation (non-instrumented mode)\n" " -x dict_file - optional fuzzer dictionary (see README.md, its really " -- cgit 1.4.1 From 22d3a5e90abd58c6a4bb68bf1b3f7ece8283f5bb Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 7 Aug 2020 16:55:58 +0200 Subject: enabled Wextra, fixed bugs --- GNUmakefile | 2 +- include/debug.h | 4 +- src/afl-analyze.c | 5 ++- src/afl-as.c | 4 +- src/afl-forkserver.c | 6 ++- src/afl-fuzz-extras.c | 8 ++-- src/afl-fuzz-init.c | 11 ++++-- src/afl-fuzz-one.c | 99 ++++++++++++++++++++++++++----------------------- src/afl-fuzz-python.c | 7 ++++ src/afl-fuzz-redqueen.c | 14 +++---- src/afl-fuzz-run.c | 4 +- src/afl-fuzz-stats.c | 6 +-- src/afl-fuzz.c | 6 +-- src/afl-showmap.c | 6 +-- src/afl-tmin.c | 17 +++++---- 15 files changed, 110 insertions(+), 89 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index 679ccc82..4a0fcdb6 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -98,7 +98,7 @@ ifneq "$(shell uname -m)" "x86_64" endif CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT) -override CFLAGS += -Wall -g -Wno-pointer-sign \ +override CFLAGS += -Wall -Wextra -Werror -g -Wno-pointer-sign \ -I include/ -DAFL_PATH=\"$(HELPER_PATH)\" \ -DBIN_PATH=\"$(BIN_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\" diff --git a/include/debug.h b/include/debug.h index d1bd971b..79b05c5f 100644 --- a/include/debug.h +++ b/include/debug.h @@ -281,7 +281,7 @@ #define ck_write(fd, buf, len, fn) \ do { \ \ - u32 _len = (len); \ + s32 _len = (s32)(len); \ s32 _res = write(fd, buf, _len); \ if (_res != _len) RPFATAL(_res, "Short write to %s", fn); \ \ @@ -290,7 +290,7 @@ #define ck_read(fd, buf, len, fn) \ do { \ \ - u32 _len = (len); \ + s32 _len = (s32)(len); \ s32 _res = read(fd, buf, _len); \ if (_res != _len) RPFATAL(_res, "Short read from %s", fn); \ \ diff --git a/src/afl-analyze.c b/src/afl-analyze.c index e6dd0fca..7c1c269a 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -384,7 +384,7 @@ static void show_legend(void) { /* Interpret and report a pattern in the input file. */ -static void dump_hex(u8 *buf, u32 len, u8 *b_data) { +static void dump_hex(u32 len, u8 *b_data) { u32 i; @@ -678,7 +678,7 @@ static void analyze(char **argv) { } - dump_hex(in_data, in_len, b_data); + dump_hex(in_len, b_data); SAYF("\n"); @@ -700,6 +700,7 @@ static void analyze(char **argv) { static void handle_stop_sig(int sig) { + (void)sig; stop_soon = 1; if (child_pid > 0) { kill(child_pid, SIGKILL); } diff --git a/src/afl-as.c b/src/afl-as.c index f16d6060..4ba4cc71 100644 --- a/src/afl-as.c +++ b/src/afl-as.c @@ -136,7 +136,7 @@ static void edit_params(int argc, char **argv) { as_params[argc] = 0; - for (i = 1; i < argc - 1; i++) { + for (i = 1; (s32)i < argc - 1; i++) { if (!strcmp(argv[i], "--64")) { @@ -591,7 +591,7 @@ int main(int argc, char **argv) { rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); // in fast systems where pids can repeat in the same seconds we need this - for (i = 1; i < argc; i++) + for (i = 1; (s32)i < argc; i++) for (j = 0; j < strlen(argv[i]); j++) rand_seed += argv[i][j]; diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 47493eba..15935ab0 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -145,6 +145,10 @@ restart_select: if (likely(sret > 0)) { restart_read: + if (*stop_soon_p) { + // Early return - the user wants to quit. + return 0; + } len_read = read(fd, (u8 *)buf, 4); if (likely(len_read == 4)) { // for speed we put this first @@ -691,7 +695,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, } offset = 0; - while (offset < status && (u8)dict[offset] + offset < status) { + while (offset < (u32)status && (u8)dict[offset] + offset < (u32)status) { fsrv->function_ptr(fsrv->function_opt, dict + offset + 1, (u8)dict[offset]); diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 12771cd7..097871c8 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -115,7 +115,7 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, if (*lptr == '@') { ++lptr; - if (atoi(lptr) > dict_level) { continue; } + if (atoi(lptr) > (s32)dict_level) { continue; } while (isdigit(*lptr)) { ++lptr; @@ -402,7 +402,7 @@ void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) { while (i--) { - if (*((u32 *)mem) == interesting_32[i] || + if (*((u32 *)mem) == (u32)interesting_32[i] || *((u32 *)mem) == SWAP32(interesting_32[i])) { return; @@ -480,7 +480,7 @@ sort_a_extras: /* Then, sort the top USE_AUTO_EXTRAS entries by size. */ - qsort(afl->a_extras, MIN(USE_AUTO_EXTRAS, afl->a_extras_cnt), + qsort(afl->a_extras, MIN((u32)USE_AUTO_EXTRAS, afl->a_extras_cnt), sizeof(struct extra_data), compare_extras_len); } @@ -494,7 +494,7 @@ void save_auto(afl_state_t *afl) { if (!afl->auto_changed) { return; } afl->auto_changed = 0; - for (i = 0; i < MIN(USE_AUTO_EXTRAS, afl->a_extras_cnt); ++i) { + for (i = 0; i < MIN((u32)USE_AUTO_EXTRAS, afl->a_extras_cnt); ++i) { u8 *fn = alloc_printf("%s/queue/.state/auto_extras/auto_%06u", afl->out_dir, i); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 2c17ffbb..1abdcede 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -110,7 +110,7 @@ void bind_to_free_cpu(afl_state_t *afl) { u8 cpu_used[4096] = {0}; u8 lockfile[PATH_MAX] = ""; - u32 i; + s32 i; if (afl->afl_env.afl_no_affinity) { @@ -509,7 +509,7 @@ void read_foreign_testcases(afl_state_t *afl, int first) { afl->stage_cur = 0; afl->stage_max = 0; - for (i = 0; i < nl_cnt; ++i) { + for (i = 0; i < (u32)nl_cnt; ++i) { struct stat st; @@ -667,7 +667,7 @@ void read_testcases(afl_state_t *afl) { } - for (i = 0; i < nl_cnt; ++i) { + for (i = 0; i < (u32)nl_cnt; ++i) { struct stat st; @@ -2147,7 +2147,7 @@ void get_core_count(afl_state_t *afl) { WARNF("System under apparent load, performance may be spotty."); - } else if (cur_runnable + 1 <= afl->cpu_core_count) { + } else if ((s64)cur_runnable + 1 <= (s64)afl->cpu_core_count) { OKF("Try parallel jobs - see %s/parallel_fuzzing.md.", doc_path); @@ -2201,6 +2201,7 @@ void fix_up_sync(afl_state_t *afl) { static void handle_resize(int sig) { + (void)sig; afl_states_clear_screen(); } @@ -2252,6 +2253,7 @@ void check_asan_opts(void) { static void handle_stop_sig(int sig) { + (void)sig; afl_states_stop(); } @@ -2260,6 +2262,7 @@ static void handle_stop_sig(int sig) { static void handle_skipreq(int sig) { + (void)sig; afl_states_request_skip(); } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 1f0bf30e..9d09f6af 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -77,7 +77,7 @@ static int select_algorithm(afl_state_t *afl) { static u32 choose_block_len(afl_state_t *afl, u32 limit) { u32 min_value, max_value; - u32 rlim = MIN(afl->queue_cycle, 3); + u32 rlim = MIN(afl->queue_cycle, (u32)3); if (unlikely(!afl->run_over10m)) { rlim = 1; } @@ -292,7 +292,7 @@ static u8 could_be_interest(u32 old_val, u32 new_val, u8 blen, u8 check_le) { /* See if two-byte insertions over old_val could give us new_val. */ - for (i = 0; i < blen - 1; ++i) { + for (i = 0; (s32)i < blen - 1; ++i) { for (j = 0; j < sizeof(interesting_16) / 2; ++j) { @@ -372,7 +372,9 @@ static void locate_diffs(u8 *ptr1, u8 *ptr2, u32 len, s32 *first, s32 *last) { u8 fuzz_one_original(afl_state_t *afl) { - s32 len, fd, temp_len, i, j; + s32 len, fd, temp_len; + u32 j; + u32 i; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; u64 havoc_queued = 0, orig_hit_cnt, new_hit_cnt = 0, prev_cksum; u32 splice_cycle = 0, perf_score = 100, orig_perf, eff_cnt = 1; @@ -862,7 +864,7 @@ u8 fuzz_one_original(afl_state_t *afl) { whole thing as worth fuzzing, since we wouldn't be saving much time anyway. */ - if (eff_cnt != EFF_ALEN(len) && + if (eff_cnt != (u32)EFF_ALEN(len) && eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) { memset(eff_map, 1, EFF_ALEN(len)); @@ -893,7 +895,7 @@ u8 fuzz_one_original(afl_state_t *afl) { orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 1; ++i) { + for (i = 0; (s32)i < len - 1; ++i) { /* Let's consult the effector map... */ @@ -931,7 +933,7 @@ u8 fuzz_one_original(afl_state_t *afl) { orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 3; ++i) { + for (i = 0; (s32)i < len - 3; ++i) { /* Let's consult the effector map... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] && @@ -977,7 +979,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u8 orig = out_buf[i]; @@ -1051,7 +1053,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 1; ++i) { + for (i = 0; i < (u32)len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -1161,7 +1163,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 3; ++i) { + for (i = 0; i < (u32)len - 3; ++i) { u32 orig = *(u32 *)(out_buf + i); @@ -1202,7 +1204,7 @@ skip_bitflip: } - if ((orig & 0xffff) < j && !could_be_bitflip(r2)) { + if ((orig & 0xffff) < (u32)j && !could_be_bitflip(r2)) { afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = orig - j; @@ -1234,7 +1236,7 @@ skip_bitflip: } - if ((SWAP32(orig) & 0xffff) < j && !could_be_bitflip(r4)) { + if ((SWAP32(orig) & 0xffff) < (u32)j && !could_be_bitflip(r4)) { afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) - j); @@ -1276,7 +1278,7 @@ skip_arith: /* Setting 8-bit integers. */ - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u8 orig = out_buf[i]; @@ -1291,7 +1293,7 @@ skip_arith: afl->stage_cur_byte = i; - for (j = 0; j < sizeof(interesting_8); ++j) { + for (j = 0; j < (u32)sizeof(interesting_8); ++j) { /* Skip if the value could be a product of bitflips or arithmetics. */ @@ -1331,7 +1333,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 1; ++i) { + for (i = 0; (s32)i < len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -1409,7 +1411,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 3; i++) { + for (i = 0; (s32)i < len - 3; i++) { u32 orig = *(u32 *)(out_buf + i); @@ -1496,7 +1498,7 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u32 last_len = 0; @@ -1556,7 +1558,7 @@ skip_interest: ex_tmp = ck_maybe_grow(BUF_PARAMS(ex), len + MAX_DICT_FILE); - for (i = 0; i <= len; ++i) { + for (i = 0; i <= (u32)len; ++i) { afl->stage_cur_byte = i; @@ -1602,19 +1604,20 @@ skip_user_extras: afl->stage_name = "auto extras (over)"; afl->stage_short = "ext_AO"; afl->stage_cur = 0; - afl->stage_max = MIN(afl->a_extras_cnt, USE_AUTO_EXTRAS) * len; + afl->stage_max = MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS) * len; afl->stage_val_type = STAGE_VAL_NONE; orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u32 last_len = 0; afl->stage_cur_byte = i; - for (j = 0; j < MIN(afl->a_extras_cnt, USE_AUTO_EXTRAS); ++j) { + u32 min_extra_len = MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS); + for (j = 0; j < min_extra_len; ++j) { /* See the comment in the earlier code; extras are sorted by size. */ @@ -2231,7 +2234,7 @@ havoc_stage: u32 extra_len = afl->a_extras[use_extra].len; u32 insert_at; - if (extra_len > temp_len) { break; } + if ((s32)extra_len > temp_len) { break; } insert_at = rand_below(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, @@ -2245,7 +2248,7 @@ havoc_stage: u32 extra_len = afl->extras[use_extra].len; u32 insert_at; - if (extra_len > temp_len) { break; } + if ((s32)extra_len > temp_len) { break; } insert_at = rand_below(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->extras[use_extra].data, @@ -2360,7 +2363,7 @@ havoc_stage: u32 copy_from, copy_to, copy_len; copy_len = choose_block_len(afl, new_len - 1); - if (copy_len > temp_len) copy_len = temp_len; + if ((s32)copy_len > temp_len) copy_len = temp_len; copy_from = rand_below(afl, new_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); @@ -2517,7 +2520,7 @@ retry_splicing: the last differing byte. Bail out if the difference is just a single byte or so. */ - locate_diffs(in_buf, new_buf, MIN(len, target->len), &f_diff, &l_diff); + locate_diffs(in_buf, new_buf, MIN(len, (s64)target->len), &f_diff, &l_diff); if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { goto retry_splicing; } @@ -2587,7 +2590,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { } - s32 len, fd, temp_len, i, j; + s32 len, fd, temp_len; + u32 i; + u32 j; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; u64 havoc_queued = 0, orig_hit_cnt, new_hit_cnt = 0, cur_ms_lv, prev_cksum; u32 splice_cycle = 0, perf_score = 100, orig_perf, eff_cnt = 1; @@ -2761,9 +2766,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { cur_ms_lv = get_cur_time(); if (!(afl->key_puppet == 0 && - ((cur_ms_lv - afl->last_path_time < afl->limit_time_puppet) || + ((cur_ms_lv - afl->last_path_time < (u32)afl->limit_time_puppet) || (afl->last_crash_time != 0 && - cur_ms_lv - afl->last_crash_time < afl->limit_time_puppet) || + cur_ms_lv - afl->last_crash_time < (u32)afl->limit_time_puppet) || afl->last_path_time == 0))) { afl->key_puppet = 1; @@ -3058,7 +3063,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { whole thing as worth fuzzing, since we wouldn't be saving much time anyway. */ - if (eff_cnt != EFF_ALEN(len) && + if (eff_cnt != (u32)EFF_ALEN(len) && eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) { memset(eff_map, 1, EFF_ALEN(len)); @@ -3089,7 +3094,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 1; ++i) { + for (i = 0; (s32)i < len - 1; ++i) { /* Let's consult the effector map... */ @@ -3127,7 +3132,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 3; ++i) { + for (i = 0; (s32)i < len - 3; ++i) { /* Let's consult the effector map... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] && @@ -3173,7 +3178,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u8 orig = out_buf[i]; @@ -3247,7 +3252,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 1; ++i) { + for (i = 0; (s32)i < len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -3357,7 +3362,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 3; ++i) { + for (i = 0; (s32)i < len - 3; ++i) { u32 orig = *(u32 *)(out_buf + i); @@ -3472,7 +3477,7 @@ skip_arith: /* Setting 8-bit integers. */ - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u8 orig = out_buf[i]; @@ -3527,7 +3532,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 1; ++i) { + for (i = 0; (s32)i < len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -3605,7 +3610,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len - 3; ++i) { + for (i = 0; (s32)i < len - 3; ++i) { u32 orig = *(u32 *)(out_buf + i); @@ -3692,7 +3697,7 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u32 last_len = 0; @@ -3752,7 +3757,7 @@ skip_interest: ex_tmp = ck_maybe_grow(BUF_PARAMS(ex), len + MAX_DICT_FILE); - for (i = 0; i <= len; ++i) { + for (i = 0; i <= (u32)len; ++i) { afl->stage_cur_byte = i; @@ -3798,23 +3803,23 @@ skip_user_extras: afl->stage_name = "auto extras (over)"; afl->stage_short = "ext_AO"; afl->stage_cur = 0; - afl->stage_max = MIN(afl->a_extras_cnt, USE_AUTO_EXTRAS) * len; + afl->stage_max = MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS) * len; afl->stage_val_type = STAGE_VAL_NONE; orig_hit_cnt = new_hit_cnt; - for (i = 0; i < len; ++i) { + for (i = 0; i < (u32)len; ++i) { u32 last_len = 0; afl->stage_cur_byte = i; - for (j = 0; j < MIN(afl->a_extras_cnt, USE_AUTO_EXTRAS); ++j) { + for (j = 0; j < MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS); ++j) { /* See the comment in the earlier code; extras are sorted by size. */ - if (afl->a_extras[j].len > len - i || + if ((s32)(afl->a_extras[j].len) > (s32)(len - i) || !memcmp(afl->a_extras[j].data, out_buf + i, afl->a_extras[j].len) || !memchr(eff_map + EFF_APOS(i), 1, EFF_SPAN_ALEN(i, afl->a_extras[j].len))) { @@ -4276,7 +4281,7 @@ pacemaker_fuzzing: u32 use_extra = rand_below(afl, afl->a_extras_cnt); u32 extra_len = afl->a_extras[use_extra].len; - if (extra_len > temp_len) break; + if (extra_len > (u32)temp_len) break; u32 insert_at = rand_below(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, @@ -4289,7 +4294,7 @@ pacemaker_fuzzing: u32 use_extra = rand_below(afl, afl->extras_cnt); u32 extra_len = afl->extras[use_extra].len; - if (extra_len > temp_len) break; + if (extra_len > (u32)temp_len) break; u32 insert_at = rand_below(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->extras[use_extra].data, @@ -4449,7 +4454,7 @@ pacemaker_fuzzing: retry_splicing_puppet: - if (afl->use_splicing && splice_cycle++ < afl->SPLICE_CYCLES_puppet && + if (afl->use_splicing && splice_cycle++ < (u32)afl->SPLICE_CYCLES_puppet && afl->queued_paths > 1 && afl->queue_cur->len > 1) { struct queue_entry *target; @@ -4519,7 +4524,7 @@ pacemaker_fuzzing: the last differing byte. Bail out if the difference is just a single byte or so. */ - locate_diffs(in_buf, new_buf, MIN(len, target->len), &f_diff, &l_diff); + locate_diffs(in_buf, new_buf, MIN(len, (s32)target->len), &f_diff, &l_diff); if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { @@ -4551,7 +4556,7 @@ pacemaker_fuzzing: abandon_entry: abandon_entry_puppet: - if (splice_cycle >= afl->SPLICE_CYCLES_puppet) { + if ((s64)splice_cycle >= afl->SPLICE_CYCLES_puppet) { afl->SPLICE_CYCLES_puppet = (rand_below( diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index 2044c97d..a077469e 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -30,6 +30,9 @@ static void *unsupported(afl_state_t *afl, unsigned int seed) { + (void)afl; + (void)seed; + FATAL("Python Mutator cannot be called twice yet"); return NULL; @@ -111,6 +114,8 @@ static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { + (void)afl; + if (!module_name) { return NULL; } py_mutator_t *py = calloc(1, sizeof(py_mutator_t)); @@ -247,6 +252,8 @@ void finalize_py_module(void *py_mutator) { static void init_py(afl_state_t *afl, py_mutator_t *py_mutator, unsigned int seed) { + (void)afl; + PyObject *py_args, *py_value; /* Provide the init function a seed for the Python RNG */ diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index cb4c78df..1cfe8802 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -352,7 +352,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - if (use_num && num == pattern) { + if (use_num && (u64) num == pattern) { size_t old_len = endptr - buf_8; size_t num_len = snprintf(NULL, 0, "%lld", num); @@ -659,12 +659,12 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } -static u8 rtn_extend_encoding(afl_state_t *afl, struct cmp_header *h, +static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, u8 *o_pattern, u32 idx, u8 *orig_buf, u8 *buf, u32 len, u8 *status) { u32 i; - u32 its_len = MIN(32, len - idx); + u32 its_len = MIN((u32)32, len - idx); u8 save[32]; memcpy(save, &buf[idx], its_len); @@ -728,7 +728,7 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { for (idx = 0; idx < len && fails < 8; ++idx) { - if (unlikely(rtn_extend_encoding(afl, h, o->v0, o->v1, orig_o->v0, idx, + if (unlikely(rtn_extend_encoding(afl, o->v0, o->v1, orig_o->v0, idx, orig_buf, buf, len, &status))) { return 1; @@ -745,7 +745,7 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } - if (unlikely(rtn_extend_encoding(afl, h, o->v1, o->v0, orig_o->v1, idx, + if (unlikely(rtn_extend_encoding(afl, o->v1, o->v0, orig_o->v1, idx, orig_buf, buf, len, &status))) { return 1; @@ -853,12 +853,12 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, if (afl->shm.cmp_map->headers[k].type == CMP_TYPE_INS) { - afl->stage_max += MIN((u32)afl->shm.cmp_map->headers[k].hits, CMP_MAP_H); + afl->stage_max += MIN((u32)(afl->shm.cmp_map->headers[k].hits), (u32)CMP_MAP_H); } else { afl->stage_max += - MIN((u32)afl->shm.cmp_map->headers[k].hits, CMP_MAP_RTN_H); + MIN((u32)(afl->shm.cmp_map->headers[k].hits), (u32)CMP_MAP_RTN_H); } diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index ed4a1081..8d652155 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -733,12 +733,12 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { len_p2 = next_pow2(q->len); - remove_len = MAX(len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES); + remove_len = MAX(len_p2 / TRIM_START_STEPS, (u32)TRIM_MIN_BYTES); /* Continue until the number of steps gets too high or the stepover gets too small. */ - while (remove_len >= MAX(len_p2 / TRIM_END_STEPS, TRIM_MIN_BYTES)) { + while (remove_len >= MAX(len_p2 / TRIM_END_STEPS, (u32)TRIM_MIN_BYTES)) { u32 remove_pos = remove_len; diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 7b30b5ea..aeb290bd 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -890,12 +890,12 @@ void show_stats(afl_state_t *afl) { if (afl->cpu_aff >= 0) { SAYF("%s" cGRA "[cpu%03u:%s%3u%%" cGRA "]\r" cRST, spacing, - MIN(afl->cpu_aff, 999), cpu_color, MIN(cur_utilization, 999)); + MIN(afl->cpu_aff, 999), cpu_color, MIN(cur_utilization, (u32)999)); } else { SAYF("%s" cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, spacing, cpu_color, - MIN(cur_utilization, 999)); + MIN(cur_utilization, (u32)999)); } @@ -1081,7 +1081,7 @@ void show_init_stats(afl_state_t *afl) { if (afl->non_instrumented_mode && !(afl->afl_env.afl_hang_tmout)) { - afl->hang_tmout = MIN(EXEC_TIMEOUT, afl->fsrv.exec_tmout * 2 + 100); + afl->hang_tmout = MIN((u32)EXEC_TIMEOUT, afl->fsrv.exec_tmout * 2 + 100); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index c73a76c6..031c4049 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -79,7 +79,7 @@ static void at_exit() { /* Display usage hints. */ -static void usage(afl_state_t *afl, u8 *argv0, int more_help) { +static void usage(u8 *argv0, int more_help) { SAYF( "\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n" @@ -677,7 +677,7 @@ int main(int argc, char **argv_orig, char **envp) { u64 limit_time_puppet2 = afl->limit_time_puppet * 60 * 1000; - if (limit_time_puppet2 < afl->limit_time_puppet) { + if ((s32)limit_time_puppet2 < afl->limit_time_puppet) { FATAL("limit_time overflow"); @@ -811,7 +811,7 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !afl->in_dir || !afl->out_dir || show_help) { - usage(afl, argv[0], show_help); + usage(argv[0], show_help); } diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 71e975a1..4962006e 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -256,8 +256,7 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { /* Execute target application. */ -static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, char **argv, - u8 *mem, u32 len) { +static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem, u32 len) { afl_fsrv_write_to_testcase(fsrv, mem, len); @@ -444,6 +443,7 @@ static void showmap_run_target(afl_forkserver_t *fsrv, char **argv) { static void handle_stop_sig(int sig) { + (void)sig; stop_soon = 1; afl_fsrv_killall(); @@ -1016,7 +1016,7 @@ int main(int argc, char **argv_orig, char **envp) { } - showmap_run_target_forkserver(fsrv, use_argv, in_data, in_len); + showmap_run_target_forkserver(fsrv, in_data, in_len); ck_free(in_data); tcnt = write_results_to_file(fsrv, outfile); diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 68fcdd14..b50d8597 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -250,7 +250,7 @@ static s32 write_to_file(u8 *path, u8 *mem, u32 len) { /* Execute target application. Returns 0 if the changes are a dud, or 1 if they should be kept. */ -static u8 tmin_run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len, +static u8 tmin_run_target(afl_forkserver_t *fsrv, u8 *mem, u32 len, u8 first_run) { afl_fsrv_write_to_testcase(fsrv, mem, len); @@ -342,7 +342,7 @@ static u8 tmin_run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len, /* Actually minimize! */ -static void minimize(afl_forkserver_t *fsrv, char **argv) { +static void minimize(afl_forkserver_t *fsrv) { static u32 alpha_map[256]; @@ -380,7 +380,7 @@ static void minimize(afl_forkserver_t *fsrv, char **argv) { memset(tmp_buf + set_pos, '0', use_len); u8 res; - res = tmin_run_target(fsrv, argv, tmp_buf, in_len, 0); + res = tmin_run_target(fsrv, tmp_buf, in_len, 0); if (res) { @@ -453,7 +453,7 @@ next_del_blksize: /* Tail */ memcpy(tmp_buf + del_pos, in_data + del_pos + del_len, tail_len); - res = tmin_run_target(fsrv, argv, tmp_buf, del_pos + tail_len, 0); + res = tmin_run_target(fsrv, tmp_buf, del_pos + tail_len, 0); if (res) { @@ -524,7 +524,7 @@ next_del_blksize: } - res = tmin_run_target(fsrv, argv, tmp_buf, in_len, 0); + res = tmin_run_target(fsrv, tmp_buf, in_len, 0); if (res) { @@ -560,7 +560,7 @@ next_del_blksize: if (orig == '0') { continue; } tmp_buf[i] = '0'; - res = tmin_run_target(fsrv, argv, tmp_buf, in_len, 0); + res = tmin_run_target(fsrv, tmp_buf, in_len, 0); if (res) { @@ -623,6 +623,7 @@ finalize_all: static void handle_stop_sig(int sig) { + (void)sig; stop_soon = 1; afl_fsrv_killall(); @@ -1131,7 +1132,7 @@ int main(int argc, char **argv_orig, char **envp) { ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...", fsrv->mem_limit, fsrv->exec_tmout, edges_only ? ", edges only" : ""); - tmin_run_target(fsrv, use_argv, in_data, in_len, 1); + tmin_run_target(fsrv, in_data, in_len, 1); if (hang_mode && !fsrv->last_run_timed_out) { @@ -1169,7 +1170,7 @@ int main(int argc, char **argv_orig, char **envp) { } - minimize(fsrv, use_argv); + minimize(fsrv); ACTF("Writing output to '%s'...", output_file); -- cgit 1.4.1 From 699ebaa8e210e0d72ad7e3ac6f4a580cfbe37eae Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 7 Aug 2020 17:32:41 +0200 Subject: code format --- include/debug.h | 4 ++-- src/afl-forkserver.c | 6 +++++- src/afl-fuzz-one.c | 6 ++++-- src/afl-fuzz-redqueen.c | 11 ++++++----- src/afl-showmap.c | 3 ++- 5 files changed, 19 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/include/debug.h b/include/debug.h index 79b05c5f..ae2946f0 100644 --- a/include/debug.h +++ b/include/debug.h @@ -281,7 +281,7 @@ #define ck_write(fd, buf, len, fn) \ do { \ \ - s32 _len = (s32)(len); \ + s32 _len = (s32)(len); \ s32 _res = write(fd, buf, _len); \ if (_res != _len) RPFATAL(_res, "Short write to %s", fn); \ \ @@ -290,7 +290,7 @@ #define ck_read(fd, buf, len, fn) \ do { \ \ - s32 _len = (s32)(len); \ + s32 _len = (s32)(len); \ s32 _res = read(fd, buf, _len); \ if (_res != _len) RPFATAL(_res, "Short read from %s", fn); \ \ diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 15935ab0..752641d7 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -146,9 +146,12 @@ restart_select: restart_read: if (*stop_soon_p) { + // Early return - the user wants to quit. return 0; + } + len_read = read(fd, (u8 *)buf, 4); if (likely(len_read == 4)) { // for speed we put this first @@ -695,7 +698,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, } offset = 0; - while (offset < (u32)status && (u8)dict[offset] + offset < (u32)status) { + while (offset < (u32)status && + (u8)dict[offset] + offset < (u32)status) { fsrv->function_ptr(fsrv->function_opt, dict + offset + 1, (u8)dict[offset]); diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 9d09f6af..74e09ee3 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -4454,7 +4454,8 @@ pacemaker_fuzzing: retry_splicing_puppet: - if (afl->use_splicing && splice_cycle++ < (u32)afl->SPLICE_CYCLES_puppet && + if (afl->use_splicing && + splice_cycle++ < (u32)afl->SPLICE_CYCLES_puppet && afl->queued_paths > 1 && afl->queue_cur->len > 1) { struct queue_entry *target; @@ -4524,7 +4525,8 @@ pacemaker_fuzzing: the last differing byte. Bail out if the difference is just a single byte or so. */ - locate_diffs(in_buf, new_buf, MIN(len, (s32)target->len), &f_diff, &l_diff); + locate_diffs(in_buf, new_buf, MIN(len, (s32)target->len), &f_diff, + &l_diff); if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 1cfe8802..9716be95 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -352,7 +352,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - if (use_num && (u64) num == pattern) { + if (use_num && (u64)num == pattern) { size_t old_len = endptr - buf_8; size_t num_len = snprintf(NULL, 0, "%lld", num); @@ -659,9 +659,9 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } -static u8 rtn_extend_encoding(afl_state_t *afl, - u8 *pattern, u8 *repl, u8 *o_pattern, u32 idx, - u8 *orig_buf, u8 *buf, u32 len, u8 *status) { +static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, + u8 *o_pattern, u32 idx, u8 *orig_buf, u8 *buf, + u32 len, u8 *status) { u32 i; u32 its_len = MIN((u32)32, len - idx); @@ -853,7 +853,8 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, if (afl->shm.cmp_map->headers[k].type == CMP_TYPE_INS) { - afl->stage_max += MIN((u32)(afl->shm.cmp_map->headers[k].hits), (u32)CMP_MAP_H); + afl->stage_max += + MIN((u32)(afl->shm.cmp_map->headers[k].hits), (u32)CMP_MAP_H); } else { diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 4962006e..0aa116e5 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -256,7 +256,8 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { /* Execute target application. */ -static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem, u32 len) { +static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem, + u32 len) { afl_fsrv_write_to_testcase(fsrv, mem, len); -- cgit 1.4.1 From 27abecbff5021d9eaa226a7f370e6b8f14ec9601 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Fri, 7 Aug 2020 21:07:05 +0200 Subject: compile fixes on 32-bit OSs --- src/afl-as.c | 6 +++--- src/afl-fuzz-init.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/afl-as.c b/src/afl-as.c index 4ba4cc71..0ed47d8c 100644 --- a/src/afl-as.c +++ b/src/afl-as.c @@ -407,7 +407,7 @@ static void add_instrumentation(void) { if (line[0] == '\t') { - if (line[1] == 'j' && line[2] != 'm' && R(100) < inst_ratio) { + if (line[1] == 'j' && line[2] != 'm' && R(100) < (long)inst_ratio) { fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, R(MAP_SIZE)); @@ -449,7 +449,7 @@ static void add_instrumentation(void) { /* Apple: L / LBB */ if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3))) && - R(100) < inst_ratio) { + R(100) < (long)inst_ratio) { #else @@ -457,7 +457,7 @@ static void add_instrumentation(void) { if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3))) && - R(100) < inst_ratio) { + R(100) < (long)inst_ratio) { #endif /* __APPLE__ */ diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 1abdcede..350a8599 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -762,7 +762,7 @@ void perform_dry_run(afl_state_t *afl) { use_mem = ck_alloc_nozero(q->len); - if (read(fd, use_mem, q->len) != q->len) { + if (read(fd, use_mem, q->len) != (ssize_t)q->len) { FATAL("Short read from '%s'", q->fname); -- cgit 1.4.1 From 9a1d526ed408cbd7d681be15c5512032f7632887 Mon Sep 17 00:00:00 2001 From: murx- Date: Sat, 8 Aug 2020 18:34:54 +0200 Subject: Add support for specific custom mutator name --- include/afl-fuzz.h | 1 + src/afl-fuzz-mutators.c | 1 + src/afl-fuzz-one.c | 2 ++ 3 files changed, 4 insertions(+) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index bb1bb314..51ab0e85 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -656,6 +656,7 @@ typedef struct afl_state { struct custom_mutator { const char *name; + char * name_short; void * dh; u8 * post_process_buf; size_t post_process_size; diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index b30106a0..0fa646f9 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -142,6 +142,7 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { struct custom_mutator *mutator = ck_alloc(sizeof(struct custom_mutator)); mutator->name = fn; + mutator->name_short = strrchr(fn, '/') + 1; ACTF("Loading custom mutator library from '%s'...", fn); dh = dlopen(fn, RTLD_NOW); diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 74e09ee3..452c5298 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1683,6 +1683,8 @@ custom_mutator_stage: has_custom_fuzz = true; + afl->stage_short = el->name_short; + for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { -- cgit 1.4.1 From e4a0237cbc745552a5b21a2450d7ab55ee98759d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 9 Aug 2020 00:35:12 +0200 Subject: step 1 --- GNUmakefile | 2 +- include/afl-fuzz.h | 6 ++++++ include/forkserver.h | 2 ++ qemu_taint/README.md | 2 +- src/afl-common.c | 1 + src/afl-forkserver.c | 15 +++++++++++++-- src/afl-fuzz-bitmap.c | 32 ++++++++++++++++++++++++++++++++ src/afl-fuzz-run.c | 13 +++++++++++++ src/afl-fuzz.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++---- 9 files changed, 116 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index 9b064eb6..4d0c434e 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -98,7 +98,7 @@ ifneq "$(shell uname -m)" "x86_64" endif CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT) -override CFLAGS += -Wall -Wextra -Werror -g -Wno-pointer-sign \ +override CFLAGS += -g -Wno-pointer-sign \ -I include/ -DAFL_PATH=\"$(HELPER_PATH)\" \ -DBIN_PATH=\"$(BIN_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\" diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index bb1bb314..eb7f8ca5 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -371,6 +371,8 @@ typedef struct afl_state { afl_env_vars_t afl_env; char **argv; /* argv if needed */ + + char **argv_taint; /* argv for taint mode */ /* MOpt: Lots of globals, but mostly for the status UI and other things where it @@ -581,6 +583,9 @@ typedef struct afl_state { char * cmplog_binary; afl_forkserver_t cmplog_fsrv; /* cmplog has its own little forkserver */ + /* Taint mode */ + afl_forkserver_t taint_fsrv; /* taint mode has its own little forkserver */ + /* Custom mutators */ struct custom_mutator *mutator; @@ -889,6 +894,7 @@ u32 calculate_score(afl_state_t *, struct queue_entry *); void write_bitmap(afl_state_t *); u32 count_bits(afl_state_t *, u8 *); +u32 count_bits_len(afl_state_t *, u8 *, u32); u32 count_bytes(afl_state_t *, u8 *); u32 count_non_255_bytes(afl_state_t *, u8 *); #ifdef WORD_SIZE_64 diff --git a/include/forkserver.h b/include/forkserver.h index 717493db..a5fca30e 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -79,6 +79,8 @@ typedef struct afl_forkserver { u8 use_fauxsrv; /* Fauxsrv for non-forking targets? */ u8 qemu_mode; /* if running in qemu mode or not */ + + u8 taint_mode; /* if running taint analysis or not */ u32 *shmem_fuzz_len; /* length of the fuzzing test case */ diff --git a/qemu_taint/README.md b/qemu_taint/README.md index c842da0e..6a7d19af 100644 --- a/qemu_taint/README.md +++ b/qemu_taint/README.md @@ -18,7 +18,7 @@ Only touched bytes are then fuzzed by afl-fuzz ## How to use -Add the -T flag to afl-fuzz +Add the -A flag to afl-fuzz ## Caveats diff --git a/src/afl-common.c b/src/afl-common.c index 367dec72..134d3180 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -138,6 +138,7 @@ void argv_cpy_free(char **argv) { } + /* Rewrite argv for QEMU. */ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 752641d7..b4f92e5b 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -481,6 +481,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "handle_sigill=0", 0); +fprintf(stderr, "init %p\n", fsrv->init_child_func); fsrv->init_child_func(fsrv, argv); /* Use a distinctive bitmap signature to tell the parent about execv() @@ -496,10 +497,20 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, char pid_buf[16]; sprintf(pid_buf, "%d", fsrv->fsrv_pid); - if (fsrv->cmplog_binary) + + if (fsrv->qemu_mode == 2) { + + setenv("__AFL_TARGET_PID3", pid_buf, 1); + + } else if (fsrv->cmplog_binary) { + setenv("__AFL_TARGET_PID2", pid_buf, 1); - else + + } else { + setenv("__AFL_TARGET_PID1", pid_buf, 1); + + } /* Close the unneeded endpoints. */ diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index aa8d5a18..9cb1b83f 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -177,6 +177,38 @@ u32 count_bits(afl_state_t *afl, u8 *mem) { } +u32 count_bits_len(afl_state_t *afl, u8 *mem, u32 len) { + + u32 *ptr = (u32 *)mem; + u32 i = (len >> 2); + u32 ret = 0; + + if (len % 4) i++; + + while (i--) { + + u32 v = *(ptr++); + + /* This gets called on the inverse, virgin bitmap; optimize for sparse + data. */ + + if (v == 0xffffffff) { + + ret += 32; + continue; + + } + + v -= ((v >> 1) & 0x55555555); + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); + ret += (((v + (v >> 4)) & 0xF0F0F0F) * 0x01010101) >> 24; + + } + + return ret; + +} + /* Count the number of bytes set in the bitmap. Called fairly sporadically, mostly to update the status screen or calibrate and examine confirmed new paths. */ diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 8d652155..207b3046 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -471,6 +471,19 @@ abort_calibration: afl->stage_cur = old_sc; afl->stage_max = old_sm; + /* if taint mode was selected, run the taint */ + + if (afl->fsrv.taint_mode) { + write_to_testcase(afl, use_mem, q->len); + if (afl_fsrv_run_target(&afl->taint_fsrv, use_tmout, &afl->stop_soon) == 0) { + u32 len = q->len / 8; + if (q->len % 8) len++; + u32 bits = count_bits_len(afl, afl->taint_fsrv.trace_bits, len); + if (afl->debug) fprintf(stderr, "Debug: tainted bytes: %u\n", bits); + + } + } + if (!first_run) { show_stats(afl); } return fault; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 031c4049..bc780b55 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -53,6 +53,9 @@ static void at_exit() { ptr = getenv("__AFL_TARGET_PID2"); if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL); + ptr = getenv("__AFL_TARGET_PID3"); + if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL); + i = 0; while (list[i] != NULL) { @@ -89,6 +92,7 @@ static void usage(u8 *argv0, int more_help) { " -o dir - output directory for fuzzer findings\n\n" "Execution control settings:\n" + " -A - use first level taint analysis (see qemu_taint/README.md)\n" " -p schedule - power schedules compute a seed's performance score. " " 0) { + "+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QANUWe:p:s:V:E:L:hRP:")) > 0) { switch (opt) { + case 'A': + afl->fsrv.taint_mode = 1; + if (!mem_limit_given) { afl->fsrv.mem_limit = MEM_LIMIT_QEMU; } + break; + case 'I': afl->infoexec = optarg; break; @@ -485,7 +494,7 @@ int main(int argc, char **argv_orig, char **envp) { if (!optarg) { FATAL("Wrong usage of -m"); } - if (!strcmp(optarg, "none")) { + if (!strcmp(optarg, "none") || !strcmp(optarg, "0")) { afl->fsrv.mem_limit = 0; break; @@ -815,6 +824,14 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->fsrv.taint_mode && afl->fsrv.map_size < (MAX_FILE / 8) + 1) { + + afl->shm.map_size = (MAX_FILE / 8); + if (MAX_FILE % 8) afl->shm.map_size++; + afl->fsrv.map_size = afl->shm.map_size; + + } + if (!mem_limit_given && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260; OKF("afl++ is maintained by Marc \"van Hauser\" Heuse, Heiko \"hexcoder\" " @@ -869,6 +886,7 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->crash_mode) { FATAL("-C and -n are mutually exclusive"); } if (afl->fsrv.qemu_mode) { FATAL("-Q and -n are mutually exclusive"); } if (afl->unicorn_mode) { FATAL("-U and -n are mutually exclusive"); } + if (afl->fsrv.taint_mode) { FATAL("-A and -n are mutually exclusive"); } } @@ -969,7 +987,7 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->afl_env.afl_preload) { - if (afl->fsrv.qemu_mode) { + if (afl->fsrv.qemu_mode || afl->fsrv.taint_mode) { u8 *qemu_preload = getenv("QEMU_SET_ENV"); u8 *afl_preload = getenv("AFL_PRELOAD"); @@ -1220,7 +1238,6 @@ int main(int argc, char **argv_orig, char **envp) { ACTF("Spawning cmplog forkserver"); afl_fsrv_init_dup(&afl->cmplog_fsrv, &afl->fsrv); - // TODO: this is semi-nice afl->cmplog_fsrv.trace_bits = afl->fsrv.trace_bits; afl->cmplog_fsrv.qemu_mode = afl->fsrv.qemu_mode; afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; @@ -1230,6 +1247,29 @@ int main(int argc, char **argv_orig, char **envp) { OKF("Cmplog forkserver successfully started"); } + + if (afl->fsrv.taint_mode) { + + ACTF("Spawning qemu_taint forkserver"); + afl_fsrv_init_dup(&afl->taint_fsrv, &afl->fsrv); + afl->taint_fsrv.qemu_mode = 2; + afl->taint_fsrv.trace_bits = afl->fsrv.trace_bits; + ck_free(afl->taint_fsrv.target_path); + afl->taint_fsrv.target_path = ck_strdup(afl->fsrv.target_path); + afl->argv_taint = get_qemu_argv(argv[0], &afl->taint_fsrv.target_path, + argc - optind, argv + optind); + u32 len = strlen(afl->taint_fsrv.target_path); + strcpy(afl->taint_fsrv.target_path + len - 5, "taint"); + strcpy((afl->argv_taint[0]) + len - 5, "taint"); + if (afl->fsrv.use_stdin) + unsetenv("AFL_TAINT_INPUT"); + else + setenv("AFL_TAINT_INPUT", afl->fsrv.out_file, 1); + afl_fsrv_start(&afl->taint_fsrv, afl->argv_taint, &afl->stop_soon, + afl->afl_env.afl_debug_child_output); + OKF("Taint forkserver successfully started"); + + } perform_dry_run(afl); @@ -1493,8 +1533,11 @@ stop_fuzzing: } + if (afl->cmplog_binary) afl_fsrv_deinit(&afl->cmplog_fsrv); + if (afl->fsrv.taint_mode) afl_fsrv_deinit(&afl->taint_fsrv); afl_fsrv_deinit(&afl->fsrv); if (afl->orig_cmdline) { ck_free(afl->orig_cmdline); } + if (afl->argv_taint) { ck_free(afl->argv_taint); } ck_free(afl->fsrv.target_path); ck_free(afl->fsrv.out_file); ck_free(afl->sync_id); -- cgit 1.4.1 From 0bb59ba11606e0382126304f78507efe7d62fd6b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 9 Aug 2020 01:09:26 +0200 Subject: code format --- include/afl-fuzz.h | 2 +- include/common.h | 1 + include/forkserver.h | 2 +- src/afl-common.c | 140 +++++++++++++++++---------------------------------- src/afl-forkserver.c | 11 ++-- src/afl-fuzz-run.c | 8 ++- src/afl-fuzz.c | 25 ++++++--- 7 files changed, 78 insertions(+), 111 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index eb7f8ca5..37e2dc6c 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -371,7 +371,7 @@ typedef struct afl_state { afl_env_vars_t afl_env; char **argv; /* argv if needed */ - + char **argv_taint; /* argv for taint mode */ /* MOpt: diff --git a/include/common.h b/include/common.h index 87a7425b..42c79c62 100644 --- a/include/common.h +++ b/include/common.h @@ -55,6 +55,7 @@ extern u8 *doc_path; /* path to documentation dir */ @returns the path, allocating the string */ u8 *find_binary(u8 *fname); +u8 *find_binary_own_loc(u8 *fname, u8 *own_loc); /* Read a bitmap from file fname to memory This is for the -B option again. */ diff --git a/include/forkserver.h b/include/forkserver.h index a5fca30e..89f23ab7 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -79,7 +79,7 @@ typedef struct afl_forkserver { u8 use_fauxsrv; /* Fauxsrv for non-forking targets? */ u8 qemu_mode; /* if running in qemu mode or not */ - + u8 taint_mode; /* if running taint analysis or not */ u32 *shmem_fuzz_len; /* length of the fuzzing test case */ diff --git a/src/afl-common.c b/src/afl-common.c index 134d3180..c0202821 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -138,32 +138,19 @@ void argv_cpy_free(char **argv) { } +u8 *find_binary_own_loc(u8 *fname, u8 *own_loc) { -/* Rewrite argv for QEMU. */ - -char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { - - char **new_argv = ck_alloc(sizeof(char *) * (argc + 4)); - u8 * tmp, *cp = NULL, *rsl, *own_copy; - - memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1)); - new_argv[argc - 1] = NULL; - - new_argv[2] = *target_path_p; - new_argv[1] = "--"; - - /* Now we need to actually find the QEMU binary to put in argv[0]. */ + u8 *tmp, *rsl, *own_copy, *cp; tmp = getenv("AFL_PATH"); if (tmp) { - cp = alloc_printf("%s/afl-qemu-trace", tmp); + cp = alloc_printf("%s/%s", tmp, fname); if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); } - *target_path_p = new_argv[0] = cp; - return new_argv; + return cp; } @@ -174,15 +161,10 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { *rsl = 0; - cp = alloc_printf("%s/afl-qemu-trace", own_copy); + cp = alloc_printf("%s/%s", own_copy, fname); ck_free(own_copy); - if (!access(cp, X_OK)) { - - *target_path_p = new_argv[0] = cp; - return new_argv; - - } + if (!access(cp, X_OK)) { return cp; } } else { @@ -190,11 +172,35 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { } - if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { + cp = alloc_printf("%s/%s", BIN_PATH, fname); + if (!access(cp, X_OK)) { return cp; } + + ck_free(cp); + + return NULL; - if (cp) { ck_free(cp); } - *target_path_p = new_argv[0] = ck_strdup(BIN_PATH "/afl-qemu-trace"); +} + +/* Rewrite argv for QEMU. */ + +char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { + + char **new_argv = ck_alloc(sizeof(char *) * (argc + 4)); + u8 * cp = NULL; + + memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1)); + new_argv[argc - 1] = NULL; + new_argv[2] = *target_path_p; + new_argv[1] = "--"; + + /* Now we need to actually find the QEMU binary to put in argv[0]. */ + + cp = find_binary_own_loc("afl-qemu-trace", own_loc); + + if (cp) { + + *target_path_p = new_argv[0] = cp; return new_argv; } @@ -235,66 +241,16 @@ char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { /* Now we need to actually find the QEMU binary to put in argv[0]. */ - tmp = getenv("AFL_PATH"); - - if (tmp) { + cp = find_binary_own_loc("afl-qemu-trace", own_loc); - cp = alloc_printf("%s/afl-qemu-trace", tmp); - - if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); } + if (cp) { ck_free(cp); + cp = find_binary_own_loc("afl-wine-trace", own_loc); - cp = alloc_printf("%s/afl-wine-trace", tmp); + if (cp) { - if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); } - - *target_path_p = new_argv[0] = cp; - return new_argv; - - } - - own_copy = ck_strdup(own_loc); - rsl = strrchr(own_copy, '/'); - - if (rsl) { - - *rsl = 0; - - cp = alloc_printf("%s/afl-qemu-trace", own_copy); - - if (cp && !access(cp, X_OK)) { - - ck_free(cp); - - cp = alloc_printf("%s/afl-wine-trace", own_copy); - - if (!access(cp, X_OK)) { - - *target_path_p = new_argv[0] = cp; - return new_argv; - - } - - } - - ck_free(own_copy); - - } else { - - ck_free(own_copy); - - } - - u8 *ncp = BIN_PATH "/afl-qemu-trace"; - - if (!access(ncp, X_OK)) { - - ncp = BIN_PATH "/afl-wine-trace"; - - if (!access(ncp, X_OK)) { - - *target_path_p = new_argv[0] = ck_strdup(ncp); + *target_path_p = new_argv[0] = cp; return new_argv; } @@ -302,25 +258,21 @@ char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { } SAYF("\n" cLRD "[-] " cRST - "Oops, unable to find the '%s' binary. The binary must be " - "built\n" - " separately by following the instructions in " - "qemu_mode/README.md. " - "If you\n" - " already have the binary installed, you may need to specify " - "AFL_PATH in the\n" - " environment.\n\n" - + "Oops, unable to find the afl-qemu-trace and afl-wine-trace binaries.\n" + "The afl-qemu-trace binary must be built separately by following the " + "instructions\n" + "in qemu_mode/README.md. If you already have the binary installed, you " + "may need\n" + "to specify the location via AFL_PATH in the environment.\n\n" " Of course, even without QEMU, afl-fuzz can still work with " "binaries that are\n" " instrumented at compile time with afl-gcc. It is also possible to " "use it as a\n" " traditional non-instrumented fuzzer by specifying '-n' in the " "command " - "line.\n", - ncp); + "line.\n"); - FATAL("Failed to locate '%s'.", ncp); + FATAL("Failed to locate 'afl-qemu-trace' and 'afl-wine-trace'."); } diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index b4f92e5b..eeb2f8c3 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -481,7 +481,6 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "handle_sigill=0", 0); -fprintf(stderr, "init %p\n", fsrv->init_child_func); fsrv->init_child_func(fsrv, argv); /* Use a distinctive bitmap signature to tell the parent about execv() @@ -497,19 +496,19 @@ fprintf(stderr, "init %p\n", fsrv->init_child_func); char pid_buf[16]; sprintf(pid_buf, "%d", fsrv->fsrv_pid); - + if (fsrv->qemu_mode == 2) { setenv("__AFL_TARGET_PID3", pid_buf, 1); } else if (fsrv->cmplog_binary) { - + setenv("__AFL_TARGET_PID2", pid_buf, 1); - + } else { - + setenv("__AFL_TARGET_PID1", pid_buf, 1); - + } /* Close the unneeded endpoints. */ diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 207b3046..badc2239 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -472,16 +472,20 @@ abort_calibration: afl->stage_max = old_sm; /* if taint mode was selected, run the taint */ - + if (afl->fsrv.taint_mode) { + write_to_testcase(afl, use_mem, q->len); - if (afl_fsrv_run_target(&afl->taint_fsrv, use_tmout, &afl->stop_soon) == 0) { + if (afl_fsrv_run_target(&afl->taint_fsrv, use_tmout, &afl->stop_soon) == + 0) { + u32 len = q->len / 8; if (q->len % 8) len++; u32 bits = count_bits_len(afl, afl->taint_fsrv.trace_bits, len); if (afl->debug) fprintf(stderr, "Debug: tainted bytes: %u\n", bits); } + } if (!first_run) { show_stats(afl); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index bc780b55..684b123e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -92,7 +92,8 @@ static void usage(u8 *argv0, int more_help) { " -o dir - output directory for fuzzer findings\n\n" "Execution control settings:\n" - " -A - use first level taint analysis (see qemu_taint/README.md)\n" + " -A - use first level taint analysis (see " + "qemu_taint/README.md)\n" " -p schedule - power schedules compute a seed's performance score. " "fsrv.taint_mode) { ACTF("Spawning qemu_taint forkserver"); @@ -1256,11 +1257,21 @@ int main(int argc, char **argv_orig, char **envp) { afl->taint_fsrv.trace_bits = afl->fsrv.trace_bits; ck_free(afl->taint_fsrv.target_path); afl->taint_fsrv.target_path = ck_strdup(afl->fsrv.target_path); - afl->argv_taint = get_qemu_argv(argv[0], &afl->taint_fsrv.target_path, - argc - optind, argv + optind); - u32 len = strlen(afl->taint_fsrv.target_path); - strcpy(afl->taint_fsrv.target_path + len - 5, "taint"); - strcpy((afl->argv_taint[0]) + len - 5, "taint"); + afl->argv_taint = ck_alloc(sizeof(char *) * (argc + 4 - optind)); + afl->argv_taint[0] = find_binary_own_loc("afl-qemu-taint", argv[0]); + if (!afl->argv_taint[0]) + FATAL( + "Cannot find 'afl-qemu-taint', read qemu_taint/README.md on how to " + "build it."); + u32 idx = optind - 1, offset = 0; + do { + + idx++; + offset++; + afl->argv_taint[offset] = argv[idx]; + + } while (argv[idx] != NULL); + if (afl->fsrv.use_stdin) unsetenv("AFL_TAINT_INPUT"); else -- cgit 1.4.1 From a1129b67c22ff54e25d457efbe44b3ab11851b5b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 9 Aug 2020 12:15:36 +0200 Subject: changes --- include/afl-fuzz.h | 1 + src/afl-fuzz-bitmap.c | 23 +++++++++++++++++++++++ src/afl-fuzz-run.c | 9 +++++---- src/afl-fuzz.c | 6 ++---- 4 files changed, 31 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 37e2dc6c..5e4e5a19 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -896,6 +896,7 @@ void write_bitmap(afl_state_t *); u32 count_bits(afl_state_t *, u8 *); u32 count_bits_len(afl_state_t *, u8 *, u32); u32 count_bytes(afl_state_t *, u8 *); +u32 count_bytes_len(afl_state_t *, u8 *, u32); u32 count_non_255_bytes(afl_state_t *, u8 *); #ifdef WORD_SIZE_64 void simplify_trace(afl_state_t *, u64 *); diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 9cb1b83f..8aaa4ae1 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -235,6 +235,29 @@ u32 count_bytes(afl_state_t *afl, u8 *mem) { } +u32 count_bytes_len(afl_state_t *afl, u8 *mem, u32 len) { + + u32 *ptr = (u32 *)mem; + u32 i = (len >> 2); + u32 ret = 0; + + while (i--) { + + u32 v = *(ptr++); + + if (!v) { continue; } + if (v & 0x000000ff) { ++ret; } + if (v & 0x0000ff00) { ++ret; } + if (v & 0x00ff0000) { ++ret; } + if (v & 0xff000000) { ++ret; } + + } + + return ret; + +} + + /* Count the number of non-255 bytes set in the bitmap. Used strictly for the status screen, several calls per second or so. */ diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index badc2239..b325f788 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -479,10 +479,11 @@ abort_calibration: if (afl_fsrv_run_target(&afl->taint_fsrv, use_tmout, &afl->stop_soon) == 0) { - u32 len = q->len / 8; - if (q->len % 8) len++; - u32 bits = count_bits_len(afl, afl->taint_fsrv.trace_bits, len); - if (afl->debug) fprintf(stderr, "Debug: tainted bytes: %u\n", bits); + u32 len = q->len; + if (len % 4) + len = len + 4 - (q->len % 4); + u32 bytes = count_bytes_len(afl, afl->taint_fsrv.trace_bits, len); + if (afl->debug) fprintf(stderr, "Debug: tainted bytes: %u\n", bytes); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 684b123e..4a3d2e97 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -825,11 +825,9 @@ int main(int argc, char **argv_orig, char **envp) { } - if (afl->fsrv.taint_mode && afl->fsrv.map_size < (MAX_FILE / 8) + 1) { + if (afl->fsrv.taint_mode && afl->fsrv.map_size < MAX_FILE) { - afl->shm.map_size = (MAX_FILE / 8); - if (MAX_FILE % 8) afl->shm.map_size++; - afl->fsrv.map_size = afl->shm.map_size; + afl->fsrv.map_size = afl->shm.map_size = MAX_FILE; } -- cgit 1.4.1 From 32db31b5550b73cbb20abb5e862fb08f86681ace Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 9 Aug 2020 12:35:52 +0200 Subject: fixes --- include/afl-fuzz.h | 3 ++- src/afl-fuzz-bitmap.c | 1 - src/afl-fuzz-run.c | 6 +++--- src/afl-fuzz-state.c | 30 +++++++++++++++++++----------- src/afl-fuzz.c | 6 ++++-- 5 files changed, 28 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 5e4e5a19..328c8405 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -838,7 +838,8 @@ struct custom_mutator { }; -void afl_state_init(afl_state_t *, uint32_t map_size); +void afl_state_init_1(afl_state_t *, uint32_t map_size); +void afl_state_init_2(afl_state_t *, uint32_t map_size); void afl_state_deinit(afl_state_t *); /* Set stop_soon flag on all childs, kill all childs */ diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 8aaa4ae1..11a3f121 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -257,7 +257,6 @@ u32 count_bytes_len(afl_state_t *afl, u8 *mem, u32 len) { } - /* Count the number of non-255 bytes set in the bitmap. Used strictly for the status screen, several calls per second or so. */ diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index b325f788..89ae0424 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -480,10 +480,10 @@ abort_calibration: 0) { u32 len = q->len; - if (len % 4) - len = len + 4 - (q->len % 4); + if (len % 4) len = len + 4 - (q->len % 4); u32 bytes = count_bytes_len(afl, afl->taint_fsrv.trace_bits, len); - if (afl->debug) fprintf(stderr, "Debug: tainted bytes: %u\n", bytes); + if (afl->debug) + fprintf(stderr, "Debug: tainted %u out of %u bytes\n", bytes, q->len); } diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index e2d62bc6..aab785e1 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -75,7 +75,7 @@ static list_t afl_states = {.element_prealloc_count = 0}; /* Initializes an afl_state_t. */ -void afl_state_init(afl_state_t *afl, uint32_t map_size) { +void afl_state_init_1(afl_state_t *afl, uint32_t map_size) { /* thanks to this memset, growing vars like out_buf and out_size are NULL/0 by default. */ @@ -100,16 +100,6 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->cpu_aff = -1; /* Selected CPU core */ #endif /* HAVE_AFFINITY */ - afl->virgin_bits = ck_alloc(map_size); - afl->virgin_tmout = ck_alloc(map_size); - afl->virgin_crash = ck_alloc(map_size); - afl->var_bytes = ck_alloc(map_size); - afl->top_rated = ck_alloc(map_size * sizeof(void *)); - afl->clean_trace = ck_alloc(map_size); - afl->clean_trace_custom = ck_alloc(map_size); - afl->first_trace = ck_alloc(map_size); - afl->map_tmp_buf = ck_alloc(map_size); - afl->fsrv.use_stdin = 1; afl->fsrv.map_size = map_size; afl->fsrv.function_opt = (u8 *)afl; @@ -160,6 +150,24 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { } +void afl_state_init_2(afl_state_t *afl, uint32_t map_size) { + + afl->shm.map_size = map_size ? map_size : MAP_SIZE; + + afl->virgin_bits = ck_alloc(map_size); + afl->virgin_tmout = ck_alloc(map_size); + afl->virgin_crash = ck_alloc(map_size); + afl->var_bytes = ck_alloc(map_size); + afl->top_rated = ck_alloc(map_size * sizeof(void *)); + afl->clean_trace = ck_alloc(map_size); + afl->clean_trace_custom = ck_alloc(map_size); + afl->first_trace = ck_alloc(map_size); + afl->map_tmp_buf = ck_alloc(map_size); + + afl->fsrv.map_size = map_size; + +} + /*This sets up the environment variables for afl-fuzz into the afl_state * struct*/ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 4a3d2e97..93ab90e2 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -262,7 +262,7 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_DEBUG")) { debug = afl->debug = 1; } map_size = get_map_size(); - afl_state_init(afl, map_size); + afl_state_init_1(afl, map_size); afl->debug = debug; afl_fsrv_init(&afl->fsrv); @@ -827,10 +827,12 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->fsrv.taint_mode && afl->fsrv.map_size < MAX_FILE) { - afl->fsrv.map_size = afl->shm.map_size = MAX_FILE; + map_size = afl->fsrv.map_size = afl->shm.map_size = MAX_FILE; } + afl_state_init_2(afl, map_size); + if (!mem_limit_given && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260; OKF("afl++ is maintained by Marc \"van Hauser\" Heuse, Heiko \"hexcoder\" " -- cgit 1.4.1 From b60663c0318b8baf21b36b549d765ddd2eeeb54e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 9 Aug 2020 18:48:12 +0200 Subject: taint integration done --- include/afl-fuzz.h | 15 +++-- llvm_mode/afl-llvm-rt.o.c | 10 +++ src/afl-common.c | 2 +- src/afl-forkserver.c | 3 +- src/afl-fuzz-bitmap.c | 2 +- src/afl-fuzz-init.c | 28 ++++++++- src/afl-fuzz-queue.c | 151 ++++++++++++++++++++++++++++++++++++++++++++-- src/afl-fuzz-run.c | 83 ++++++++++++------------- src/afl-fuzz.c | 25 ++++++-- 9 files changed, 255 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 328c8405..19807880 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -133,8 +133,10 @@ extern s32 struct queue_entry { - u8 *fname; /* File name for the test case */ - u32 len; /* Input length */ + u8 * fname; /* File name for the test case */ + u8 * fname_taint; /* File name for taint data */ + u32 len; /* Input length */ + struct queue_entry *prev; /* previous queue entry, if any */ u8 cal_failed, /* Calibration failed? */ trim_done, /* Trimmed? */ @@ -148,7 +150,10 @@ struct queue_entry { is_ascii; /* Is the input just ascii text? */ u32 bitmap_size, /* Number of bits set in bitmap */ - fuzz_level; /* Number of fuzzing iterations */ + fuzz_level, /* Number of fuzzing iterations */ + taint_bytes_all, /* Number of tainted bytes */ + taint_bytes_new, /* Number of new tainted bytes */ + taint_bytes_highest; /* highest offset in input */ u64 exec_us, /* Execution time (us) */ handicap, /* Number of queue cycles behind */ @@ -885,7 +890,7 @@ void deinit_py(void *); void mark_as_det_done(afl_state_t *, struct queue_entry *); void mark_as_variable(afl_state_t *, struct queue_entry *); void mark_as_redundant(afl_state_t *, struct queue_entry *, u8); -void add_to_queue(afl_state_t *, u8 *, u32, u8); +void add_to_queue(afl_state_t *, u8 *, u8 *, u32, struct queue_entry *, u8); void destroy_queue(afl_state_t *); void update_bitmap_score(afl_state_t *, struct queue_entry *); void cull_queue(afl_state_t *); @@ -975,6 +980,8 @@ void check_if_tty(afl_state_t *); void setup_signal_handlers(void); void save_cmdline(afl_state_t *, u32, char **); void read_foreign_testcases(afl_state_t *, int); +void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, + u8 *mem, u32 len); /* CmpLog */ diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 8cc59cbb..5ffae39c 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -831,6 +831,16 @@ void __afl_manual_init(void) { static u8 init_done; + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) { + + init_done = 1; + if (getenv("AFL_DEBUG")) + fprintf(stderr, + "DEBUG: disabled instrumenation because of " + "AFL_DISABLE_LLVM_INSTRUMENTATION\n"); + + } + if (!init_done) { __afl_map_shm(); diff --git a/src/afl-common.c b/src/afl-common.c index c0202821..e01bde3c 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -232,7 +232,7 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { char **new_argv = ck_alloc(sizeof(char *) * (argc + 3)); - u8 * tmp, *cp = NULL, *rsl, *own_copy; + u8 * cp = NULL; memcpy(&new_argv[2], &argv[1], (int)(sizeof(char *)) * (argc - 1)); new_argv[argc - 1] = NULL; diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index eeb2f8c3..4dc5e438 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -933,7 +933,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { } else { - s32 fd = fsrv->out_fd; + s32 fd; if (fsrv->out_file) { @@ -952,6 +952,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { } else { + fd = fsrv->out_fd; lseek(fd, 0, SEEK_SET); } diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 11a3f121..d4ee36e1 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -648,7 +648,7 @@ u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { #endif /* ^!SIMPLE_FILES */ - add_to_queue(afl, queue_fn, len, 0); + add_to_queue(afl, queue_fn, mem, len, afl->queue_top, 0); if (hnb == 2) { diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 350a8599..350a3b4c 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -712,7 +712,7 @@ void read_testcases(afl_state_t *afl) { if (!access(dfn, F_OK)) { passed_det = 1; } - add_to_queue(afl, fn2, st.st_size, passed_det); + add_to_queue(afl, fn2, NULL, st.st_size, NULL, passed_det); } @@ -960,6 +960,9 @@ void perform_dry_run(afl_state_t *afl) { } + /* perform taint gathering on the input seed */ + perform_taint_run(afl, q, q->fname, use_mem, q->len); + q = q->next; } @@ -1438,6 +1441,10 @@ static void handle_existing_out_dir(afl_state_t *afl) { u8 *orig_q = alloc_printf("%s/queue", afl->out_dir); + u8 *fnt = alloc_printf("%s/taint", afl->out_dir); + mkdir(fnt, 0755); // ignore errors + ck_free(fnt); + afl->in_dir = alloc_printf("%s/_resume", afl->out_dir); rename(orig_q, afl->in_dir); /* Ignore errors */ @@ -1494,6 +1501,15 @@ static void handle_existing_out_dir(afl_state_t *afl) { if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; } ck_free(fn); + if (afl->fsrv.taint_mode) { + + fn = alloc_printf("%s/taint", afl->out_dir); + mkdir(fn, 0755); // ignore errors + if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; } + ck_free(fn); + + } + /* All right, let's do out_dir>/crashes/id:* and * out_dir>/hangs/id:*. */ @@ -1721,6 +1737,16 @@ void setup_dirs_fds(afl_state_t *afl) { if (mkdir(tmp, 0700)) { PFATAL("Unable to create '%s'", tmp); } ck_free(tmp); + /* Taint directory if taint_mode. */ + + if (afl->fsrv.taint_mode) { + + tmp = alloc_printf("%s/taint", afl->out_dir); + if (mkdir(tmp, 0700)) { PFATAL("Unable to create '%s'", tmp); } + ck_free(tmp); + + } + /* Top-level directory for queue metadata used for session resume and related tasks. */ diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index f35df914..36ec0896 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -103,6 +103,139 @@ void mark_as_redundant(afl_state_t *afl, struct queue_entry *q, u8 state) { } +void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, + u8 *mem, u32 len) { + + u8 * ptr, *fn = fname; + u32 bytes = 0, plen = len; + s32 fd = -1; + struct queue_entry *prev = q->prev; + + if (plen % 4) plen = plen + 4 - (len % 4); + + if ((ptr = strrchr(fname, '/')) != NULL) fn = ptr + 1; + q->fname_taint = alloc_printf("%s/taint/%s", afl->out_dir, fn); + + if (q->fname_taint) { + + afl->taint_fsrv.map_size = plen; // speed :) + write_to_testcase(afl, mem, len); + if (afl_fsrv_run_target(&afl->taint_fsrv, afl->fsrv.exec_tmout, + &afl->stop_soon) == 0) { + + bytes = count_bytes_len(afl, afl->taint_fsrv.trace_bits, plen); + if (afl->debug) + fprintf(stderr, "Debug: tainted %u out of %u bytes\n", bytes, len); + + if (bytes) { + + s32 i = len; + while (i > 0 && !afl->taint_fsrv.trace_bits[i - 1]) + i--; + q->taint_bytes_highest = i; + + } + + } + + if (((bytes * 100) / len) < 90) { + + // we only use the taint havoc mode if the entry has less than 90% of + // overall tainted bytes + q->taint_bytes_all = bytes; + + // save the bytes away + int w = open(q->fname_taint, O_CREAT | O_WRONLY, 0644); + if (w >= 0) { + + ck_write(w, afl->taint_fsrv.trace_bits, plen, q->fname_taint); + close(w); + + } else { + + FATAL("could not create %s", q->fname_taint); + bytes = 0; + + } + + if (bytes && prev && prev->taint_bytes_all) { + + // check if there are new bytes in the taint vs the previous + int r = open(prev->fname_taint, O_RDONLY); + + if (r >= 0) { + + u8 *bufr = mmap(0, prev->len, PROT_READ, MAP_PRIVATE, r, 0); + + if ((size_t)bufr != -1) { + + u32 i; + u8 *tmp = ck_maybe_grow(BUF_PARAMS(in_scratch), plen); + memset(tmp, 0, plen); + + for (i = 0; i < len; i++) + if (afl->taint_fsrv.trace_bits[i] && (i >= prev->len || !bufr[i])) + tmp[i] = 1; + + q->taint_bytes_new = count_bytes_len(afl, tmp, plen); + + if (q->taint_bytes_new) { + + u8 *fnw = alloc_printf("%s.new", q->fname_taint); + int w = open(fnw, O_CREAT | O_WRONLY, 0644); + if (w >= 0) { + + ck_write(w, tmp, plen, fnw); + close(w); + + } else { + + q->taint_bytes_new = 0; + + } + + ck_free(fnw); + + } + + munmap(bufr, prev->len); + + } + + close(r); + + } + + } + + } else { + + bytes = 0; + + } + + } + + if (!bytes) { + + q->taint_bytes_highest = q->taint_bytes_all = q->taint_bytes_new = 0; + + if (q->fname_taint) { + + ck_free(q->fname_taint); + q->fname_taint = NULL; + + } + + } else { + + if (q->taint_bytes_all && !q->taint_bytes_new) + q->taint_bytes_new = q->taint_bytes_all; + + } + +} + /* check if ascii or UTF-8 */ static u8 check_if_text(struct queue_entry *q) { @@ -212,10 +345,12 @@ static u8 check_if_text(struct queue_entry *q) { /* Append new test case to the queue. */ -void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { +void add_to_queue(afl_state_t *afl, u8 *fname, u8 *mem, u32 len, + struct queue_entry *prev_q, u8 passed_det) { struct queue_entry *q = ck_alloc(sizeof(struct queue_entry)); + q->prev = prev_q; q->fname = fname; q->len = len; q->depth = afl->cur_depth + 1; @@ -254,6 +389,17 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { afl->last_path_time = get_cur_time(); + /* trigger the tain gathering if this is not a dry run */ + if (afl->fsrv.taint_mode && mem) { + + perform_taint_run(afl, q, fname, mem, len); + + } + + /* only redqueen currently uses is_ascii */ + if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(q); + + /* run custom mutators afl_custom_queue_new_entry() */ if (afl->custom_mutators_count) { LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { @@ -273,9 +419,6 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { } - /* only redqueen currently uses is_ascii */ - if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(q); - } /* Destroy the entire queue. */ diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 89ae0424..ddbd5524 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -471,24 +471,6 @@ abort_calibration: afl->stage_cur = old_sc; afl->stage_max = old_sm; - /* if taint mode was selected, run the taint */ - - if (afl->fsrv.taint_mode) { - - write_to_testcase(afl, use_mem, q->len); - if (afl_fsrv_run_target(&afl->taint_fsrv, use_tmout, &afl->stop_soon) == - 0) { - - u32 len = q->len; - if (len % 4) len = len + 4 - (q->len % 4); - u32 bytes = count_bytes_len(afl, afl->taint_fsrv.trace_bits, len); - if (afl->debug) - fprintf(stderr, "Debug: tainted %u out of %u bytes\n", bytes, q->len); - - } - - } - if (!first_run) { show_stats(afl); } return fault; @@ -770,56 +752,65 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { while (remove_pos < q->len) { u32 trim_avail = MIN(remove_len, q->len - remove_pos); - u64 cksum; - write_with_gap(afl, in_buf, q->len, remove_pos, trim_avail); + if (likely((!q->taint_bytes_highest) || + (q->len - trim_avail > q->taint_bytes_highest))) { - fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); - ++afl->trim_execs; + u64 cksum; - if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; } + write_with_gap(afl, in_buf, q->len, remove_pos, trim_avail); - /* Note that we don't keep track of crashes or hangs here; maybe TODO? - */ + fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); + ++afl->trim_execs; - cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); + if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; } - /* If the deletion had no impact on the trace, make it permanent. This - isn't perfect for variable-path inputs, but we're just making a - best-effort pass, so it's not a big deal if we end up with false - negatives every now and then. */ + /* Note that we don't keep track of crashes or hangs here; maybe TODO? + */ - if (cksum == q->exec_cksum) { + cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); - u32 move_tail = q->len - remove_pos - trim_avail; + /* If the deletion had no impact on the trace, make it permanent. This + isn't perfect for variable-path inputs, but we're just making a + best-effort pass, so it's not a big deal if we end up with false + negatives every now and then. */ - q->len -= trim_avail; - len_p2 = next_pow2(q->len); + if (cksum == q->exec_cksum) { - memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail, - move_tail); + u32 move_tail = q->len - remove_pos - trim_avail; - /* Let's save a clean trace, which will be needed by - update_bitmap_score once we're done with the trimming stuff. */ + q->len -= trim_avail; + len_p2 = next_pow2(q->len); - if (!needs_write) { + memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail, + move_tail); - needs_write = 1; - memcpy(afl->clean_trace, afl->fsrv.trace_bits, afl->fsrv.map_size); + /* Let's save a clean trace, which will be needed by + update_bitmap_score once we're done with the trimming stuff. */ + + if (!needs_write) { + + needs_write = 1; + memcpy(afl->clean_trace, afl->fsrv.trace_bits, afl->fsrv.map_size); + + } + + } else { + + remove_pos += remove_len; } + /* Since this can be slow, update the screen every now and then. */ + if (!(trim_exec++ % afl->stats_update_freq)) { show_stats(afl); } + ++afl->stage_cur; + } else { remove_pos += remove_len; } - /* Since this can be slow, update the screen every now and then. */ - - if (!(trim_exec++ % afl->stats_update_freq)) { show_stats(afl); } - ++afl->stage_cur; - } remove_len >>= 1; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 93ab90e2..6f143db7 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -244,9 +244,10 @@ static int stricmp(char const *a, char const *b) { int main(int argc, char **argv_orig, char **envp) { - s32 opt; - u64 prev_queued = 0; - u32 sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE; + s32 opt; + u64 prev_queued = 0; + u32 sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE, + real_map_size = 0; u8 * extras_dir = 0; u8 mem_limit_given = 0, exit_1 = 0, debug = 0; char **use_argv; @@ -827,6 +828,7 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->fsrv.taint_mode && afl->fsrv.map_size < MAX_FILE) { + real_map_size = map_size; map_size = afl->fsrv.map_size = afl->shm.map_size = MAX_FILE; } @@ -840,8 +842,7 @@ int main(int argc, char **argv_orig, char **envp) { OKF("afl++ is open source, get it at " "https://github.com/AFLplusplus/AFLplusplus"); OKF("Power schedules from github.com/mboehme/aflfast"); - OKF("Python Mutator and llvm_mode instrument file list from " - "github.com/choller/afl"); + OKF("Python Mutator from github.com/choller/afl"); OKF("MOpt Mutator from github.com/puppet-meteor/MOpt-AFL"); if (afl->sync_id && afl->is_main_node && @@ -1088,6 +1089,13 @@ int main(int argc, char **argv_orig, char **envp) { memset(afl->virgin_tmout, 255, afl->fsrv.map_size); memset(afl->virgin_crash, 255, afl->fsrv.map_size); + if (map_size != real_map_size) { + + afl->fsrv.map_size = real_map_size; + if (afl->cmplog_binary) afl->cmplog_fsrv.map_size; + + } + init_count_class16(); if (afl->is_main_node && check_main_node_exists(afl) == 1) { @@ -1252,12 +1260,16 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->fsrv.taint_mode) { ACTF("Spawning qemu_taint forkserver"); + u8 *disable = getenv("AFL_DISABLE_LLVM_INSTRUMENTATION"); + setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); afl_fsrv_init_dup(&afl->taint_fsrv, &afl->fsrv); afl->taint_fsrv.qemu_mode = 2; + afl->taint_fsrv.taint_mode = 1; afl->taint_fsrv.trace_bits = afl->fsrv.trace_bits; ck_free(afl->taint_fsrv.target_path); - afl->taint_fsrv.target_path = ck_strdup(afl->fsrv.target_path); afl->argv_taint = ck_alloc(sizeof(char *) * (argc + 4 - optind)); + afl->taint_fsrv.target_path = + find_binary_own_loc("afl-qemu-taint", argv[0]); afl->argv_taint[0] = find_binary_own_loc("afl-qemu-taint", argv[0]); if (!afl->argv_taint[0]) FATAL( @@ -1278,6 +1290,7 @@ int main(int argc, char **argv_orig, char **envp) { setenv("AFL_TAINT_INPUT", afl->fsrv.out_file, 1); afl_fsrv_start(&afl->taint_fsrv, afl->argv_taint, &afl->stop_soon, afl->afl_env.afl_debug_child_output); + if (!disable) unsetenv("AFL_DISABLE_LLVM_INSTRUMENTATION"); OKF("Taint forkserver successfully started"); } -- cgit 1.4.1 From e99d7e973001adea65c68113b08792144d6aa5c8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 9 Aug 2020 20:24:56 +0200 Subject: integration in fuzz_one --- include/afl-fuzz.h | 10 +++-- src/afl-fuzz-one.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++------- src/afl-fuzz-run.c | 25 ++++++++++++ src/afl-fuzz.c | 12 ++++++ 4 files changed, 142 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 19807880..88392867 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -430,7 +430,9 @@ typedef struct afl_state { *in_bitmap, /* Input bitmap */ *file_extension, /* File extension */ *orig_cmdline, /* Original command line */ - *infoexec; /* Command to execute on a new crash */ + *infoexec, /* Command to execute on a new crash */ + *taint_input_file, /* fuzz_input_one input file */ + *taint_src, *taint_map; u32 hang_tmout; /* Timeout used for hang det (ms) */ @@ -441,7 +443,8 @@ typedef struct afl_state { custom_only, /* Custom mutator only mode */ python_only, /* Python-only mode */ is_main_node, /* if this is the main node */ - is_secondary_node; /* if this is a secondary instance */ + is_secondary_node, /* if this is a secondary instance */ + taint_needs_splode; /* explode fuzz input */ u32 stats_update_freq; /* Stats update frequency (execs) */ @@ -502,7 +505,8 @@ typedef struct afl_state { useless_at_start, /* Number of useless starting paths */ var_byte_count, /* Bitmap bytes with var behavior */ current_entry, /* Current queue entry ID */ - havoc_div; /* Cycle count divisor for havoc */ + havoc_div, /* Cycle count divisor for havoc */ + taint_len; u64 total_crashes, /* Total number of crashes */ unique_crashes, /* Crashes with unique signatures */ diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 74e09ee3..ec7c4772 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -458,27 +458,102 @@ u8 fuzz_one_original(afl_state_t *afl) { } - /* Map the test case into memory. */ + if (unlikely(afl->fsrv.taint_mode && (afl->queue_cycle % 3))) { - fd = open(afl->queue_cur->fname, O_RDONLY); + if (unlikely(afl->queue_cur->cal_failed)) goto abandon_entry; - if (unlikely(fd < 0)) { + u32 dst = 0, i; - PFATAL("Unable to open '%s'", afl->queue_cur->fname); + fd = open(afl->queue_cur->fname, O_RDONLY); + afl->taint_src = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (fd < 0 || (size_t)afl->taint_src == -1) + FATAL("unable to open '%s'", afl->queue_cur->fname); + close(fd); - } + switch (afl->queue_cycle % 3) { - len = afl->queue_cur->len; + case 0: // do nothing, but cannot happen -> else + break; - orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + case 1: // fuzz only tainted bytes + if (!afl->queue_cur->taint_bytes_all) goto abandon_entry; + afl->taint_needs_splode = 1; - if (unlikely(orig_in == MAP_FAILED)) { + fd = open(afl->taint_input_file, O_RDONLY); + len = afl->taint_len = afl->queue_cur->taint_bytes_all; + orig_in = in_buf = + mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (fd < 0 || (size_t)in_buf == -1) + FATAL("unable to open '%s'", afl->taint_input_file); + close(fd); - PFATAL("Unable to mmap '%s' with len %d", afl->queue_cur->fname, len); + fd = open(afl->queue_cur->fname_taint, O_RDWR); + afl->taint_map = mmap(0, afl->queue_cur->len, PROT_READ | PROT_WRITE, + MAP_PRIVATE, fd, 0); + if (fd < 0 || (size_t)in_buf == -1) + FATAL("unable to open '%s'", afl->queue_cur->fname_taint); + close(fd); - } + for (i = 0; i < afl->queue_cur->len && dst < len; i++) + if (afl->taint_map[i]) in_buf[dst++] = afl->taint_src[i]; - close(fd); + break; + + case 2: // fuzz only newly tainted bytes + if (!afl->queue_cur->taint_bytes_new) goto abandon_entry; + afl->taint_needs_splode = 1; + + fd = open(afl->taint_input_file, O_RDONLY); + len = afl->taint_len = afl->queue_cur->taint_bytes_new; + orig_in = in_buf = + mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (fd < 0 || (size_t)in_buf == -1) + FATAL("unable to open '%s'", afl->taint_input_file); + close(fd); + + u8 *fn = alloc_printf("%s.new", afl->queue_cur->fname_taint); + fd = open(fn, O_RDWR); + afl->taint_map = mmap(0, afl->queue_cur->len, PROT_READ | PROT_WRITE, + MAP_PRIVATE, fd, 0); + if (fd < 0 || (size_t)in_buf == -1) FATAL("unable to open '%s'", fn); + close(fd); + ck_free(fn); + + for (i = 0; i < afl->queue_cur->len && dst < len; i++) + if (afl->taint_map[i]) in_buf[dst++] = afl->taint_src[i]; + + break; + + } + + goto havoc_stage; // we let the normal cycles do deterministic mode - if + + } else { + + /* Map the test case into memory. */ + afl->taint_needs_splode = 0; + + fd = open(afl->queue_cur->fname, O_RDONLY); + + if (unlikely(fd < 0)) { + + PFATAL("Unable to open '%s'", afl->queue_cur->fname); + + } + + len = afl->queue_cur->len; + + orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + + if (unlikely(orig_in == MAP_FAILED)) { + + PFATAL("Unable to mmap '%s' with len %d", afl->queue_cur->fname, len); + + } + + close(fd); + + } /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every single byte anyway, so it wouldn't give us any performance or memory usage @@ -527,7 +602,7 @@ u8 fuzz_one_original(afl_state_t *afl) { ************/ if (!afl->non_instrumented_mode && !afl->queue_cur->trim_done && - !afl->disable_trim) { + !afl->disable_trim && !afl->taint_needs_splode) { u8 res = trim_case(afl, afl->queue_cur, in_buf); @@ -2568,7 +2643,17 @@ abandon_entry: ++afl->queue_cur->fuzz_level; - munmap(orig_in, afl->queue_cur->len); + if (afl->taint_needs_splode) { + + munmap(afl->taint_src, afl->queue_cur->len); + munmap(orig_in, afl->taint_len); + munmap(afl->taint_map, afl->queue_cur->len); + + } else { + + munmap(orig_in, afl->queue_cur->len); + + } return ret_val; diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index ddbd5524..31db4d7c 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -863,6 +863,8 @@ abort_trimming: } +#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size + /* Write a modified test case, run program, process results. Handle error conditions, returning 1 if it's time to bail out. This is a helper function for fuzz_one(). */ @@ -871,6 +873,27 @@ u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { u8 fault; + if (unlikely(afl->taint_needs_splode)) { + + s32 new_len = afl->queue_cur->len + len - afl->taint_len; + if (new_len < 4) new_len = 4; + u8 *new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len); + + u32 i, taint = 0; + for (i = 0; i < new_len; i++) { + + if (afl->taint_map[i] || i > afl->queue_cur->len) + new_buf[i] = out_buf[taint++]; + else + new_buf[i] = afl->taint_src[i]; + + } + + out_buf = new_buf; + len = new_len; + + } + write_to_testcase(afl, out_buf, len); fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); @@ -918,3 +941,5 @@ u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { } +#undef BUF_PARAMS + diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 6f143db7..70a99dec 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1260,12 +1260,15 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->fsrv.taint_mode) { ACTF("Spawning qemu_taint forkserver"); + u8 *disable = getenv("AFL_DISABLE_LLVM_INSTRUMENTATION"); setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); + afl_fsrv_init_dup(&afl->taint_fsrv, &afl->fsrv); afl->taint_fsrv.qemu_mode = 2; afl->taint_fsrv.taint_mode = 1; afl->taint_fsrv.trace_bits = afl->fsrv.trace_bits; + ck_free(afl->taint_fsrv.target_path); afl->argv_taint = ck_alloc(sizeof(char *) * (argc + 4 - optind)); afl->taint_fsrv.target_path = @@ -1290,7 +1293,16 @@ int main(int argc, char **argv_orig, char **envp) { setenv("AFL_TAINT_INPUT", afl->fsrv.out_file, 1); afl_fsrv_start(&afl->taint_fsrv, afl->argv_taint, &afl->stop_soon, afl->afl_env.afl_debug_child_output); + + afl->taint_input_file = alloc_printf("%s/taint/.input", afl->out_dir); + int fd = open(afl->taint_input_file, O_CREAT | O_TRUNC | O_RDWR, 0644); + if (fd < 0) + FATAL("Cannot create taint inpu file '%s'", afl->taint_input_file); + lseek(fd, MAX_FILE, SEEK_SET); + ck_write(fd, "\0", 1, afl->taint_input_file); + if (!disable) unsetenv("AFL_DISABLE_LLVM_INSTRUMENTATION"); + OKF("Taint forkserver successfully started"); } -- cgit 1.4.1 From ff40359a608f3c14c1025908a2810ca71fd502af Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 9 Aug 2020 21:09:07 +0200 Subject: fixes --- src/afl-fuzz-init.c | 8 +++++++- src/afl-fuzz-one.c | 28 ++++++++++++++++------------ src/afl-fuzz-queue.c | 10 +++++----- 3 files changed, 28 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 350a3b4c..432e0649 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -961,7 +961,8 @@ void perform_dry_run(afl_state_t *afl) { } /* perform taint gathering on the input seed */ - perform_taint_run(afl, q, q->fname, use_mem, q->len); + if (afl->fsrv.taint_mode) + perform_taint_run(afl, q, q->fname, use_mem, q->len); q = q->next; @@ -1505,6 +1506,11 @@ static void handle_existing_out_dir(afl_state_t *afl) { fn = alloc_printf("%s/taint", afl->out_dir); mkdir(fn, 0755); // ignore errors + + u8 *fn2 = alloc_printf("%s/taint/.input", afl->out_dir); + unlink(fn2); // ignore errors + ck_free(fn2); + if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; } ck_free(fn); diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index ec7c4772..e75c2cec 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -458,26 +458,31 @@ u8 fuzz_one_original(afl_state_t *afl) { } - if (unlikely(afl->fsrv.taint_mode && (afl->queue_cycle % 3))) { + u32 tmp_val; + + if (unlikely(afl->fsrv.taint_mode && + (tmp_val = (afl->queue_cycle % 3)) != 1)) { if (unlikely(afl->queue_cur->cal_failed)) goto abandon_entry; + if (tmp_val == 1 && !afl->queue_cur->taint_bytes_all) goto abandon_entry; + if (tmp_val == 2 && !afl->queue_cur->taint_bytes_new) goto abandon_entry; u32 dst = 0, i; + temp_len = len = afl->queue_cur->len; fd = open(afl->queue_cur->fname, O_RDONLY); afl->taint_src = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (fd < 0 || (size_t)afl->taint_src == -1) FATAL("unable to open '%s'", afl->queue_cur->fname); close(fd); + afl->taint_needs_splode = 1; - switch (afl->queue_cycle % 3) { + switch (tmp_val) { - case 0: // do nothing, but cannot happen -> else + case 1: // do nothing, but cannot happen -> else break; - case 1: // fuzz only tainted bytes - if (!afl->queue_cur->taint_bytes_all) goto abandon_entry; - afl->taint_needs_splode = 1; + case 2: // fuzz only tainted bytes fd = open(afl->taint_input_file, O_RDONLY); len = afl->taint_len = afl->queue_cur->taint_bytes_all; @@ -499,9 +504,7 @@ u8 fuzz_one_original(afl_state_t *afl) { break; - case 2: // fuzz only newly tainted bytes - if (!afl->queue_cur->taint_bytes_new) goto abandon_entry; - afl->taint_needs_splode = 1; + case 0: // fuzz only newly tainted bytes fd = open(afl->taint_input_file, O_RDONLY); len = afl->taint_len = afl->queue_cur->taint_bytes_new; @@ -515,7 +518,8 @@ u8 fuzz_one_original(afl_state_t *afl) { fd = open(fn, O_RDWR); afl->taint_map = mmap(0, afl->queue_cur->len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (fd < 0 || (size_t)in_buf == -1) FATAL("unable to open '%s'", fn); + if (fd < 0 || (size_t)in_buf == -1) + FATAL("unable to open '%s' for %u bytes", fn, len); close(fd); ck_free(fn); @@ -526,8 +530,6 @@ u8 fuzz_one_original(afl_state_t *afl) { } - goto havoc_stage; // we let the normal cycles do deterministic mode - if - } else { /* Map the test case into memory. */ @@ -653,6 +655,8 @@ u8 fuzz_one_original(afl_state_t *afl) { if it has gone through deterministic testing in earlier, resumed runs (passed_det). */ + if (afl->taint_needs_splode) goto havoc_stage; + if (likely(afl->queue_cur->passed_det) || likely(afl->skip_deterministic) || likely(perf_score < (afl->queue_cur->depth * 30 <= afl->havoc_max_mult * 100 diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 36ec0896..3ada9d98 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -118,6 +118,9 @@ void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, if (q->fname_taint) { + u8 *save = ck_maybe_grow(BUF_PARAMS(out_scratch), afl->fsrv.map_size); + memcpy(save, afl->taint_fsrv.trace_bits, afl->fsrv.map_size); + afl->taint_fsrv.map_size = plen; // speed :) write_to_testcase(afl, mem, len); if (afl_fsrv_run_target(&afl->taint_fsrv, afl->fsrv.exec_tmout, @@ -214,6 +217,8 @@ void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, } + memcpy(afl->taint_fsrv.trace_bits, save, afl->fsrv.map_size); + } if (!bytes) { @@ -227,11 +232,6 @@ void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, } - } else { - - if (q->taint_bytes_all && !q->taint_bytes_new) - q->taint_bytes_new = q->taint_bytes_all; - } } -- cgit 1.4.1 From 4fc16b542e7839698f91dca46db52044fdaa9dd2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 9 Aug 2020 21:32:15 +0200 Subject: havoc copy --- src/afl-fuzz-one.c | 762 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 761 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index e75c2cec..5dc336dc 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -463,10 +463,14 @@ u8 fuzz_one_original(afl_state_t *afl) { if (unlikely(afl->fsrv.taint_mode && (tmp_val = (afl->queue_cycle % 3)) != 1)) { + ret_val = 0; + if (unlikely(afl->queue_cur->cal_failed)) goto abandon_entry; if (tmp_val == 1 && !afl->queue_cur->taint_bytes_all) goto abandon_entry; if (tmp_val == 2 && !afl->queue_cur->taint_bytes_new) goto abandon_entry; + ret_val = 1; + u32 dst = 0, i; temp_len = len = afl->queue_cur->len; @@ -655,7 +659,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if it has gone through deterministic testing in earlier, resumed runs (passed_det). */ - if (afl->taint_needs_splode) goto havoc_stage; + if (afl->taint_needs_splode) goto taint_havoc_stage; if (likely(afl->queue_cur->passed_det) || likely(afl->skip_deterministic) || likely(perf_score < @@ -2628,6 +2632,762 @@ retry_splicing: ret_val = 0; + goto abandon_entry; + + /******************************* + * same RANDOM HAVOC for taint * + *******************************/ + +taint_havoc_stage: + + afl->stage_cur_byte = -1; + + /* The havoc stage mutation code is also invoked when splicing files; if the + splice_cycle variable is set, generate different descriptions and such. */ + + if (!splice_cycle) { + + afl->stage_name = "havoc"; + afl->stage_short = "havoc"; + afl->stage_max = (doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) * + perf_score / afl->havoc_div / 100; + + } else { + + perf_score = orig_perf; + + snprintf(afl->stage_name_buf, STAGE_BUF_SIZE, "splice %u", splice_cycle); + afl->stage_name = afl->stage_name_buf; + afl->stage_short = "splice"; + afl->stage_max = SPLICE_HAVOC * perf_score / afl->havoc_div / 100; + + } + + if (afl->stage_max < HAVOC_MIN) { afl->stage_max = HAVOC_MIN; } + + temp_len = len; + + orig_hit_cnt = afl->queued_paths + afl->unique_crashes; + + havoc_queued = afl->queued_paths; + + if (afl->custom_mutators_count) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (el->stacked_custom && el->afl_custom_havoc_mutation_probability) { + + el->stacked_custom_prob = + el->afl_custom_havoc_mutation_probability(el->data); + if (el->stacked_custom_prob > 100) { + + FATAL( + "The probability returned by " + "afl_custom_havoc_mutation_propability " + "has to be in the range 0-100."); + + } + + } + + }); + + } + + /* We essentially just do several thousand runs (depending on perf_score) + where we take the input file and make random stacked tweaks. */ + + if (unlikely(afl->expand_havoc)) { + + /* add expensive havoc cases here, they are activated after a full + cycle without finds happened */ + + r_max = 16 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0); + + } else { + + r_max = 15 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0); + + } + + for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { + + u32 use_stacking = 1 << (1 + rand_below(afl, HAVOC_STACK_POW2)); + + afl->stage_cur_val = use_stacking; + + for (i = 0; i < use_stacking; ++i) { + + if (afl->custom_mutators_count) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (el->stacked_custom && + rand_below(afl, 100) < el->stacked_custom_prob) { + + u8 * custom_havoc_buf = NULL; + size_t new_len = el->afl_custom_havoc_mutation( + el->data, out_buf, temp_len, &custom_havoc_buf, MAX_FILE); + if (unlikely(!custom_havoc_buf)) { + + FATAL("Error in custom_havoc (return %zd)", new_len); + + } + + if (likely(new_len > 0 && custom_havoc_buf)) { + + temp_len = new_len; + if (out_buf != custom_havoc_buf) { + + ck_maybe_grow(BUF_PARAMS(out), temp_len); + memcpy(out_buf, custom_havoc_buf, temp_len); + + } + + } + + } + + }); + + } + + switch ((r = rand_below(afl, r_max))) { + + case 0: + + /* Flip a single bit somewhere. Spooky! */ + + FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); + break; + + case 1: + + /* Set byte to interesting value. */ + + out_buf[rand_below(afl, temp_len)] = + interesting_8[rand_below(afl, sizeof(interesting_8))]; + break; + + case 2: + + /* Set word to interesting value, randomly choosing endian. */ + + if (temp_len < 2) { break; } + + if (rand_below(afl, 2)) { + + *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = + interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]; + + } else { + + *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = SWAP16( + interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]); + + } + + break; + + case 3: + + /* Set dword to interesting value, randomly choosing endian. */ + + if (temp_len < 4) { break; } + + if (rand_below(afl, 2)) { + + *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = + interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]; + + } else { + + *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = SWAP32( + interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]); + + } + + break; + + case 4: + + /* Randomly subtract from byte. */ + + out_buf[rand_below(afl, temp_len)] -= 1 + rand_below(afl, ARITH_MAX); + break; + + case 5: + + /* Randomly add to byte. */ + + out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); + break; + + case 6: + + /* Randomly subtract from word, random endian. */ + + if (temp_len < 2) { break; } + + if (rand_below(afl, 2)) { + + u32 pos = rand_below(afl, temp_len - 1); + + *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); + + } else { + + u32 pos = rand_below(afl, temp_len - 1); + u16 num = 1 + rand_below(afl, ARITH_MAX); + + *(u16 *)(out_buf + pos) = + SWAP16(SWAP16(*(u16 *)(out_buf + pos)) - num); + + } + + break; + + case 7: + + /* Randomly add to word, random endian. */ + + if (temp_len < 2) { break; } + + if (rand_below(afl, 2)) { + + u32 pos = rand_below(afl, temp_len - 1); + + *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); + + } else { + + u32 pos = rand_below(afl, temp_len - 1); + u16 num = 1 + rand_below(afl, ARITH_MAX); + + *(u16 *)(out_buf + pos) = + SWAP16(SWAP16(*(u16 *)(out_buf + pos)) + num); + + } + + break; + + case 8: + + /* Randomly subtract from dword, random endian. */ + + if (temp_len < 4) { break; } + + if (rand_below(afl, 2)) { + + u32 pos = rand_below(afl, temp_len - 3); + + *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); + + } else { + + u32 pos = rand_below(afl, temp_len - 3); + u32 num = 1 + rand_below(afl, ARITH_MAX); + + *(u32 *)(out_buf + pos) = + SWAP32(SWAP32(*(u32 *)(out_buf + pos)) - num); + + } + + break; + + case 9: + + /* Randomly add to dword, random endian. */ + + if (temp_len < 4) { break; } + + if (rand_below(afl, 2)) { + + u32 pos = rand_below(afl, temp_len - 3); + + *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); + + } else { + + u32 pos = rand_below(afl, temp_len - 3); + u32 num = 1 + rand_below(afl, ARITH_MAX); + + *(u32 *)(out_buf + pos) = + SWAP32(SWAP32(*(u32 *)(out_buf + pos)) + num); + + } + + break; + + case 10: + + /* Just set a random byte to a random value. Because, + why not. We use XOR with 1-255 to eliminate the + possibility of a no-op. */ + + out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); + break; + + case 11 ... 12: { + + /* Delete bytes. We're making this a bit more likely + than insertion (the next option) in hopes of keeping + files reasonably small. */ + + u32 del_from, del_len; + + if (temp_len < 2) { break; } + + /* Don't delete too much. */ + + del_len = choose_block_len(afl, temp_len - 1); + + del_from = rand_below(afl, temp_len - del_len + 1); + + memmove(out_buf + del_from, out_buf + del_from + del_len, + temp_len - del_from - del_len); + + temp_len -= del_len; + + break; + + } + + case 13: + + if (temp_len + HAVOC_BLK_XL < MAX_FILE) { + + /* Clone bytes (75%) or insert a block of constant bytes (25%). */ + + u8 actually_clone = rand_below(afl, 4); + u32 clone_from, clone_to, clone_len; + u8 *new_buf; + + if (actually_clone) { + + clone_len = choose_block_len(afl, temp_len); + clone_from = rand_below(afl, temp_len - clone_len + 1); + + } else { + + clone_len = choose_block_len(afl, HAVOC_BLK_XL); + clone_from = 0; + + } + + clone_to = rand_below(afl, temp_len); + + new_buf = + ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); + + /* Head */ + + memcpy(new_buf, out_buf, clone_to); + + /* Inserted part */ + + if (actually_clone) { + + memcpy(new_buf + clone_to, out_buf + clone_from, clone_len); + + } else { + + memset(new_buf + clone_to, + rand_below(afl, 2) ? rand_below(afl, 256) + : out_buf[rand_below(afl, temp_len)], + clone_len); + + } + + /* Tail */ + memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, + temp_len - clone_to); + + swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); + out_buf = new_buf; + new_buf = NULL; + temp_len += clone_len; + + } + + break; + + case 14: { + + /* Overwrite bytes with a randomly selected chunk (75%) or fixed + bytes (25%). */ + + u32 copy_from, copy_to, copy_len; + + if (temp_len < 2) { break; } + + copy_len = choose_block_len(afl, temp_len - 1); + + copy_from = rand_below(afl, temp_len - copy_len + 1); + copy_to = rand_below(afl, temp_len - copy_len + 1); + + if (rand_below(afl, 4)) { + + if (copy_from != copy_to) { + + memmove(out_buf + copy_to, out_buf + copy_from, copy_len); + + } + + } else { + + memset(out_buf + copy_to, + rand_below(afl, 2) ? rand_below(afl, 256) + : out_buf[rand_below(afl, temp_len)], + copy_len); + + } + + break; + + } + + default: + + if (likely(r <= 16 && (afl->extras_cnt || afl->a_extras_cnt))) { + + /* Values 15 and 16 can be selected only if there are any extras + present in the dictionaries. */ + + if (r == 15) { + + /* Overwrite bytes with an extra. */ + + if (!afl->extras_cnt || + (afl->a_extras_cnt && rand_below(afl, 2))) { + + /* No user-specified extras or odds in our favor. Let's use an + auto-detected one. */ + + u32 use_extra = rand_below(afl, afl->a_extras_cnt); + u32 extra_len = afl->a_extras[use_extra].len; + u32 insert_at; + + if ((s32)extra_len > temp_len) { break; } + + insert_at = rand_below(afl, temp_len - extra_len + 1); + memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, + extra_len); + + } else { + + /* No auto extras or odds in our favor. Use the dictionary. */ + + u32 use_extra = rand_below(afl, afl->extras_cnt); + u32 extra_len = afl->extras[use_extra].len; + u32 insert_at; + + if ((s32)extra_len > temp_len) { break; } + + insert_at = rand_below(afl, temp_len - extra_len + 1); + memcpy(out_buf + insert_at, afl->extras[use_extra].data, + extra_len); + + } + + break; + + } else { // case 16 + + u32 use_extra, extra_len, + insert_at = rand_below(afl, temp_len + 1); + u8 *ptr; + + /* Insert an extra. Do the same dice-rolling stuff as for the + previous case. */ + + if (!afl->extras_cnt || + (afl->a_extras_cnt && rand_below(afl, 2))) { + + use_extra = rand_below(afl, afl->a_extras_cnt); + extra_len = afl->a_extras[use_extra].len; + ptr = afl->a_extras[use_extra].data; + + } else { + + use_extra = rand_below(afl, afl->extras_cnt); + extra_len = afl->extras[use_extra].len; + ptr = afl->extras[use_extra].data; + + } + + if (temp_len + extra_len >= MAX_FILE) { break; } + + out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); + + /* Tail */ + memmove(out_buf + insert_at + extra_len, out_buf + insert_at, + temp_len - insert_at); + + /* Inserted part */ + memcpy(out_buf + insert_at, ptr, extra_len); + + temp_len += extra_len; + + break; + + } + + } else { + + /* + switch (r) { + + case 15: // fall through + case 16: + case 17: {*/ + + /* Overwrite bytes with a randomly selected chunk from another + testcase or insert that chunk. */ + + if (afl->queued_paths < 4) break; + + /* Pick a random queue entry and seek to it. */ + + u32 tid; + do + tid = rand_below(afl, afl->queued_paths); + while (tid == afl->current_entry); + + struct queue_entry *target = afl->queue_buf[tid]; + + /* Make sure that the target has a reasonable length. */ + + while (target && (target->len < 2 || target == afl->queue_cur)) + target = target->next; + + if (!target) break; + + /* Read the testcase into a new buffer. */ + + fd = open(target->fname, O_RDONLY); + + if (unlikely(fd < 0)) { + + PFATAL("Unable to open '%s'", target->fname); + + } + + u32 new_len = target->len; + u8 *new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len); + + ck_read(fd, new_buf, new_len, target->fname); + + close(fd); + + u8 overwrite = 0; + if (temp_len >= 2 && rand_below(afl, 2)) + overwrite = 1; + else if (temp_len + HAVOC_BLK_XL >= MAX_FILE) { + + if (temp_len >= 2) + overwrite = 1; + else + break; + + } + + if (overwrite) { + + u32 copy_from, copy_to, copy_len; + + copy_len = choose_block_len(afl, new_len - 1); + if ((s32)copy_len > temp_len) copy_len = temp_len; + + copy_from = rand_below(afl, new_len - copy_len + 1); + copy_to = rand_below(afl, temp_len - copy_len + 1); + + memmove(out_buf + copy_to, new_buf + copy_from, copy_len); + + } else { + + u32 clone_from, clone_to, clone_len; + + clone_len = choose_block_len(afl, new_len); + clone_from = rand_below(afl, new_len - clone_len + 1); + + clone_to = rand_below(afl, temp_len); + + u8 *temp_buf = + ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); + + /* Head */ + + memcpy(temp_buf, out_buf, clone_to); + + /* Inserted part */ + + memcpy(temp_buf + clone_to, new_buf + clone_from, clone_len); + + /* Tail */ + memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to, + temp_len - clone_to); + + swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); + out_buf = temp_buf; + temp_len += clone_len; + + } + + break; + + } + + // end of default: + + } + + } + + if (common_fuzz_stuff(afl, out_buf, temp_len)) { goto abandon_entry; } + + /* out_buf might have been mangled a bit, so let's restore it to its + original size and shape. */ + + out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + temp_len = len; + memcpy(out_buf, in_buf, len); + + /* If we're finding new stuff, let's run for a bit longer, limits + permitting. */ + + if (afl->queued_paths != havoc_queued) { + + if (perf_score <= afl->havoc_max_mult * 100) { + + afl->stage_max *= 2; + perf_score *= 2; + + } + + havoc_queued = afl->queued_paths; + + } + + } + + new_hit_cnt = afl->queued_paths + afl->unique_crashes; + + if (!splice_cycle) { + + afl->stage_finds[STAGE_HAVOC] += new_hit_cnt - orig_hit_cnt; + afl->stage_cycles[STAGE_HAVOC] += afl->stage_max; + + } else { + + afl->stage_finds[STAGE_SPLICE] += new_hit_cnt - orig_hit_cnt; + afl->stage_cycles[STAGE_SPLICE] += afl->stage_max; + + } + +#ifndef IGNORE_FINDS + + /************ + * SPLICING * + ************/ + + /* This is a last-resort strategy triggered by a full round with no findings. + It takes the current input file, randomly selects another input, and + splices them together at some offset, then relies on the havoc + code to mutate that blob. */ + +taint_retry_splicing: + + if (afl->use_splicing && splice_cycle++ < SPLICE_CYCLES && + afl->queued_paths > 1 && afl->queue_cur->len > 1) { + + struct queue_entry *target; + u32 tid, split_at; + u8 * new_buf; + s32 f_diff, l_diff; + + /* First of all, if we've modified in_buf for havoc, let's clean that + up... */ + + if (in_buf != orig_in) { + + in_buf = orig_in; + len = afl->queue_cur->len; + + } + + /* Pick a random queue entry and seek to it. Don't splice with yourself. */ + + do { + + tid = rand_below(afl, afl->queued_paths); + + } while (tid == afl->current_entry); + + afl->splicing_with = tid; + target = afl->queue_buf[tid]; + + /* Make sure that the target has a reasonable length. */ + + while (target && (target->len < 2 || target == afl->queue_cur)) { + + target = target->next; + ++afl->splicing_with; + + } + + if (!target) { goto taint_retry_splicing; } + + /* Read the testcase into a new buffer. */ + + fd = open(target->fname, O_RDONLY); + + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } + + new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), target->len); + + ck_read(fd, new_buf, target->len, target->fname); + + close(fd); + + /* Find a suitable splicing location, somewhere between the first and + the last differing byte. Bail out if the difference is just a single + byte or so. */ + + locate_diffs(in_buf, new_buf, MIN(len, (s64)target->len), &f_diff, &l_diff); + + if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { goto taint_retry_splicing; } + + /* Split somewhere between the first and last differing byte. */ + + split_at = f_diff + rand_below(afl, l_diff - f_diff); + + /* Do the thing. */ + + len = target->len; + memcpy(new_buf, in_buf, split_at); + swap_bufs(BUF_PARAMS(in), BUF_PARAMS(in_scratch)); + in_buf = new_buf; + + out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + memcpy(out_buf, in_buf, len); + + goto custom_mutator_stage; + /* ???: While integrating Python module, the author decided to jump to + python stage, but the reason behind this is not clear.*/ + // goto havoc_stage; + + } + +#endif /* !IGNORE_FINDS */ + + + + ret_val = 0; + + goto abandon_entry; + + /* we are through with this queue entry - for this iteration */ abandon_entry: -- cgit 1.4.1 From 558a82891abb7617eb51d49991a128fccdc9be6e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 9 Aug 2020 22:02:44 +0200 Subject: finalize havoc --- src/afl-fuzz-one.c | 807 +++-------------------------------------------------- 1 file changed, 44 insertions(+), 763 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 5dc336dc..75381db8 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -489,7 +489,7 @@ u8 fuzz_one_original(afl_state_t *afl) { case 2: // fuzz only tainted bytes fd = open(afl->taint_input_file, O_RDONLY); - len = afl->taint_len = afl->queue_cur->taint_bytes_all; + temp_len = len = afl->taint_len = afl->queue_cur->taint_bytes_all; orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (fd < 0 || (size_t)in_buf == -1) @@ -511,7 +511,7 @@ u8 fuzz_one_original(afl_state_t *afl) { case 0: // fuzz only newly tainted bytes fd = open(afl->taint_input_file, O_RDONLY); - len = afl->taint_len = afl->queue_cur->taint_bytes_new; + temp_len = len = afl->taint_len = afl->queue_cur->taint_bytes_new; orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (fd < 0 || (size_t)in_buf == -1) @@ -659,7 +659,8 @@ u8 fuzz_one_original(afl_state_t *afl) { if it has gone through deterministic testing in earlier, resumed runs (passed_det). */ - if (afl->taint_needs_splode) goto taint_havoc_stage; + // custom mutators would not work, deterministic is done -> so havoc! + if (afl->taint_needs_splode) goto havoc_stage; if (likely(afl->queue_cur->passed_det) || likely(afl->skip_deterministic) || likely(perf_score < @@ -2214,760 +2215,19 @@ havoc_stage: if (actually_clone) { - clone_len = choose_block_len(afl, temp_len); - clone_from = rand_below(afl, temp_len - clone_len + 1); + if (unlikely(afl->taint_needs_splode)) { - } else { - - clone_len = choose_block_len(afl, HAVOC_BLK_XL); - clone_from = 0; - - } - - clone_to = rand_below(afl, temp_len); - - new_buf = - ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); - - /* Head */ - - memcpy(new_buf, out_buf, clone_to); - - /* Inserted part */ - - if (actually_clone) { - - memcpy(new_buf + clone_to, out_buf + clone_from, clone_len); - - } else { - - memset(new_buf + clone_to, - rand_below(afl, 2) ? rand_below(afl, 256) - : out_buf[rand_below(afl, temp_len)], - clone_len); - - } - - /* Tail */ - memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, - temp_len - clone_to); - - swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); - out_buf = new_buf; - new_buf = NULL; - temp_len += clone_len; - - } - - break; - - case 14: { - - /* Overwrite bytes with a randomly selected chunk (75%) or fixed - bytes (25%). */ - - u32 copy_from, copy_to, copy_len; - - if (temp_len < 2) { break; } - - copy_len = choose_block_len(afl, temp_len - 1); - - copy_from = rand_below(afl, temp_len - copy_len + 1); - copy_to = rand_below(afl, temp_len - copy_len + 1); - - if (rand_below(afl, 4)) { - - if (copy_from != copy_to) { - - memmove(out_buf + copy_to, out_buf + copy_from, copy_len); - - } - - } else { - - memset(out_buf + copy_to, - rand_below(afl, 2) ? rand_below(afl, 256) - : out_buf[rand_below(afl, temp_len)], - copy_len); - - } - - break; - - } - - default: - - if (likely(r <= 16 && (afl->extras_cnt || afl->a_extras_cnt))) { - - /* Values 15 and 16 can be selected only if there are any extras - present in the dictionaries. */ - - if (r == 15) { - - /* Overwrite bytes with an extra. */ - - if (!afl->extras_cnt || - (afl->a_extras_cnt && rand_below(afl, 2))) { - - /* No user-specified extras or odds in our favor. Let's use an - auto-detected one. */ - - u32 use_extra = rand_below(afl, afl->a_extras_cnt); - u32 extra_len = afl->a_extras[use_extra].len; - u32 insert_at; - - if ((s32)extra_len > temp_len) { break; } - - insert_at = rand_below(afl, temp_len - extra_len + 1); - memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, - extra_len); - - } else { - - /* No auto extras or odds in our favor. Use the dictionary. */ - - u32 use_extra = rand_below(afl, afl->extras_cnt); - u32 extra_len = afl->extras[use_extra].len; - u32 insert_at; - - if ((s32)extra_len > temp_len) { break; } - - insert_at = rand_below(afl, temp_len - extra_len + 1); - memcpy(out_buf + insert_at, afl->extras[use_extra].data, - extra_len); - - } - - break; - - } else { // case 16 - - u32 use_extra, extra_len, - insert_at = rand_below(afl, temp_len + 1); - u8 *ptr; - - /* Insert an extra. Do the same dice-rolling stuff as for the - previous case. */ - - if (!afl->extras_cnt || - (afl->a_extras_cnt && rand_below(afl, 2))) { - - use_extra = rand_below(afl, afl->a_extras_cnt); - extra_len = afl->a_extras[use_extra].len; - ptr = afl->a_extras[use_extra].data; - - } else { - - use_extra = rand_below(afl, afl->extras_cnt); - extra_len = afl->extras[use_extra].len; - ptr = afl->extras[use_extra].data; - - } - - if (temp_len + extra_len >= MAX_FILE) { break; } - - out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); - - /* Tail */ - memmove(out_buf + insert_at + extra_len, out_buf + insert_at, - temp_len - insert_at); - - /* Inserted part */ - memcpy(out_buf + insert_at, ptr, extra_len); - - temp_len += extra_len; - - break; - - } - - } else { - - /* - switch (r) { - - case 15: // fall through - case 16: - case 17: {*/ - - /* Overwrite bytes with a randomly selected chunk from another - testcase or insert that chunk. */ - - if (afl->queued_paths < 4) break; - - /* Pick a random queue entry and seek to it. */ - - u32 tid; - do - tid = rand_below(afl, afl->queued_paths); - while (tid == afl->current_entry); - - struct queue_entry *target = afl->queue_buf[tid]; - - /* Make sure that the target has a reasonable length. */ - - while (target && (target->len < 2 || target == afl->queue_cur)) - target = target->next; - - if (!target) break; - - /* Read the testcase into a new buffer. */ - - fd = open(target->fname, O_RDONLY); - - if (unlikely(fd < 0)) { - - PFATAL("Unable to open '%s'", target->fname); - - } - - u32 new_len = target->len; - u8 *new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len); - - ck_read(fd, new_buf, new_len, target->fname); - - close(fd); - - u8 overwrite = 0; - if (temp_len >= 2 && rand_below(afl, 2)) - overwrite = 1; - else if (temp_len + HAVOC_BLK_XL >= MAX_FILE) { - - if (temp_len >= 2) - overwrite = 1; - else - break; - - } - - if (overwrite) { - - u32 copy_from, copy_to, copy_len; - - copy_len = choose_block_len(afl, new_len - 1); - if ((s32)copy_len > temp_len) copy_len = temp_len; - - copy_from = rand_below(afl, new_len - copy_len + 1); - copy_to = rand_below(afl, temp_len - copy_len + 1); - - memmove(out_buf + copy_to, new_buf + copy_from, copy_len); - - } else { - - u32 clone_from, clone_to, clone_len; - - clone_len = choose_block_len(afl, new_len); - clone_from = rand_below(afl, new_len - clone_len + 1); - - clone_to = rand_below(afl, temp_len); - - u8 *temp_buf = - ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); - - /* Head */ - - memcpy(temp_buf, out_buf, clone_to); - - /* Inserted part */ - - memcpy(temp_buf + clone_to, new_buf + clone_from, clone_len); - - /* Tail */ - memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to, - temp_len - clone_to); - - swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); - out_buf = temp_buf; - temp_len += clone_len; - - } - - break; - - } - - // end of default: - - } - - } - - if (common_fuzz_stuff(afl, out_buf, temp_len)) { goto abandon_entry; } - - /* out_buf might have been mangled a bit, so let's restore it to its - original size and shape. */ - - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); - temp_len = len; - memcpy(out_buf, in_buf, len); - - /* If we're finding new stuff, let's run for a bit longer, limits - permitting. */ - - if (afl->queued_paths != havoc_queued) { - - if (perf_score <= afl->havoc_max_mult * 100) { - - afl->stage_max *= 2; - perf_score *= 2; - - } - - havoc_queued = afl->queued_paths; - - } - - } - - new_hit_cnt = afl->queued_paths + afl->unique_crashes; - - if (!splice_cycle) { - - afl->stage_finds[STAGE_HAVOC] += new_hit_cnt - orig_hit_cnt; - afl->stage_cycles[STAGE_HAVOC] += afl->stage_max; - - } else { - - afl->stage_finds[STAGE_SPLICE] += new_hit_cnt - orig_hit_cnt; - afl->stage_cycles[STAGE_SPLICE] += afl->stage_max; - - } - -#ifndef IGNORE_FINDS - - /************ - * SPLICING * - ************/ - - /* This is a last-resort strategy triggered by a full round with no findings. - It takes the current input file, randomly selects another input, and - splices them together at some offset, then relies on the havoc - code to mutate that blob. */ - -retry_splicing: - - if (afl->use_splicing && splice_cycle++ < SPLICE_CYCLES && - afl->queued_paths > 1 && afl->queue_cur->len > 1) { - - struct queue_entry *target; - u32 tid, split_at; - u8 * new_buf; - s32 f_diff, l_diff; - - /* First of all, if we've modified in_buf for havoc, let's clean that - up... */ - - if (in_buf != orig_in) { - - in_buf = orig_in; - len = afl->queue_cur->len; - - } - - /* Pick a random queue entry and seek to it. Don't splice with yourself. */ - - do { - - tid = rand_below(afl, afl->queued_paths); - - } while (tid == afl->current_entry); - - afl->splicing_with = tid; - target = afl->queue_buf[tid]; - - /* Make sure that the target has a reasonable length. */ - - while (target && (target->len < 2 || target == afl->queue_cur)) { - - target = target->next; - ++afl->splicing_with; - - } - - if (!target) { goto retry_splicing; } - - /* Read the testcase into a new buffer. */ - - fd = open(target->fname, O_RDONLY); - - if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } - - new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), target->len); - - ck_read(fd, new_buf, target->len, target->fname); - - close(fd); - - /* Find a suitable splicing location, somewhere between the first and - the last differing byte. Bail out if the difference is just a single - byte or so. */ - - locate_diffs(in_buf, new_buf, MIN(len, (s64)target->len), &f_diff, &l_diff); - - if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { goto retry_splicing; } - - /* Split somewhere between the first and last differing byte. */ - - split_at = f_diff + rand_below(afl, l_diff - f_diff); - - /* Do the thing. */ - - len = target->len; - memcpy(new_buf, in_buf, split_at); - swap_bufs(BUF_PARAMS(in), BUF_PARAMS(in_scratch)); - in_buf = new_buf; - - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); - memcpy(out_buf, in_buf, len); - - goto custom_mutator_stage; - /* ???: While integrating Python module, the author decided to jump to - python stage, but the reason behind this is not clear.*/ - // goto havoc_stage; - - } - -#endif /* !IGNORE_FINDS */ - - ret_val = 0; - - goto abandon_entry; - - /******************************* - * same RANDOM HAVOC for taint * - *******************************/ - -taint_havoc_stage: - - afl->stage_cur_byte = -1; - - /* The havoc stage mutation code is also invoked when splicing files; if the - splice_cycle variable is set, generate different descriptions and such. */ - - if (!splice_cycle) { - - afl->stage_name = "havoc"; - afl->stage_short = "havoc"; - afl->stage_max = (doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) * - perf_score / afl->havoc_div / 100; - - } else { - - perf_score = orig_perf; - - snprintf(afl->stage_name_buf, STAGE_BUF_SIZE, "splice %u", splice_cycle); - afl->stage_name = afl->stage_name_buf; - afl->stage_short = "splice"; - afl->stage_max = SPLICE_HAVOC * perf_score / afl->havoc_div / 100; - - } - - if (afl->stage_max < HAVOC_MIN) { afl->stage_max = HAVOC_MIN; } - - temp_len = len; - - orig_hit_cnt = afl->queued_paths + afl->unique_crashes; - - havoc_queued = afl->queued_paths; - - if (afl->custom_mutators_count) { - - LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { - - if (el->stacked_custom && el->afl_custom_havoc_mutation_probability) { - - el->stacked_custom_prob = - el->afl_custom_havoc_mutation_probability(el->data); - if (el->stacked_custom_prob > 100) { - - FATAL( - "The probability returned by " - "afl_custom_havoc_mutation_propability " - "has to be in the range 0-100."); - - } - - } - - }); - - } - - /* We essentially just do several thousand runs (depending on perf_score) - where we take the input file and make random stacked tweaks. */ - - if (unlikely(afl->expand_havoc)) { - - /* add expensive havoc cases here, they are activated after a full - cycle without finds happened */ - - r_max = 16 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0); - - } else { - - r_max = 15 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0); - - } - - for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { - - u32 use_stacking = 1 << (1 + rand_below(afl, HAVOC_STACK_POW2)); - - afl->stage_cur_val = use_stacking; - - for (i = 0; i < use_stacking; ++i) { - - if (afl->custom_mutators_count) { - - LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { - - if (el->stacked_custom && - rand_below(afl, 100) < el->stacked_custom_prob) { - - u8 * custom_havoc_buf = NULL; - size_t new_len = el->afl_custom_havoc_mutation( - el->data, out_buf, temp_len, &custom_havoc_buf, MAX_FILE); - if (unlikely(!custom_havoc_buf)) { - - FATAL("Error in custom_havoc (return %zd)", new_len); - - } - - if (likely(new_len > 0 && custom_havoc_buf)) { + clone_len = choose_block_len(afl, afl->queue_cur->len); + clone_from = + rand_below(afl, afl->queue_cur->len - clone_len + 1); - temp_len = new_len; - if (out_buf != custom_havoc_buf) { + } else { - ck_maybe_grow(BUF_PARAMS(out), temp_len); - memcpy(out_buf, custom_havoc_buf, temp_len); + clone_len = choose_block_len(afl, temp_len); + clone_from = rand_below(afl, temp_len - clone_len + 1); } - } - - } - - }); - - } - - switch ((r = rand_below(afl, r_max))) { - - case 0: - - /* Flip a single bit somewhere. Spooky! */ - - FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); - break; - - case 1: - - /* Set byte to interesting value. */ - - out_buf[rand_below(afl, temp_len)] = - interesting_8[rand_below(afl, sizeof(interesting_8))]; - break; - - case 2: - - /* Set word to interesting value, randomly choosing endian. */ - - if (temp_len < 2) { break; } - - if (rand_below(afl, 2)) { - - *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = - interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]; - - } else { - - *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = SWAP16( - interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]); - - } - - break; - - case 3: - - /* Set dword to interesting value, randomly choosing endian. */ - - if (temp_len < 4) { break; } - - if (rand_below(afl, 2)) { - - *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = - interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]; - - } else { - - *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = SWAP32( - interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]); - - } - - break; - - case 4: - - /* Randomly subtract from byte. */ - - out_buf[rand_below(afl, temp_len)] -= 1 + rand_below(afl, ARITH_MAX); - break; - - case 5: - - /* Randomly add to byte. */ - - out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); - break; - - case 6: - - /* Randomly subtract from word, random endian. */ - - if (temp_len < 2) { break; } - - if (rand_below(afl, 2)) { - - u32 pos = rand_below(afl, temp_len - 1); - - *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); - - } else { - - u32 pos = rand_below(afl, temp_len - 1); - u16 num = 1 + rand_below(afl, ARITH_MAX); - - *(u16 *)(out_buf + pos) = - SWAP16(SWAP16(*(u16 *)(out_buf + pos)) - num); - - } - - break; - - case 7: - - /* Randomly add to word, random endian. */ - - if (temp_len < 2) { break; } - - if (rand_below(afl, 2)) { - - u32 pos = rand_below(afl, temp_len - 1); - - *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); - - } else { - - u32 pos = rand_below(afl, temp_len - 1); - u16 num = 1 + rand_below(afl, ARITH_MAX); - - *(u16 *)(out_buf + pos) = - SWAP16(SWAP16(*(u16 *)(out_buf + pos)) + num); - - } - - break; - - case 8: - - /* Randomly subtract from dword, random endian. */ - - if (temp_len < 4) { break; } - - if (rand_below(afl, 2)) { - - u32 pos = rand_below(afl, temp_len - 3); - - *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); - - } else { - - u32 pos = rand_below(afl, temp_len - 3); - u32 num = 1 + rand_below(afl, ARITH_MAX); - - *(u32 *)(out_buf + pos) = - SWAP32(SWAP32(*(u32 *)(out_buf + pos)) - num); - - } - - break; - - case 9: - - /* Randomly add to dword, random endian. */ - - if (temp_len < 4) { break; } - - if (rand_below(afl, 2)) { - - u32 pos = rand_below(afl, temp_len - 3); - - *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); - - } else { - - u32 pos = rand_below(afl, temp_len - 3); - u32 num = 1 + rand_below(afl, ARITH_MAX); - - *(u32 *)(out_buf + pos) = - SWAP32(SWAP32(*(u32 *)(out_buf + pos)) + num); - - } - - break; - - case 10: - - /* Just set a random byte to a random value. Because, - why not. We use XOR with 1-255 to eliminate the - possibility of a no-op. */ - - out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); - break; - - case 11 ... 12: { - - /* Delete bytes. We're making this a bit more likely - than insertion (the next option) in hopes of keeping - files reasonably small. */ - - u32 del_from, del_len; - - if (temp_len < 2) { break; } - - /* Don't delete too much. */ - - del_len = choose_block_len(afl, temp_len - 1); - - del_from = rand_below(afl, temp_len - del_len + 1); - - memmove(out_buf + del_from, out_buf + del_from + del_len, - temp_len - del_from - del_len); - - temp_len -= del_len; - - break; - - } - - case 13: - - if (temp_len + HAVOC_BLK_XL < MAX_FILE) { - - /* Clone bytes (75%) or insert a block of constant bytes (25%). */ - - u8 actually_clone = rand_below(afl, 4); - u32 clone_from, clone_to, clone_len; - u8 *new_buf; - - if (actually_clone) { - - clone_len = choose_block_len(afl, temp_len); - clone_from = rand_below(afl, temp_len - clone_len + 1); - } else { clone_len = choose_block_len(afl, HAVOC_BLK_XL); @@ -2988,7 +2248,11 @@ taint_havoc_stage: if (actually_clone) { - memcpy(new_buf + clone_to, out_buf + clone_from, clone_len); + if (unlikely(afl->taint_needs_splode)) + memcpy(new_buf + clone_to, afl->taint_src + clone_from, + clone_len); + else + memcpy(new_buf + clone_to, out_buf + clone_from, clone_len); } else { @@ -3021,16 +2285,29 @@ taint_havoc_stage: if (temp_len < 2) { break; } - copy_len = choose_block_len(afl, temp_len - 1); + if (unlikely(afl->taint_needs_splode)) { + + copy_len = choose_block_len(afl, afl->queue_cur->len - 1); + copy_from = rand_below(afl, afl->queue_cur->len - copy_len + 1); + + } else { + + copy_len = choose_block_len(afl, temp_len - 1); + copy_from = rand_below(afl, temp_len - copy_len + 1); + + } - copy_from = rand_below(afl, temp_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); if (rand_below(afl, 4)) { if (copy_from != copy_to) { - memmove(out_buf + copy_to, out_buf + copy_from, copy_len); + if (unlikely(afl->taint_needs_splode)) + memmove(out_buf + copy_to, afl->taint_src + copy_from, + copy_len); + else + memmove(out_buf + copy_to, out_buf + copy_from, copy_len); } @@ -3296,10 +2573,17 @@ taint_havoc_stage: splices them together at some offset, then relies on the havoc code to mutate that blob. */ -taint_retry_splicing: + u32 saved_len; + + if (unlikely(afl->taint_needs_splode)) + saved_len = afl->taint_len; + else + saved_len = afl->queue_cur->len; + +retry_splicing: if (afl->use_splicing && splice_cycle++ < SPLICE_CYCLES && - afl->queued_paths > 1 && afl->queue_cur->len > 1) { + afl->queued_paths > 1 && saved_len > 1) { struct queue_entry *target; u32 tid, split_at; @@ -3312,7 +2596,7 @@ taint_retry_splicing: if (in_buf != orig_in) { in_buf = orig_in; - len = afl->queue_cur->len; + len = saved_len; } @@ -3336,7 +2620,7 @@ taint_retry_splicing: } - if (!target) { goto taint_retry_splicing; } + if (!target) { goto retry_splicing; } /* Read the testcase into a new buffer. */ @@ -3356,7 +2640,7 @@ taint_retry_splicing: locate_diffs(in_buf, new_buf, MIN(len, (s64)target->len), &f_diff, &l_diff); - if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { goto taint_retry_splicing; } + if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { goto retry_splicing; } /* Split somewhere between the first and last differing byte. */ @@ -3381,13 +2665,10 @@ taint_retry_splicing: #endif /* !IGNORE_FINDS */ - - ret_val = 0; goto abandon_entry; - /* we are through with this queue entry - for this iteration */ abandon_entry: -- cgit 1.4.1 From 9ec223c844a9fd9d8b66e309517fd7c64e5d4c5e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 9 Aug 2020 23:47:51 +0200 Subject: final touches for first testing --- src/afl-fuzz-one.c | 58 ++++++++++++++++++++++++++++++------------------------ src/afl-fuzz-run.c | 4 +++- 2 files changed, 35 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 75381db8..beb73246 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -458,16 +458,17 @@ u8 fuzz_one_original(afl_state_t *afl) { } - u32 tmp_val; + u32 tmp_val = 0; - if (unlikely(afl->fsrv.taint_mode && - (tmp_val = (afl->queue_cycle % 3)) != 1)) { + if (unlikely(afl->fsrv.taint_mode)) { + tmp_val = afl->queue_cycle % 2; ret_val = 0; if (unlikely(afl->queue_cur->cal_failed)) goto abandon_entry; + if (unlikely(!afl->queue_cur->passed_det) && !tmp_val) goto abandon_entry; if (tmp_val == 1 && !afl->queue_cur->taint_bytes_all) goto abandon_entry; - if (tmp_val == 2 && !afl->queue_cur->taint_bytes_new) goto abandon_entry; + if (tmp_val == 0 && !afl->queue_cur->taint_bytes_new) goto abandon_entry; ret_val = 1; @@ -483,10 +484,7 @@ u8 fuzz_one_original(afl_state_t *afl) { switch (tmp_val) { - case 1: // do nothing, but cannot happen -> else - break; - - case 2: // fuzz only tainted bytes + case 1: // fuzz only tainted bytes fd = open(afl->taint_input_file, O_RDONLY); temp_len = len = afl->taint_len = afl->queue_cur->taint_bytes_all; @@ -536,9 +534,10 @@ u8 fuzz_one_original(afl_state_t *afl) { } else { - /* Map the test case into memory. */ afl->taint_needs_splode = 0; + /* Map the test case into memory. */ + fd = open(afl->queue_cur->fname, O_RDONLY); if (unlikely(fd < 0)) { @@ -565,7 +564,7 @@ u8 fuzz_one_original(afl_state_t *afl) { single byte anyway, so it wouldn't give us any performance or memory usage benefits. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = ck_maybe_grow(BUF_PARAMS(out), len + 4096); afl->subseq_tmouts = 0; @@ -575,7 +574,8 @@ u8 fuzz_one_original(afl_state_t *afl) { * CALIBRATION (only if failed earlier on) * *******************************************/ - if (unlikely(afl->queue_cur->cal_failed)) { + if (unlikely(afl->queue_cur->cal_failed && + (!afl->taint_needs_splode || tmp_val == 1))) { u8 res = FSRV_RUN_TMOUT; @@ -659,9 +659,6 @@ u8 fuzz_one_original(afl_state_t *afl) { if it has gone through deterministic testing in earlier, resumed runs (passed_det). */ - // custom mutators would not work, deterministic is done -> so havoc! - if (afl->taint_needs_splode) goto havoc_stage; - if (likely(afl->queue_cur->passed_det) || likely(afl->skip_deterministic) || likely(perf_score < (afl->queue_cur->depth * 30 <= afl->havoc_max_mult * 100 @@ -1640,7 +1637,7 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - ex_tmp = ck_maybe_grow(BUF_PARAMS(ex), len + MAX_DICT_FILE); + ex_tmp = ck_maybe_grow(BUF_PARAMS(ex), len + MAX_DICT_FILE + 4096); for (i = 0; i <= (u32)len; ++i) { @@ -1814,7 +1811,7 @@ custom_mutator_stage: fd = open(target->fname, O_RDONLY); if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } - new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), target->len); + new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), target->len + 4096); ck_read(fd, new_buf, target->len, target->fname); close(fd); @@ -1989,7 +1986,7 @@ havoc_stage: temp_len = new_len; if (out_buf != custom_havoc_buf) { - ck_maybe_grow(BUF_PARAMS(out), temp_len); + ck_maybe_grow(BUF_PARAMS(out), temp_len + 4096); memcpy(out_buf, custom_havoc_buf, temp_len); } @@ -2237,8 +2234,8 @@ havoc_stage: clone_to = rand_below(afl, temp_len); - new_buf = - ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); + new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), + temp_len + clone_len + 4096); /* Head */ @@ -2303,10 +2300,18 @@ havoc_stage: if (copy_from != copy_to) { - if (unlikely(afl->taint_needs_splode)) + if (unlikely(afl->taint_needs_splode)) { + + if (copy_to > temp_len) copy_to = rand_below(afl, temp_len); + + // fprintf(stderr, "\nout_buf %p + copy_to %u, src %p + %u, + // copy_len %u -- len %u\n", out_buf , copy_to, afl->taint_src , + // copy_from, copy_len, afl->taint_len, afl->queue_cur->len); memmove(out_buf + copy_to, afl->taint_src + copy_from, copy_len); - else + + } else + memmove(out_buf + copy_to, out_buf + copy_from, copy_len); } @@ -2395,7 +2400,8 @@ havoc_stage: if (temp_len + extra_len >= MAX_FILE) { break; } - out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); + out_buf = + ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len + 4096); /* Tail */ memmove(out_buf + insert_at + extra_len, out_buf + insert_at, @@ -2490,8 +2496,8 @@ havoc_stage: clone_to = rand_below(afl, temp_len); - u8 *temp_buf = - ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); + u8 *temp_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), + temp_len + clone_len + 4096); /* Head */ @@ -2526,7 +2532,7 @@ havoc_stage: /* out_buf might have been mangled a bit, so let's restore it to its original size and shape. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = ck_maybe_grow(BUF_PARAMS(out), len + 4096); temp_len = len; memcpy(out_buf, in_buf, len); @@ -2653,7 +2659,7 @@ retry_splicing: swap_bufs(BUF_PARAMS(in), BUF_PARAMS(in_scratch)); in_buf = new_buf; - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = ck_maybe_grow(BUF_PARAMS(out), len + 4096); memcpy(out_buf, in_buf, len); goto custom_mutator_stage; diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 31db4d7c..41de143c 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -349,7 +349,9 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } - if (q->exec_cksum) { + if (unlikely(afl->fsrv.taint_mode)) + q->exec_cksum = 0; + else if (q->exec_cksum) { memcpy(afl->first_trace, afl->fsrv.trace_bits, afl->fsrv.map_size); hnb = has_new_bits(afl, afl->virgin_bits); -- cgit 1.4.1 From 8f8555dfdfeee643795ba04cd4240db40a88711e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 10 Aug 2020 12:05:30 +0200 Subject: fix segfault --- src/afl-fuzz-one.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index beb73246..c664f281 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -2295,6 +2295,7 @@ havoc_stage: } copy_to = rand_below(afl, temp_len - copy_len + 1); + if (unlikely(copy_to > temp_len)) copy_to = rand_below(afl, temp_len); if (rand_below(afl, 4)) { -- cgit 1.4.1 From 84b9d551fd288a836941975adc7ec0984ef54b0a Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 10 Aug 2020 12:11:49 +0200 Subject: disable expand havoc mopt for taint --- src/afl-fuzz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 70a99dec..2b9af94c 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1381,7 +1381,7 @@ int main(int argc, char **argv_orig, char **envp) { break; case 1: if (afl->limit_time_sig == 0 && !afl->custom_only && - !afl->python_only) { + !afl->python_only && !afl->fsrv.taint_mode) { afl->limit_time_sig = -1; afl->limit_time_puppet = 0; -- cgit 1.4.1 From f181a8307b9544a24e2c737e748e9ff34e8620e1 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 10 Aug 2020 12:48:15 +0200 Subject: put ! in .new map --- src/afl-fuzz-queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 3ada9d98..28af17f0 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -178,7 +178,7 @@ void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, for (i = 0; i < len; i++) if (afl->taint_fsrv.trace_bits[i] && (i >= prev->len || !bufr[i])) - tmp[i] = 1; + tmp[i] = '!'; q->taint_bytes_new = count_bytes_len(afl, tmp, plen); -- cgit 1.4.1 From 9c953ab51ff22b2fc3e1b73e6563211e7676b62e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 10 Aug 2020 13:03:59 +0200 Subject: memory grab at startup to prevent crashes --- src/afl-fuzz-one.c | 18 +++++++++--------- src/afl-fuzz.c | 11 +++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index c664f281..75687703 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -564,7 +564,7 @@ u8 fuzz_one_original(afl_state_t *afl) { single byte anyway, so it wouldn't give us any performance or memory usage benefits. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len + 4096); + out_buf = ck_maybe_grow(BUF_PARAMS(out), len); afl->subseq_tmouts = 0; @@ -1637,7 +1637,7 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - ex_tmp = ck_maybe_grow(BUF_PARAMS(ex), len + MAX_DICT_FILE + 4096); + ex_tmp = ck_maybe_grow(BUF_PARAMS(ex), len + MAX_DICT_FILE); for (i = 0; i <= (u32)len; ++i) { @@ -1811,7 +1811,7 @@ custom_mutator_stage: fd = open(target->fname, O_RDONLY); if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } - new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), target->len + 4096); + new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), target->len); ck_read(fd, new_buf, target->len, target->fname); close(fd); @@ -1986,7 +1986,7 @@ havoc_stage: temp_len = new_len; if (out_buf != custom_havoc_buf) { - ck_maybe_grow(BUF_PARAMS(out), temp_len + 4096); + ck_maybe_grow(BUF_PARAMS(out), temp_len); memcpy(out_buf, custom_havoc_buf, temp_len); } @@ -2235,7 +2235,7 @@ havoc_stage: clone_to = rand_below(afl, temp_len); new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), - temp_len + clone_len + 4096); + temp_len + clone_len); /* Head */ @@ -2402,7 +2402,7 @@ havoc_stage: if (temp_len + extra_len >= MAX_FILE) { break; } out_buf = - ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len + 4096); + ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); /* Tail */ memmove(out_buf + insert_at + extra_len, out_buf + insert_at, @@ -2498,7 +2498,7 @@ havoc_stage: clone_to = rand_below(afl, temp_len); u8 *temp_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), - temp_len + clone_len + 4096); + temp_len + clone_len); /* Head */ @@ -2533,7 +2533,7 @@ havoc_stage: /* out_buf might have been mangled a bit, so let's restore it to its original size and shape. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len + 4096); + out_buf = ck_maybe_grow(BUF_PARAMS(out), len); temp_len = len; memcpy(out_buf, in_buf, len); @@ -2660,7 +2660,7 @@ retry_splicing: swap_bufs(BUF_PARAMS(in), BUF_PARAMS(in_scratch)); in_buf = new_buf; - out_buf = ck_maybe_grow(BUF_PARAMS(out), len + 4096); + out_buf = ck_maybe_grow(BUF_PARAMS(out), len); memcpy(out_buf, in_buf, len); goto custom_mutator_stage; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 2b9af94c..5cdd0292 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1305,6 +1305,17 @@ int main(int argc, char **argv_orig, char **envp) { OKF("Taint forkserver successfully started"); +#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size + u8 *tmp1 = ck_maybe_grow(BUF_PARAMS(eff), MAX_FILE + 4096); + u8 *tmp2 = ck_maybe_grow(BUF_PARAMS(ex), MAX_FILE + 4096); + u8 *tmp3 = ck_maybe_grow(BUF_PARAMS(in_scratch), MAX_FILE + 4096); + u8 *tmp4 = ck_maybe_grow(BUF_PARAMS(out), MAX_FILE + 4096); + u8 *tmp5 = ck_maybe_grow(BUF_PARAMS(out_scratch), MAX_FILE + 4096); +#undef BUF_PARAMS + + if (!tmp1 || !tmp2 || !tmp3 || !tmp4 || !tmp5) + FATAL("memory issues. me hungry, feed me!"); + } perform_dry_run(afl); -- cgit 1.4.1 From 8428b18d2a48cf7e995797a8b2183920aaa14f7e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 10 Aug 2020 13:30:25 +0200 Subject: fix another segfault --- src/afl-fuzz-run.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 41de143c..7180d255 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -879,12 +879,13 @@ u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { s32 new_len = afl->queue_cur->len + len - afl->taint_len; if (new_len < 4) new_len = 4; + if (new_len > MAX_FILE) new_len = MAX_FILE; u8 *new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len); u32 i, taint = 0; for (i = 0; i < new_len; i++) { - if (afl->taint_map[i] || i > afl->queue_cur->len) + if (i > afl->taint_len || afl->taint_map[i] || i > afl->queue_cur->len) new_buf[i] = out_buf[taint++]; else new_buf[i] = afl->taint_src[i]; -- cgit 1.4.1 From 3ecafde29deac10bb41c6c9b7370f7cef951ef11 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 10 Aug 2020 13:59:30 +0200 Subject: increase stack size --- src/afl-common.c | 4 ++-- src/afl-fuzz-run.c | 2 +- src/afl-fuzz.c | 7 ++++++- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/afl-common.c b/src/afl-common.c index e01bde3c..dabeeedd 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -467,8 +467,8 @@ void read_bitmap(u8 *fname, u8 *map, size_t len) { u64 get_cur_time(void) { - struct timeval tv; - struct timezone tz; + static struct timeval tv; + static struct timezone tz; gettimeofday(&tv, &tz); diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 7180d255..0aef1c9e 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -885,7 +885,7 @@ u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { u32 i, taint = 0; for (i = 0; i < new_len; i++) { - if (i > afl->taint_len || afl->taint_map[i] || i > afl->queue_cur->len) + if (i >= afl->taint_len || i >= afl->queue_cur->len || afl->taint_map[i]) new_buf[i] = out_buf[taint++]; else new_buf[i] = afl->taint_src[i]; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 5cdd0292..783da6e0 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1305,6 +1305,12 @@ int main(int argc, char **argv_orig, char **envp) { OKF("Taint forkserver successfully started"); + const rlim_t kStackSize = 64L * 1024L * 1024L; // min stack size = 64 Mb + struct rlimit rl; + rl.rlim_cur = kStackSize; + if (getrlimit(RLIMIT_STACK, &rl) != 0) + WARNF("Setting a higher stack size failed!"); + #define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size u8 *tmp1 = ck_maybe_grow(BUF_PARAMS(eff), MAX_FILE + 4096); u8 *tmp2 = ck_maybe_grow(BUF_PARAMS(ex), MAX_FILE + 4096); @@ -1312,7 +1318,6 @@ int main(int argc, char **argv_orig, char **envp) { u8 *tmp4 = ck_maybe_grow(BUF_PARAMS(out), MAX_FILE + 4096); u8 *tmp5 = ck_maybe_grow(BUF_PARAMS(out_scratch), MAX_FILE + 4096); #undef BUF_PARAMS - if (!tmp1 || !tmp2 || !tmp3 || !tmp4 || !tmp5) FATAL("memory issues. me hungry, feed me!"); -- cgit 1.4.1 From a422fcaa40a3c7cd577b693060d9bc2e6c36cf73 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 10 Aug 2020 19:04:51 +0200 Subject: fixed minor inconsistencies, reenabled warnings --- GNUmakefile | 3 ++- include/debug.h | 9 +++++++-- src/afl-fuzz-cmplog.c | 4 ---- src/afl-fuzz-run.c | 2 +- src/afl-fuzz.c | 3 ++- 5 files changed, 12 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index 86ae24b6..fe5f8c03 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -98,7 +98,7 @@ ifneq "$(shell uname -m)" "x86_64" endif CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT) -override CFLAGS += -g -Wno-pointer-sign \ +override CFLAGS += -g -Wno-pointer-sign -Wno-variadic-macros -Wall -Wextra -Wpointer-arith \ -I include/ -DAFL_PATH=\"$(HELPER_PATH)\" \ -DBIN_PATH=\"$(BIN_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\" @@ -198,6 +198,7 @@ else endif ifneq "$(filter Linux GNU%,$(shell uname))" "" + override CFLAGS += -D_FORTIFY_SOURCE=2 LDFLAGS += -ldl -lrt endif diff --git a/include/debug.h b/include/debug.h index ae2946f0..cb0a63e9 100644 --- a/include/debug.h +++ b/include/debug.h @@ -29,10 +29,15 @@ #include "config.h" /* __FUNCTION__ is non-iso */ -#ifdef __func__ - #define __FUNCTION__ __func__ +#ifndef __FUNCTION__ + #ifdef __func__ + #define __FUNCTION__ __func__ + #else + #define __FUNCTION__ "unknown_func" + #endif #endif + /******************* * Terminal colors * *******************/ diff --git a/src/afl-fuzz-cmplog.c b/src/afl-fuzz-cmplog.c index faf4dcb7..8ffc6e1b 100644 --- a/src/afl-fuzz-cmplog.c +++ b/src/afl-fuzz-cmplog.c @@ -29,10 +29,6 @@ #include "afl-fuzz.h" #include "cmplog.h" -typedef struct cmplog_data { - -} cmplog_data_t; - void cmplog_exec_child(afl_forkserver_t *fsrv, char **argv) { setenv("___AFL_EINS_ZWEI_POLIZEI___", "1", 1); diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 8d652155..e69e9791 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -138,7 +138,7 @@ void write_to_testcase(afl_state_t *afl, void *mem, u32 len) { /* The same, but with an adjustable gap. Used for trimming. */ -static void write_with_gap(afl_state_t *afl, void *mem, u32 len, u32 skip_at, +static void write_with_gap(afl_state_t *afl, u8 *mem, u32 len, u32 skip_at, u32 skip_len) { s32 fd = afl->fsrv.out_fd; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 031c4049..009eaa12 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -289,8 +289,9 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->cpu_to_bind != -1) FATAL("Multiple -b options not supported"); - if (sscanf(optarg, "%u", &afl->cpu_to_bind) < 0 || optarg[0] == '-') + if (sscanf(optarg, "%d", &afl->cpu_to_bind) < 0) { FATAL("Bad syntax used for -b"); + } break; -- cgit 1.4.1 From 7d7a8c7c39173c340a53868891d65b4477c296c0 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 10 Aug 2020 19:05:22 +0200 Subject: code-format --- include/debug.h | 1 - src/afl-fuzz.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/include/debug.h b/include/debug.h index cb0a63e9..5b1ae54d 100644 --- a/include/debug.h +++ b/include/debug.h @@ -37,7 +37,6 @@ #endif #endif - /******************* * Terminal colors * *******************/ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 009eaa12..5dd092f2 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -290,7 +290,9 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->cpu_to_bind != -1) FATAL("Multiple -b options not supported"); if (sscanf(optarg, "%d", &afl->cpu_to_bind) < 0) { + FATAL("Bad syntax used for -b"); + } break; -- cgit 1.4.1 From 701fb95d24cd754e9c116d81502b6057a29eb2bd Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 10 Aug 2020 23:42:33 +0200 Subject: LTO: make dynamic map the default --- docs/Changelog.md | 2 ++ llvm_mode/README.lto.md | 20 ++++------------- llvm_mode/afl-llvm-lto-instrumentation.so.cc | 33 +++++++++++++++++++++++----- src/afl-forkserver.c | 13 +++++------ 4 files changed, 40 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index f8742b1c..182a15b8 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -28,6 +28,8 @@ sending a mail to . sancov, and also supports function matching! - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) + - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR + for a fixed map address (eg. 0x10000) - LTO: autodictionary mode is a default - LTO: instrim instrumentation disabled, only classic support used as it is always better diff --git a/llvm_mode/README.lto.md b/llvm_mode/README.lto.md index 4d643324..9046c5a8 100644 --- a/llvm_mode/README.lto.md +++ b/llvm_mode/README.lto.md @@ -17,9 +17,6 @@ This version requires a current llvm 11+ compiled from the github master. 5. If any problems arise be sure to set `AR=llvm-ar RANLIB=llvm-ranlib`. Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`. -6. If a target uses _init functions or early constructors then additionally - set `AFL_LLVM_MAP_DYNAMIC=1` as your target will crash otherwise! - ## Introduction and problem description A big issue with how afl/afl++ works is that the basic block IDs that are @@ -128,14 +125,14 @@ on start. This improves coverage statistically by 5-10% :) ## Fixed memory map -To speed up fuzzing, the shared memory map is hard set to a specific address, -by default 0x10000. In most cases this will work without any problems. +To speed up fuzzing, it is possible to set a fixed shared memory map. +Recommened is the value 0x10000. +In most cases this will work without any problems. However if a target uses +early constructors, ifuncs or a deferred forkserver this can crash the target. On unusual operating systems/processors/kernels or weird libraries this might fail so to change the fixed address at compile time set AFL_LLVM_MAP_ADDR with a better value (a value of 0 or empty sets the map address to be dynamic - the original afl way, which is slower). -AFL_LLVM_MAP_DYNAMIC can be set so the shared memory address is dynamic (which -is safer but also slower). ## Document edge IDs @@ -262,15 +259,6 @@ If this succeeeds then there is an issue with afl-clang-lto. Please report at Even some targets where clang-12 fails can be build if the fail is just in `./configure`, see `Solving difficult targets` above. -### Target crashes immediately - -If the target is using early constructors (priority values smaller than 6) -or have their own _init/.init functions and these are instrumented then the -target will likely crash when started. This can be avoided by compiling with -`AFL_LLVM_MAP_DYNAMIC=1` . - -This can e.g. happen with OpenSSL. - ## History This was originally envisioned by hexcoder- in Summer 2019, however we saw no diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index 38c3f202..ddfcb400 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -49,6 +49,7 @@ #include "llvm/Analysis/MemorySSAUpdater.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/Pass.h" +#include "llvm/IR/Constants.h" #include "afl-llvm-common.h" @@ -135,7 +136,10 @@ bool AFLLTOPass::runOnModule(Module &M) { if (getenv("AFL_LLVM_LTO_AUTODICTIONARY")) autodictionary = 1; - if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; + // we make this the default as the fixed map has problems with + // defered forkserver, early constructors, ifuncs and maybe more + /*if (getenv("AFL_LLVM_MAP_DYNAMIC"))*/ + map_addr = 0; if (getenv("AFL_LLVM_SKIPSINGLEBLOCK")) function_minimum_size = 2; @@ -196,7 +200,8 @@ bool AFLLTOPass::runOnModule(Module &M) { ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); ConstantInt *One = ConstantInt::get(Int8Ty, 1); - /* This dumps all inialized global strings - might be useful in the future + // This dumps all inialized global strings - might be useful in the future + /* for (auto G=M.getGlobalList().begin(); G!=M.getGlobalList().end(); G++) { GlobalVariable &GV=*G; @@ -212,7 +217,21 @@ bool AFLLTOPass::runOnModule(Module &M) { } - */ + */ + + if (map_addr) + for (GlobalIFunc &IF : M.ifuncs()) { + + // No clue how to follow these up and find the resolver function. + // If we would know that resolver function name we could just skip + // instrumenting it and everything would be fine :-( + // StringRef ifunc_name = IF.getName(); + // Constant *r = IF.getResolver(); + FATAL( + "Target uses ifunc attribute, dynamic map cannot be used, remove " + "AFL_LLVM_MAP_DYNAMIC"); + + } /* Instrument all the things! */ @@ -220,8 +239,12 @@ bool AFLLTOPass::runOnModule(Module &M) { for (auto &F : M) { - // fprintf(stderr, "DEBUG: Module %s Function %s\n", - // M.getName().str().c_str(), F.getName().str().c_str()); + /*For debugging + AttributeSet X = F.getAttributes().getFnAttributes(); + fprintf(stderr, "DEBUG: Module %s Function %s attributes %u\n", + M.getName().str().c_str(), F.getName().str().c_str(), + X.getNumAttributes()); + */ if (F.size() < function_minimum_size) continue; if (isIgnoreFunction(&F)) continue; diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 752641d7..1ececf27 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -293,8 +293,8 @@ static void report_error_and_exit(int error) { FATAL( "the fuzzing target reports that hardcoded map address might be the " "reason the mmap of the shared memory failed. Solution: recompile " - "the target with either afl-clang-lto and the environment variable " - "AFL_LLVM_MAP_DYNAMIC set or recompile with afl-clang-fast."); + "the target with either afl-clang-lto and do not set " + "AFL_LLVM_MAP_ADDR or recompile with afl-clang-fast."); break; case FS_ERROR_SHM_OPEN: FATAL("the fuzzing target reports that the shm_open() call failed."); @@ -828,8 +828,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, SAYF("\n" cLRD "[-] " cRST "Hmm, looks like the target binary terminated before we could" " complete a handshake with the injected code.\n" - "If the target was compiled with afl-clang-lto then recompiling with" - " AFL_LLVM_MAP_DYNAMIC might solve your problem.\n" + "If the target was compiled with afl-clang-lto and AFL_LLVM_MAP_ADDR" + " then recompiling without this parameter.\n" "Otherwise there is a horrible bug in the fuzzer.\n" "Poke for troubleshooting tips.\n"); @@ -860,9 +860,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, " - the target was compiled with afl-clang-lto and a constructor " "was\n" - " instrumented, recompiling with AFL_LLVM_MAP_DYNAMIC might solve " - "your\n" - " problem\n\n" + " instrumented, recompiling without AFL_LLVM_MAP_ADDR might solve " + "your problem\n\n" " - Less likely, there is a horrible bug in the fuzzer. If other " "options\n" -- cgit 1.4.1 From 9cf8637fab8cf3fe8aba5660015bbe7177805807 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 11 Aug 2020 03:37:02 +0200 Subject: break up llvm rt afl init --- llvm_mode/afl-llvm-rt.o.c | 21 ++++++++++++++++----- src/afl-fuzz.c | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 02dd8dc8..32903d2f 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include @@ -848,7 +850,6 @@ void __afl_manual_init(void) { if (!init_done) { - __afl_map_shm(); __afl_start_forkserver(); init_done = 1; @@ -856,20 +857,30 @@ void __afl_manual_init(void) { } -/* Proper initialization routine. */ +/* Initialization of the forkserver - latest possible */ -__attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { +__attribute__((constructor())) void __afl_auto_init(void) { if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; - is_persistent = !!getenv(PERSIST_ENV_VAR); - if (getenv(DEFER_ENV_VAR)) return; __afl_manual_init(); } +/* Initialization of the shmem - earliest possible because of LTO fixed mem. */ + +__attribute__((constructor(0))) void __afl_auto_early(void) { + + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; + + is_persistent = !!getenv(PERSIST_ENV_VAR); + + __afl_map_shm(); + +} + /* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard. It remains non-operational in the traditional, plugin-backed LLVM mode. For more info about 'trace-pc-guard', see llvm_mode/README.md. diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 783da6e0..fc9cbb6c 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1305,7 +1305,7 @@ int main(int argc, char **argv_orig, char **envp) { OKF("Taint forkserver successfully started"); - const rlim_t kStackSize = 64L * 1024L * 1024L; // min stack size = 64 Mb + const rlim_t kStackSize = 256L * 1024L * 1024L; // min stack size = 256 Mb struct rlimit rl; rl.rlim_cur = kStackSize; if (getrlimit(RLIMIT_STACK, &rl) != 0) -- cgit 1.4.1 From 0ba09ee85a65878e70d1a224f9d41fcbac3ff1e5 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 11 Aug 2020 10:24:45 +0200 Subject: enhancements --- examples/aflpp_driver/aflpp_driver.c | 14 ++++++++------ include/common.h | 2 +- llvm_mode/afl-llvm-rt.o.c | 7 +++---- src/afl-common.c | 30 +++++++++++++++++------------- src/afl-fuzz-one.c | 11 +++++------ src/afl-fuzz.c | 11 +++++------ 6 files changed, 39 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c index 2b7be45f..81782c67 100644 --- a/examples/aflpp_driver/aflpp_driver.c +++ b/examples/aflpp_driver/aflpp_driver.c @@ -306,10 +306,13 @@ int main(int argc, char **argv) { else if (argc > 1) { if (!getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) { + munmap(__afl_area_ptr, MAX_DUMMY_SIZE); // we need to free 0x10000 __afl_area_ptr = NULL; __afl_manual_init(); + } + return ExecuteFilesOnyByOne(argc, argv); } @@ -317,11 +320,14 @@ int main(int argc, char **argv) { assert(N > 0); if (!getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) { + munmap(__afl_area_ptr, MAX_DUMMY_SIZE); __afl_area_ptr = NULL; fprintf(stderr, "performing manual init\n"); - __afl_manual_init(); + __afl_manual_init(); + } + fprintf(stderr, "map is now at %p\n", __afl_area_ptr); // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization @@ -333,11 +339,7 @@ int main(int argc, char **argv) { ssize_t r = read(0, buf, sizeof(buf)); - if (r > 0) { - - LLVMFuzzerTestOneInput(buf, r); - - } + if (r > 0) { LLVMFuzzerTestOneInput(buf, r); } } diff --git a/include/common.h b/include/common.h index 42c79c62..c7d57e07 100644 --- a/include/common.h +++ b/include/common.h @@ -55,7 +55,7 @@ extern u8 *doc_path; /* path to documentation dir */ @returns the path, allocating the string */ u8 *find_binary(u8 *fname); -u8 *find_binary_own_loc(u8 *fname, u8 *own_loc); +u8 *find_afl_binary(u8 *fname, u8 *own_loc); /* Read a bitmap from file fname to memory This is for the -B option again. */ diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index c69d8bb7..20151aea 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -847,9 +847,8 @@ void __afl_manual_init(void) { init_done = 1; is_persistent = 0; __afl_sharedmem_fuzzing = 0; - if (__afl_area_ptr == NULL) - __afl_area_ptr = __afl_area_initial; - + if (__afl_area_ptr == NULL) __afl_area_ptr = __afl_area_initial; + if (getenv("AFL_DEBUG")) fprintf(stderr, "DEBUG: disabled instrumenation because of " @@ -886,7 +885,7 @@ __attribute__((constructor(0))) void __afl_auto_early(void) { is_persistent = !!getenv(PERSIST_ENV_VAR); - __afl_map_shm(); + __afl_map_shm(); } diff --git a/src/afl-common.c b/src/afl-common.c index dabeeedd..c1302080 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -138,7 +138,7 @@ void argv_cpy_free(char **argv) { } -u8 *find_binary_own_loc(u8 *fname, u8 *own_loc) { +u8 *find_afl_binary(u8 *fname, u8 *own_loc) { u8 *tmp, *rsl, *own_copy, *cp; @@ -154,21 +154,25 @@ u8 *find_binary_own_loc(u8 *fname, u8 *own_loc) { } - own_copy = ck_strdup(own_loc); - rsl = strrchr(own_copy, '/'); + if (own_loc) { - if (rsl) { + own_copy = ck_strdup(own_loc); + rsl = strrchr(own_copy, '/'); - *rsl = 0; + if (rsl) { - cp = alloc_printf("%s/%s", own_copy, fname); - ck_free(own_copy); + *rsl = 0; - if (!access(cp, X_OK)) { return cp; } + cp = alloc_printf("%s/%s", own_copy, fname); + ck_free(own_copy); - } else { + if (!access(cp, X_OK)) { return cp; } + + } else { - ck_free(own_copy); + ck_free(own_copy); + + } } @@ -196,7 +200,7 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { /* Now we need to actually find the QEMU binary to put in argv[0]. */ - cp = find_binary_own_loc("afl-qemu-trace", own_loc); + cp = find_afl_binary("afl-qemu-trace", own_loc); if (cp) { @@ -241,12 +245,12 @@ char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { /* Now we need to actually find the QEMU binary to put in argv[0]. */ - cp = find_binary_own_loc("afl-qemu-trace", own_loc); + cp = find_afl_binary("afl-qemu-trace", own_loc); if (cp) { ck_free(cp); - cp = find_binary_own_loc("afl-wine-trace", own_loc); + cp = find_afl_binary("afl-wine-trace", own_loc); if (cp) { diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 9f38b8f8..2f724569 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -2236,8 +2236,8 @@ havoc_stage: clone_to = rand_below(afl, temp_len); - new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), - temp_len + clone_len); + new_buf = + ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); /* Head */ @@ -2403,8 +2403,7 @@ havoc_stage: if (temp_len + extra_len >= MAX_FILE) { break; } - out_buf = - ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); + out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); /* Tail */ memmove(out_buf + insert_at + extra_len, out_buf + insert_at, @@ -2499,8 +2498,8 @@ havoc_stage: clone_to = rand_below(afl, temp_len); - u8 *temp_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), - temp_len + clone_len); + u8 *temp_buf = + ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); /* Head */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 11db004d..d2b2c2d9 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1274,9 +1274,8 @@ int main(int argc, char **argv_orig, char **envp) { ck_free(afl->taint_fsrv.target_path); afl->argv_taint = ck_alloc(sizeof(char *) * (argc + 4 - optind)); - afl->taint_fsrv.target_path = - find_binary_own_loc("afl-qemu-taint", argv[0]); - afl->argv_taint[0] = find_binary_own_loc("afl-qemu-taint", argv[0]); + afl->taint_fsrv.target_path = find_afl_binary("afl-qemu-taint", argv[0]); + afl->argv_taint[0] = find_afl_binary("afl-qemu-taint", argv[0]); if (!afl->argv_taint[0]) FATAL( "Cannot find 'afl-qemu-taint', read qemu_taint/README.md on how to " @@ -1308,19 +1307,19 @@ int main(int argc, char **argv_orig, char **envp) { OKF("Taint forkserver successfully started"); - const rlim_t kStackSize = 256L * 1024L * 1024L; // min stack size = 256 Mb + const rlim_t kStackSize = 256L * 1024L * 1024L; // min stack size = 256 Mb struct rlimit rl; rl.rlim_cur = kStackSize; if (getrlimit(RLIMIT_STACK, &rl) != 0) WARNF("Setting a higher stack size failed!"); -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size + #define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size u8 *tmp1 = ck_maybe_grow(BUF_PARAMS(eff), MAX_FILE + 4096); u8 *tmp2 = ck_maybe_grow(BUF_PARAMS(ex), MAX_FILE + 4096); u8 *tmp3 = ck_maybe_grow(BUF_PARAMS(in_scratch), MAX_FILE + 4096); u8 *tmp4 = ck_maybe_grow(BUF_PARAMS(out), MAX_FILE + 4096); u8 *tmp5 = ck_maybe_grow(BUF_PARAMS(out_scratch), MAX_FILE + 4096); -#undef BUF_PARAMS + #undef BUF_PARAMS if (!tmp1 || !tmp2 || !tmp3 || !tmp4 || !tmp5) FATAL("memory issues. me hungry, feed me!"); -- cgit 1.4.1 From 3ec1b2374336d0b98aa4fc586cd5bc601b711821 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 11 Aug 2020 10:36:34 +0200 Subject: cleanup minor issues --- src/afl-fuzz-bitmap.c | 4 ++++ src/afl-fuzz-one.c | 22 ++++++++++++---------- src/afl-fuzz-queue.c | 3 +-- src/afl-fuzz-run.c | 2 +- src/afl-fuzz.c | 2 +- 5 files changed, 19 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index d4ee36e1..9f58d604 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -183,6 +183,8 @@ u32 count_bits_len(afl_state_t *afl, u8 *mem, u32 len) { u32 i = (len >> 2); u32 ret = 0; + (void)(afl); + if (len % 4) i++; while (i--) { @@ -241,6 +243,8 @@ u32 count_bytes_len(afl_state_t *afl, u8 *mem, u32 len) { u32 i = (len >> 2); u32 ret = 0; + (void)(afl); + while (i--) { u32 v = *(ptr++); diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 2f724569..4b2fd90a 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -472,12 +472,12 @@ u8 fuzz_one_original(afl_state_t *afl) { ret_val = 1; - u32 dst = 0, i; + s32 dst = 0, i; temp_len = len = afl->queue_cur->len; fd = open(afl->queue_cur->fname, O_RDONLY); afl->taint_src = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (fd < 0 || (size_t)afl->taint_src == -1) + if (fd < 0 || (ssize_t)afl->taint_src == -1) FATAL("unable to open '%s'", afl->queue_cur->fname); close(fd); afl->taint_needs_splode = 1; @@ -490,18 +490,18 @@ u8 fuzz_one_original(afl_state_t *afl) { temp_len = len = afl->taint_len = afl->queue_cur->taint_bytes_all; orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (fd < 0 || (size_t)in_buf == -1) + if (fd < 0 || (ssize_t)in_buf == -1) FATAL("unable to open '%s'", afl->taint_input_file); close(fd); fd = open(afl->queue_cur->fname_taint, O_RDWR); afl->taint_map = mmap(0, afl->queue_cur->len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (fd < 0 || (size_t)in_buf == -1) + if (fd < 0 || (ssize_t)in_buf == -1) FATAL("unable to open '%s'", afl->queue_cur->fname_taint); close(fd); - for (i = 0; i < afl->queue_cur->len && dst < len; i++) + for (i = 0; i < (s32)afl->queue_cur->len && dst < len; i++) if (afl->taint_map[i]) in_buf[dst++] = afl->taint_src[i]; break; @@ -512,7 +512,7 @@ u8 fuzz_one_original(afl_state_t *afl) { temp_len = len = afl->taint_len = afl->queue_cur->taint_bytes_new; orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (fd < 0 || (size_t)in_buf == -1) + if (fd < 0 || (ssize_t)in_buf == -1) FATAL("unable to open '%s'", afl->taint_input_file); close(fd); @@ -520,12 +520,12 @@ u8 fuzz_one_original(afl_state_t *afl) { fd = open(fn, O_RDWR); afl->taint_map = mmap(0, afl->queue_cur->len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (fd < 0 || (size_t)in_buf == -1) + if (fd < 0 || (ssize_t)in_buf == -1) FATAL("unable to open '%s' for %u bytes", fn, len); close(fd); ck_free(fn); - for (i = 0; i < afl->queue_cur->len && dst < len; i++) + for (i = 0; i < (s32)afl->queue_cur->len && dst < len; i++) if (afl->taint_map[i]) in_buf[dst++] = afl->taint_src[i]; break; @@ -2297,7 +2297,8 @@ havoc_stage: } copy_to = rand_below(afl, temp_len - copy_len + 1); - if (unlikely(copy_to > temp_len)) copy_to = rand_below(afl, temp_len); + if (unlikely(copy_to > (u32)temp_len)) + copy_to = rand_below(afl, temp_len); if (rand_below(afl, 4)) { @@ -2305,7 +2306,8 @@ havoc_stage: if (unlikely(afl->taint_needs_splode)) { - if (copy_to > temp_len) copy_to = rand_below(afl, temp_len); + if (copy_to > (u32)temp_len) + copy_to = rand_below(afl, temp_len); // fprintf(stderr, "\nout_buf %p + copy_to %u, src %p + %u, // copy_len %u -- len %u\n", out_buf , copy_to, afl->taint_src , diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 28af17f0..f4b58a9d 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -108,7 +108,6 @@ void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, u8 * ptr, *fn = fname; u32 bytes = 0, plen = len; - s32 fd = -1; struct queue_entry *prev = q->prev; if (plen % 4) plen = plen + 4 - (len % 4); @@ -170,7 +169,7 @@ void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, u8 *bufr = mmap(0, prev->len, PROT_READ, MAP_PRIVATE, r, 0); - if ((size_t)bufr != -1) { + if ((ssize_t)bufr != -1) { u32 i; u8 *tmp = ck_maybe_grow(BUF_PARAMS(in_scratch), plen); diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 9db23134..058f8c2d 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -883,7 +883,7 @@ u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { u8 *new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len); u32 i, taint = 0; - for (i = 0; i < new_len; i++) { + for (i = 0; i < (u32)new_len; i++) { if (i >= afl->taint_len || i >= afl->queue_cur->len || afl->taint_map[i]) new_buf[i] = out_buf[taint++]; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index d2b2c2d9..e6238366 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1095,7 +1095,7 @@ int main(int argc, char **argv_orig, char **envp) { if (map_size != real_map_size) { afl->fsrv.map_size = real_map_size; - if (afl->cmplog_binary) afl->cmplog_fsrv.map_size; + if (afl->cmplog_binary) afl->cmplog_fsrv.map_size = real_map_size; } -- cgit 1.4.1 From 4f695b6f4c3ced165703363904e42492fca82112 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 11 Aug 2020 11:16:48 +0200 Subject: fixes --- include/afl-fuzz.h | 2 +- src/afl-common.c | 4 +-- src/afl-fuzz-bitmap.c | 2 ++ src/afl-fuzz-queue.c | 78 +++++++++++++++++++++++++++++++++++---------------- src/afl-fuzz-stats.c | 5 ++-- 5 files changed, 62 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 02c36861..c578c583 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -506,7 +506,7 @@ typedef struct afl_state { var_byte_count, /* Bitmap bytes with var behavior */ current_entry, /* Current queue entry ID */ havoc_div, /* Cycle count divisor for havoc */ - taint_len; + taint_len, taint_count; u64 total_crashes, /* Total number of crashes */ unique_crashes, /* Crashes with unique signatures */ diff --git a/src/afl-common.c b/src/afl-common.c index c1302080..cefed8dc 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -471,8 +471,8 @@ void read_bitmap(u8 *fname, u8 *map, size_t len) { u64 get_cur_time(void) { - static struct timeval tv; - static struct timezone tz; + struct timeval tv; + struct timezone tz; gettimeofday(&tv, &tz); diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 9f58d604..d273818d 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -245,6 +245,8 @@ u32 count_bytes_len(afl_state_t *afl, u8 *mem, u32 len) { (void)(afl); + if (len % 4) i++; + while (i--) { u32 v = *(ptr++); diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index f4b58a9d..b56e10f8 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -125,42 +125,64 @@ void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, if (afl_fsrv_run_target(&afl->taint_fsrv, afl->fsrv.exec_tmout, &afl->stop_soon) == 0) { - bytes = count_bytes_len(afl, afl->taint_fsrv.trace_bits, plen); + bytes = q->taint_bytes_all = + count_bytes_len(afl, afl->taint_fsrv.trace_bits, plen); if (afl->debug) fprintf(stderr, "Debug: tainted %u out of %u bytes\n", bytes, len); - if (bytes) { + /* DEBUG FIXME TODO XXX */ + u32 i; + for (i = 0; i < len; i++) { - s32 i = len; - while (i > 0 && !afl->taint_fsrv.trace_bits[i - 1]) - i--; - q->taint_bytes_highest = i; + if (afl->taint_fsrv.trace_bits[i] && + afl->taint_fsrv.trace_bits[i] != '!') + FATAL("invalid taint map value %02x at pos %d", + afl->taint_fsrv.trace_bits[i], i); } - } + if (len < plen) + for (i = len; i < plen; i++) { - if (((bytes * 100) / len) < 90) { + if (afl->taint_fsrv.trace_bits[i]) + FATAL("invalid taint map value %02x in padding at pos %d", + afl->taint_fsrv.trace_bits[i], i); - // we only use the taint havoc mode if the entry has less than 90% of - // overall tainted bytes - q->taint_bytes_all = bytes; + } + + } + + // if all is tainted we do not need to write taint data away + if (bytes && bytes < len) { // save the bytes away int w = open(q->fname_taint, O_CREAT | O_WRONLY, 0644); if (w >= 0) { - ck_write(w, afl->taint_fsrv.trace_bits, plen, q->fname_taint); + ck_write(w, afl->taint_fsrv.trace_bits, len, q->fname_taint); close(w); + // find the highest tainted offset in the input (for trim opt) + s32 i = len; + while (i > 0 && !afl->taint_fsrv.trace_bits[i - 1]) + i--; + q->taint_bytes_highest = i; + + afl->taint_count++; + } else { FATAL("could not create %s", q->fname_taint); - bytes = 0; + q->taint_bytes_all = bytes = 0; } - if (bytes && prev && prev->taint_bytes_all) { + // it is possible that there is no main taint file - if the whole file + // is tainted - but a .new taint file if it had new tainted bytes + + // check if there is a previous queue entry and if it had taint + if (bytes && prev && prev->taint_bytes_all && + prev->taint_bytes_all < prev->len) { // check if there are new bytes in the taint vs the previous int r = open(prev->fname_taint, O_RDONLY); @@ -181,14 +203,28 @@ void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, q->taint_bytes_new = count_bytes_len(afl, tmp, plen); + if (afl->debug) + fprintf(stderr, "Debug: %u new taint out of %u bytes\n", bytes, + len); + if (q->taint_bytes_new) { u8 *fnw = alloc_printf("%s.new", q->fname_taint); - int w = open(fnw, O_CREAT | O_WRONLY, 0644); - if (w >= 0) { + if (fnw) { + + int w = open(fnw, O_CREAT | O_WRONLY, 0644); + if (w >= 0) { + + ck_write(w, tmp, plen, fnw); + close(w); - ck_write(w, tmp, plen, fnw); - close(w); + } else { + + q->taint_bytes_new = 0; + + } + + ck_free(fnw); } else { @@ -196,8 +232,6 @@ void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, } - ck_free(fnw); - } munmap(bufr, prev->len); @@ -210,10 +244,6 @@ void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, } - } else { - - bytes = 0; - } memcpy(afl->taint_fsrv.trace_bits, save, afl->fsrv.map_size); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index aeb290bd..0cc06e12 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -116,6 +116,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, "edges_found : %u\n" "var_byte_count : %u\n" "havoc_expansion : %u\n" + "tainted_inputs : %u\n" "afl_banner : %s\n" "afl_version : " VERSION "\n" @@ -149,8 +150,8 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, #else -1, #endif - t_bytes, afl->var_byte_count, afl->expand_havoc, afl->use_banner, - afl->unicorn_mode ? "unicorn" : "", + t_bytes, afl->var_byte_count, afl->expand_havoc, afl->taint_count, + afl->use_banner, afl->unicorn_mode ? "unicorn" : "", afl->fsrv.qemu_mode ? "qemu " : "", afl->non_instrumented_mode ? " non_instrumented " : "", afl->no_forkserver ? "no_fsrv " : "", afl->crash_mode ? "crash " : "", -- cgit 1.4.1 From 457f627101c08b885e9edfd8b491b5be198b6f14 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 11 Aug 2020 15:10:18 +0200 Subject: move taint_mode var --- src/afl-forkserver.c | 2 +- src/afl-fuzz-init.c | 6 +++--- src/afl-fuzz-one.c | 2 +- src/afl-fuzz-queue.c | 3 ++- src/afl-fuzz-run.c | 2 +- src/afl-fuzz.c | 30 ++++++++++++++++-------------- 6 files changed, 24 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index adb75a2d..56475320 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -497,7 +497,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, char pid_buf[16]; sprintf(pid_buf, "%d", fsrv->fsrv_pid); - if (fsrv->qemu_mode == 2) { + if (fsrv->taint_mode) { setenv("__AFL_TARGET_PID3", pid_buf, 1); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 432e0649..669bd65a 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -961,7 +961,7 @@ void perform_dry_run(afl_state_t *afl) { } /* perform taint gathering on the input seed */ - if (afl->fsrv.taint_mode) + if (afl->taint_mode) perform_taint_run(afl, q, q->fname, use_mem, q->len); q = q->next; @@ -1502,7 +1502,7 @@ static void handle_existing_out_dir(afl_state_t *afl) { if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; } ck_free(fn); - if (afl->fsrv.taint_mode) { + if (afl->taint_mode) { fn = alloc_printf("%s/taint", afl->out_dir); mkdir(fn, 0755); // ignore errors @@ -1745,7 +1745,7 @@ void setup_dirs_fds(afl_state_t *afl) { /* Taint directory if taint_mode. */ - if (afl->fsrv.taint_mode) { + if (afl->taint_mode) { tmp = alloc_printf("%s/taint", afl->out_dir); if (mkdir(tmp, 0700)) { PFATAL("Unable to create '%s'", tmp); } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 4b2fd90a..69f885ca 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -460,7 +460,7 @@ u8 fuzz_one_original(afl_state_t *afl) { u32 tmp_val = 0; - if (unlikely(afl->fsrv.taint_mode)) { + if (unlikely(afl->taint_mode)) { tmp_val = afl->queue_cycle % 2; ret_val = 0; diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index b56e10f8..bb44e465 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -220,6 +220,7 @@ void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, } else { + FATAL("count not create '%s'", fnw); q->taint_bytes_new = 0; } @@ -419,7 +420,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u8 *mem, u32 len, afl->last_path_time = get_cur_time(); /* trigger the tain gathering if this is not a dry run */ - if (afl->fsrv.taint_mode && mem) { + if (afl->taint_mode && mem) { perform_taint_run(afl, q, fname, mem, len); diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 058f8c2d..5f928333 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -349,7 +349,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } - if (unlikely(afl->fsrv.taint_mode)) + if (unlikely(afl->taint_mode)) q->exec_cksum = 0; else if (q->exec_cksum) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e6238366..bead2ed9 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -288,7 +288,7 @@ int main(int argc, char **argv_orig, char **envp) { switch (opt) { case 'A': - afl->fsrv.taint_mode = 1; + afl->taint_mode = 1; if (!mem_limit_given) { afl->fsrv.mem_limit = MEM_LIMIT_QEMU; } break; @@ -829,10 +829,10 @@ int main(int argc, char **argv_orig, char **envp) { } - if (afl->fsrv.taint_mode && afl->fsrv.map_size < MAX_FILE) { + if (afl->taint_mode && afl->fsrv.map_size < MAX_FILE) { real_map_size = map_size; - map_size = afl->fsrv.map_size = afl->shm.map_size = MAX_FILE; + map_size = MAX_FILE; } @@ -891,9 +891,12 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->crash_mode) { FATAL("-C and -n are mutually exclusive"); } if (afl->fsrv.qemu_mode) { FATAL("-Q and -n are mutually exclusive"); } if (afl->unicorn_mode) { FATAL("-U and -n are mutually exclusive"); } - if (afl->fsrv.taint_mode) { FATAL("-A and -n are mutually exclusive"); } + if (afl->taint_mode) { FATAL("-A and -n are mutually exclusive"); } } + + if (afl->limit_time_sig != 0 && afl->taint_mode) { FATAL("-A and -L are mutually exclusive"); } + if (afl->unicorn_mode != 0 && afl->taint_mode) { FATAL("-A and -U are mutually exclusive"); } if (get_afl_env("AFL_DISABLE_TRIM")) { afl->disable_trim = 1; } @@ -992,7 +995,7 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->afl_env.afl_preload) { - if (afl->fsrv.qemu_mode || afl->fsrv.taint_mode) { + if (afl->fsrv.qemu_mode || afl->taint_mode) { u8 *qemu_preload = getenv("QEMU_SET_ENV"); u8 *afl_preload = getenv("AFL_PRELOAD"); @@ -1088,17 +1091,17 @@ int main(int argc, char **argv_orig, char **envp) { afl->fsrv.trace_bits = afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); - if (!afl->in_bitmap) { memset(afl->virgin_bits, 255, afl->fsrv.map_size); } - memset(afl->virgin_tmout, 255, afl->fsrv.map_size); - memset(afl->virgin_crash, 255, afl->fsrv.map_size); - - if (map_size != real_map_size) { + if (real_map_size && map_size != real_map_size) { afl->fsrv.map_size = real_map_size; if (afl->cmplog_binary) afl->cmplog_fsrv.map_size = real_map_size; } + if (!afl->in_bitmap) { memset(afl->virgin_bits, 255, afl->fsrv.map_size); } + memset(afl->virgin_tmout, 255, afl->fsrv.map_size); + memset(afl->virgin_crash, 255, afl->fsrv.map_size); + init_count_class16(); if (afl->is_main_node && check_main_node_exists(afl) == 1) { @@ -1260,7 +1263,7 @@ int main(int argc, char **argv_orig, char **envp) { } - if (afl->fsrv.taint_mode) { + if (afl->taint_mode) { ACTF("Spawning qemu_taint forkserver"); @@ -1268,7 +1271,6 @@ int main(int argc, char **argv_orig, char **envp) { setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); afl_fsrv_init_dup(&afl->taint_fsrv, &afl->fsrv); - afl->taint_fsrv.qemu_mode = 2; afl->taint_fsrv.taint_mode = 1; afl->taint_fsrv.trace_bits = afl->fsrv.trace_bits; @@ -1399,7 +1401,7 @@ int main(int argc, char **argv_orig, char **envp) { break; case 1: if (afl->limit_time_sig == 0 && !afl->custom_only && - !afl->python_only && !afl->fsrv.taint_mode) { + !afl->python_only && !afl->taint_mode) { afl->limit_time_sig = -1; afl->limit_time_puppet = 0; @@ -1588,7 +1590,7 @@ stop_fuzzing: } if (afl->cmplog_binary) afl_fsrv_deinit(&afl->cmplog_fsrv); - if (afl->fsrv.taint_mode) afl_fsrv_deinit(&afl->taint_fsrv); + if (afl->taint_mode) afl_fsrv_deinit(&afl->taint_fsrv); afl_fsrv_deinit(&afl->fsrv); if (afl->orig_cmdline) { ck_free(afl->orig_cmdline); } if (afl->argv_taint) { ck_free(afl->argv_taint); } -- cgit 1.4.1 From 220dc4a43d197f5ff451627a9923b874805c02aa Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 11 Aug 2020 16:25:35 +0200 Subject: review done, pray --- include/afl-fuzz.h | 3 +- src/afl-fuzz-init.c | 3 +- src/afl-fuzz-one.c | 152 ++++++++++++++++++++++++++++++++++++++++----------- src/afl-fuzz-queue.c | 6 +- src/afl-fuzz-run.c | 10 +++- src/afl-fuzz.c | 17 ++++-- 6 files changed, 144 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index c578c583..e251183c 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -444,7 +444,8 @@ typedef struct afl_state { python_only, /* Python-only mode */ is_main_node, /* if this is the main node */ is_secondary_node, /* if this is a secondary instance */ - taint_needs_splode; /* explode fuzz input */ + taint_needs_splode, /* explode fuzz input */ + taint_mode; u32 stats_update_freq; /* Stats update frequency (execs) */ diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 669bd65a..0150e18a 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -961,8 +961,7 @@ void perform_dry_run(afl_state_t *afl) { } /* perform taint gathering on the input seed */ - if (afl->taint_mode) - perform_taint_run(afl, q, q->fname, use_mem, q->len); + if (afl->taint_mode) perform_taint_run(afl, q, q->fname, use_mem, q->len); q = q->next; diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 69f885ca..7718256f 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -462,18 +462,23 @@ u8 fuzz_one_original(afl_state_t *afl) { if (unlikely(afl->taint_mode)) { - tmp_val = afl->queue_cycle % 2; + tmp_val = afl->queue_cycle % 2; // starts with 1 ret_val = 0; - if (unlikely(afl->queue_cur->cal_failed)) goto abandon_entry; - if (unlikely(!afl->queue_cur->passed_det) && !tmp_val) goto abandon_entry; - if (tmp_val == 1 && !afl->queue_cur->taint_bytes_all) goto abandon_entry; - if (tmp_val == 0 && !afl->queue_cur->taint_bytes_new) goto abandon_entry; + if (unlikely(afl->queue_cur->cal_failed && !tmp_val)) goto abandon_entry; + if (unlikely(!afl->skip_deterministic && !afl->queue_cur->passed_det && + !tmp_val)) + goto abandon_entry; + if ((!afl->queue_cur->taint_bytes_new || + afl->queue_cur->taint_bytes_new == afl->queue_cur->len) && + !tmp_val) + goto abandon_entry; ret_val = 1; s32 dst = 0, i; temp_len = len = afl->queue_cur->len; + s32 j = 0; // tmp fd = open(afl->queue_cur->fname, O_RDONLY); afl->taint_src = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); @@ -486,15 +491,27 @@ u8 fuzz_one_original(afl_state_t *afl) { case 1: // fuzz only tainted bytes + // special case: all or nothing tainted. in this case we act like + // nothing is special. this is not the taint you are looking for ... + if (!afl->queue_cur->taint_bytes_all || + afl->queue_cur->taint_bytes_all == (u32)len) { + + orig_in = in_buf = afl->taint_src; + afl->taint_needs_splode = 0; + break; + + } + fd = open(afl->taint_input_file, O_RDONLY); temp_len = len = afl->taint_len = afl->queue_cur->taint_bytes_all; orig_in = in_buf = - mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + mmap(0, len >= MAX_FILE - 65536 ? MAX_FILE : len + 65536, + PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (fd < 0 || (ssize_t)in_buf == -1) FATAL("unable to open '%s'", afl->taint_input_file); close(fd); - fd = open(afl->queue_cur->fname_taint, O_RDWR); + fd = open(afl->queue_cur->fname_taint, O_RDONLY); afl->taint_map = mmap(0, afl->queue_cur->len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (fd < 0 || (ssize_t)in_buf == -1) @@ -504,6 +521,29 @@ u8 fuzz_one_original(afl_state_t *afl) { for (i = 0; i < (s32)afl->queue_cur->len && dst < len; i++) if (afl->taint_map[i]) in_buf[dst++] = afl->taint_src[i]; + // FIXME DEBUG TODO XXX + for (i = 0; i < (s32)afl->queue_cur->len; i++) { + + switch (afl->taint_map[i]) { + + case 0x0: + break; + case '!': + j++; + break; + default: + FATAL( + "invalid taint map entry byte 0x%02x at position %d " + "(passed_det:%d)\n", + afl->taint_map[i], i, afl->queue_cur->passed_det); + + } + + } + + if (j != len) + FATAL("different taint values in map vs in queue (%d != %d)", j, len); + break; case 0: // fuzz only newly tainted bytes @@ -511,12 +551,14 @@ u8 fuzz_one_original(afl_state_t *afl) { fd = open(afl->taint_input_file, O_RDONLY); temp_len = len = afl->taint_len = afl->queue_cur->taint_bytes_new; orig_in = in_buf = - mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + mmap(0, len >= MAX_FILE - 65536 ? MAX_FILE : len + 65536, + PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (fd < 0 || (ssize_t)in_buf == -1) FATAL("unable to open '%s'", afl->taint_input_file); close(fd); u8 *fn = alloc_printf("%s.new", afl->queue_cur->fname_taint); + if (!fn) FATAL("OOM"); fd = open(fn, O_RDWR); afl->taint_map = mmap(0, afl->queue_cur->len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); @@ -528,14 +570,35 @@ u8 fuzz_one_original(afl_state_t *afl) { for (i = 0; i < (s32)afl->queue_cur->len && dst < len; i++) if (afl->taint_map[i]) in_buf[dst++] = afl->taint_src[i]; + // FIXME DEBUG TODO XXX + for (i = 0; i < (s32)afl->queue_cur->len; i++) { + + switch (afl->taint_map[i]) { + + case 0x0: + break; + case '!': + j++; + break; + default: + FATAL( + "invalid taint map entry byte 0x%02x at position %d " + "(passed_det:%d)\n", + afl->taint_map[i], i, afl->queue_cur->passed_det); + + } + + } + + if (j != len) + FATAL("different taint values in map vs in queue (%d != %d)", j, len); + break; } } else { - afl->taint_needs_splode = 0; - /* Map the test case into memory. */ fd = open(afl->queue_cur->fname, O_RDONLY); @@ -574,8 +637,7 @@ u8 fuzz_one_original(afl_state_t *afl) { * CALIBRATION (only if failed earlier on) * *******************************************/ - if (unlikely(afl->queue_cur->cal_failed && - (!afl->taint_needs_splode || tmp_val == 1))) { + if (unlikely(afl->queue_cur->cal_failed)) { u8 res = FSRV_RUN_TMOUT; @@ -583,8 +645,12 @@ u8 fuzz_one_original(afl_state_t *afl) { afl->queue_cur->exec_cksum = 0; - res = - calibrate_case(afl, afl->queue_cur, in_buf, afl->queue_cycle - 1, 0); + if (unlikely(afl->taint_needs_splode)) + res = calibrate_case(afl, afl->queue_cur, afl->taint_src, + afl->queue_cycle - 1, 0); + else + res = calibrate_case(afl, afl->queue_cur, in_buf, afl->queue_cycle - 1, + 0); if (unlikely(res == FSRV_RUN_ERROR)) { @@ -607,8 +673,8 @@ u8 fuzz_one_original(afl_state_t *afl) { * TRIMMING * ************/ - if (!afl->non_instrumented_mode && !afl->queue_cur->trim_done && - !afl->disable_trim && !afl->taint_needs_splode) { + if (unlikely(!afl->non_instrumented_mode && !afl->queue_cur->trim_done && + !afl->disable_trim && !afl->taint_needs_splode)) { u8 res = trim_case(afl, afl->queue_cur, in_buf); @@ -645,13 +711,26 @@ u8 fuzz_one_original(afl_state_t *afl) { if (afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized) { - if (input_to_state_stage(afl, in_buf, out_buf, len, - afl->queue_cur->exec_cksum)) { + int res; + if (unlikely(afl->taint_needs_splode)) { - goto abandon_entry; + len = afl->queue_cur->len; + memcpy(out_buf, afl->taint_src, len); + res = input_to_state_stage(afl, afl->taint_src, out_buf, len, + afl->queue_cur->exec_cksum); + // just abandon as success + ret_val = 0; + res = 1; + + } else { + + res = input_to_state_stage(afl, in_buf, out_buf, len, + afl->queue_cur->exec_cksum); } + if (unlikely(res)) { goto abandon_entry; } + } /* Skip right away if -d is given, if it has not been chosen sufficiently @@ -2288,37 +2367,46 @@ havoc_stage: copy_len = choose_block_len(afl, afl->queue_cur->len - 1); copy_from = rand_below(afl, afl->queue_cur->len - copy_len + 1); + copy_to = rand_below(afl, temp_len + 1); } else { copy_len = choose_block_len(afl, temp_len - 1); copy_from = rand_below(afl, temp_len - copy_len + 1); + copy_to = rand_below(afl, temp_len - copy_len + 1); } - copy_to = rand_below(afl, temp_len - copy_len + 1); - if (unlikely(copy_to > (u32)temp_len)) - copy_to = rand_below(afl, temp_len); - if (rand_below(afl, 4)) { if (copy_from != copy_to) { if (unlikely(afl->taint_needs_splode)) { - if (copy_to > (u32)temp_len) - copy_to = rand_below(afl, temp_len); + if (temp_len >= (s32)(copy_to + copy_len)) { - // fprintf(stderr, "\nout_buf %p + copy_to %u, src %p + %u, - // copy_len %u -- len %u\n", out_buf , copy_to, afl->taint_src , - // copy_from, copy_len, afl->taint_len, afl->queue_cur->len); - memmove(out_buf + copy_to, afl->taint_src + copy_from, - copy_len); + memcpy(out_buf + copy_to, afl->taint_src + copy_from, + copy_len); - } else + } else { + + u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), + copy_to + copy_len); + memcpy(new_buf, in_buf, copy_to); + memcpy(new_buf + copy_to, afl->taint_src + copy_from, + copy_len); + swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); + out_buf = new_buf; + temp_len = copy_to + copy_len; + + } + + } else { memmove(out_buf + copy_to, out_buf + copy_from, copy_len); + } + } } else { @@ -2698,7 +2786,7 @@ abandon_entry: ++afl->queue_cur->fuzz_level; - if (afl->taint_needs_splode) { + if (unlikely(afl->taint_needs_splode)) { munmap(afl->taint_src, afl->queue_cur->len); munmap(orig_in, afl->taint_len); diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index bb44e465..a1fe146b 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -420,11 +420,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u8 *mem, u32 len, afl->last_path_time = get_cur_time(); /* trigger the tain gathering if this is not a dry run */ - if (afl->taint_mode && mem) { - - perform_taint_run(afl, q, fname, mem, len); - - } + if (afl->taint_mode && mem) { perform_taint_run(afl, q, fname, mem, len); } /* only redqueen currently uses is_ascii */ if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(q); diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 5f928333..94cfc383 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -878,9 +878,11 @@ u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { if (unlikely(afl->taint_needs_splode)) { s32 new_len = afl->queue_cur->len + len - afl->taint_len; - if (new_len < 4) new_len = 4; - if (new_len > MAX_FILE) new_len = MAX_FILE; - u8 *new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len); + if (new_len < 4) + new_len = 4; + else if (new_len > MAX_FILE) + new_len = MAX_FILE; + u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), new_len); u32 i, taint = 0; for (i = 0; i < (u32)new_len; i++) { @@ -892,6 +894,8 @@ u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { } + swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); + out_buf = new_buf; len = new_len; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index bead2ed9..106aa550 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -894,9 +894,18 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->taint_mode) { FATAL("-A and -n are mutually exclusive"); } } - - if (afl->limit_time_sig != 0 && afl->taint_mode) { FATAL("-A and -L are mutually exclusive"); } - if (afl->unicorn_mode != 0 && afl->taint_mode) { FATAL("-A and -U are mutually exclusive"); } + + if (afl->limit_time_sig != 0 && afl->taint_mode) { + + FATAL("-A and -L are mutually exclusive"); + + } + + if (afl->unicorn_mode != 0 && afl->taint_mode) { + + FATAL("-A and -U are mutually exclusive"); + + } if (get_afl_env("AFL_DISABLE_TRIM")) { afl->disable_trim = 1; } @@ -1309,7 +1318,7 @@ int main(int argc, char **argv_orig, char **envp) { OKF("Taint forkserver successfully started"); - const rlim_t kStackSize = 256L * 1024L * 1024L; // min stack size = 256 Mb + const rlim_t kStackSize = 128L * 1024L * 1024L; // min stack size = 128 Mb struct rlimit rl; rl.rlim_cur = kStackSize; if (getrlimit(RLIMIT_STACK, &rl) != 0) -- cgit 1.4.1 From b604f5eafcebb816026e198df0ea66ebcbf18421 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 11 Aug 2020 18:06:18 +0200 Subject: finalize first beta! yay! --- README.md | 14 ++++++++------ examples/aflpp_driver/aflpp_driver.c | 28 ---------------------------- src/afl-fuzz-init.c | 9 +++++++-- src/afl-fuzz-one.c | 2 +- src/afl-fuzz-queue.c | 2 +- 5 files changed, 17 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/README.md b/README.md index 2b9bc588..6e324cb0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # qemu_taint variant. +UPDATE: **WORKS NOW** **PLEASE TEST** **:-)** + ## HOWTO cd qemu_taint && ./build_qemu_taint.sh @@ -8,13 +10,13 @@ afl-fuzz -A ... ## CAVEATS - * segfaults ~10-15 minutes in ... - - * shmem persistent mode does not work - * custom mutators? dunno if they work or not - * MOpt works but totally ignores the taint information + * llvm shmem persistent mode does not and can not not work + * MOpt works but totally ignores the taint information, so disabled here + * custom mutators? dunno if they work or not. depends on how they work. * not tested with qemu_mode - * if all seed entries are fully touched it might not work + * there are several debug checks to ensure the data is fine which slows down + fuzzing, if the beta experiment runs fine these will be improved and it + will result in quite a speed gain. ## THE TAINT diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c index 81782c67..8e0b554a 100644 --- a/examples/aflpp_driver/aflpp_driver.c +++ b/examples/aflpp_driver/aflpp_driver.c @@ -107,8 +107,6 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. #endif int __afl_sharedmem_fuzzing = 0; -extern unsigned char *__afl_area_ptr; -// extern struct cmp_map *__afl_cmp_map; // libFuzzer interface is thin, so we don't include any libFuzzer headers. int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); @@ -246,28 +244,8 @@ static int ExecuteFilesOnyByOne(int argc, char **argv) { } -__attribute__((constructor(1))) void __afl_protect(void) { - - setenv("__AFL_DEFER_FORKSRV", "1", 1); - __afl_area_ptr = (unsigned char *)mmap( - (void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, - MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); - if ((uint64_t)__afl_area_ptr == -1) - __afl_area_ptr = (unsigned char *)mmap((void *)0x10000, MAX_DUMMY_SIZE, - PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); - if ((uint64_t)__afl_area_ptr == -1) - __afl_area_ptr = - (unsigned char *)mmap(NULL, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); - // __afl_cmp_map = (struct cmp_map *)__afl_area_ptr; - -} - int main(int argc, char **argv) { - fprintf(stderr, "map is at %p\n", __afl_area_ptr); - printf( "======================= INFO =========================\n" "This binary is built for afl++.\n" @@ -307,8 +285,6 @@ int main(int argc, char **argv) { if (!getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) { - munmap(__afl_area_ptr, MAX_DUMMY_SIZE); // we need to free 0x10000 - __afl_area_ptr = NULL; __afl_manual_init(); } @@ -321,15 +297,11 @@ int main(int argc, char **argv) { if (!getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) { - munmap(__afl_area_ptr, MAX_DUMMY_SIZE); - __afl_area_ptr = NULL; fprintf(stderr, "performing manual init\n"); __afl_manual_init(); } - fprintf(stderr, "map is now at %p\n", __afl_area_ptr); - // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization // on the first execution of LLVMFuzzerTestOneInput is ignored. LLVMFuzzerTestOneInput(dummy_input, 1); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 0150e18a..359eef85 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -771,9 +771,13 @@ void perform_dry_run(afl_state_t *afl) { close(fd); res = calibrate_case(afl, q, use_mem, 0, 1); - ck_free(use_mem); - if (afl->stop_soon) { return; } + if (afl->stop_soon) { + + ck_free(use_mem); + return; + + } if (res == afl->crash_mode || res == FSRV_RUN_NOBITS) { @@ -962,6 +966,7 @@ void perform_dry_run(afl_state_t *afl) { /* perform taint gathering on the input seed */ if (afl->taint_mode) perform_taint_run(afl, q, q->fname, use_mem, q->len); + ck_free(use_mem); q = q->next; diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 7718256f..4db6febf 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -2342,7 +2342,7 @@ havoc_stage: } /* Tail */ - memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, + memmove(new_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index a1fe146b..43794018 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -122,7 +122,7 @@ void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, afl->taint_fsrv.map_size = plen; // speed :) write_to_testcase(afl, mem, len); - if (afl_fsrv_run_target(&afl->taint_fsrv, afl->fsrv.exec_tmout, + if (afl_fsrv_run_target(&afl->taint_fsrv, afl->fsrv.exec_tmout * 4, &afl->stop_soon) == 0) { bytes = q->taint_bytes_all = -- cgit 1.4.1 From b38837f4ff8f2e52597b7908b9226500e5c61933 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 12 Aug 2020 14:14:44 +0200 Subject: setting attribute hot intelligently gives 0.5% speed --- docs/Changelog.md | 2 +- src/afl-forkserver.c | 4 ++-- src/afl-fuzz-bitmap.c | 8 ++++---- src/afl-fuzz-run.c | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index eda57a1a..edcdac58 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -26,7 +26,7 @@ sending a mail to . AFL_LLVM_WHITELIST and AFL_LLVM_INSTRUMENT_FILE are deprecated and are matched to AFL_LLVM_ALLOWLIST). The format is compatible to llvm sancov, and also supports function matching! - - added nozero counting to trace-pc/pcgard + - added neverzero counting to trace-pc/pcgard - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 1ececf27..6819fc8a 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -122,7 +122,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { Returns the time passed to read. If the wait times out, returns timeout_ms + 1; Returns 0 if an error occurred (fd closed, signal, ...); */ -static u32 read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, +static u32 __attribute__ ((hot)) read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, volatile u8 *stop_soon_p) { fd_set readfds; @@ -322,7 +322,7 @@ static void report_error_and_exit(int error) { cloning a stopped child. So, we just execute once, and then send commands through a pipe. The other part of this logic is in afl-as.h / llvm_mode */ -void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, +void __attribute__ ((hot)) afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, u8 debug_child_output) { int st_pipe[2], ctl_pipe[2]; diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index aa8d5a18..f6389c06 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -57,7 +57,7 @@ void write_bitmap(afl_state_t *afl) { This function is called after every exec() on a fairly large buffer, so it needs to be fast. We do this in 32-bit and 64-bit flavors. */ -u8 has_new_bits(afl_state_t *afl, u8 *virgin_map) { +u8 __attribute__ ((hot)) has_new_bits(afl_state_t *afl, u8 *virgin_map) { #ifdef WORD_SIZE_64 @@ -347,7 +347,7 @@ void init_count_class16(void) { #ifdef WORD_SIZE_64 -void classify_counts(afl_forkserver_t *fsrv) { +void __attribute__ ((hot)) classify_counts(afl_forkserver_t *fsrv) { u64 *mem = (u64 *)fsrv->trace_bits; @@ -376,7 +376,7 @@ void classify_counts(afl_forkserver_t *fsrv) { #else -void classify_counts(afl_forkserver_t *fsrv) { +void __attribute__ ((hot)) classify_counts(afl_forkserver_t *fsrv) { u32 *mem = (u32 *)fsrv->trace_bits; @@ -534,7 +534,7 @@ static void write_crash_readme(afl_state_t *afl) { save or queue the input test case for further analysis if so. Returns 1 if entry is saved, 0 otherwise. */ -u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { +u8 __attribute__ ((hot)) save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (unlikely(len == 0)) { return 0; } diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index e69e9791..97fcb3c8 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -38,7 +38,7 @@ u64 time_spent_working = 0; /* Execute target application, monitoring for timeouts. Return status information. The called program will update afl->fsrv->trace_bits. */ -fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, +fsrv_run_result_t __attribute__ ((hot)) fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) { #ifdef PROFILING @@ -72,7 +72,7 @@ fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, old file is unlinked and a new one is created. Otherwise, afl->fsrv.out_fd is rewound and truncated. */ -void write_to_testcase(afl_state_t *afl, void *mem, u32 len) { +void __attribute__ ((hot)) write_to_testcase(afl_state_t *afl, void *mem, u32 len) { #ifdef _AFL_DOCUMENT_MUTATIONS s32 doc_fd; @@ -858,7 +858,7 @@ abort_trimming: error conditions, returning 1 if it's time to bail out. This is a helper function for fuzz_one(). */ -u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { +u8 __attribute__ ((hot)) common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { u8 fault; -- cgit 1.4.1 From 7a6867e2f8e8b698c08366f79d0c8751b09ce431 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 12 Aug 2020 16:06:30 +0200 Subject: split up __afl_manual_init, added internal AFL_DISABLE_LLVM_INSTRUMENTATION, skipping ctor+ifunc functions for all llvm, code-format --- TODO.md | 2 + docs/Changelog.md | 2 +- examples/aflpp_driver/aflpp_driver.c | 25 ---- examples/aflpp_driver/aflpp_driver_test.c | 16 ++- llvm_mode/LLVMInsTrim.so.cc | 2 + llvm_mode/afl-llvm-common.cc | 195 ++++++++++++++++++--------- llvm_mode/afl-llvm-common.h | 1 + llvm_mode/afl-llvm-lto-instrumentation.so.cc | 94 +------------ llvm_mode/afl-llvm-pass.so.cc | 1 + llvm_mode/afl-llvm-rt.o.c | 38 +++++- src/afl-forkserver.c | 11 +- src/afl-fuzz-bitmap.c | 9 +- src/afl-fuzz-run.c | 10 +- 13 files changed, 203 insertions(+), 203 deletions(-) (limited to 'src') diff --git a/TODO.md b/TODO.md index e81b82a3..f8ef0e0f 100644 --- a/TODO.md +++ b/TODO.md @@ -4,6 +4,8 @@ - AFL_MAP_SIZE for qemu_mode and unicorn_mode - CPU affinity for many cores? There seems to be an issue > 96 cores + - feature for afl-showmap to generate the coverage for all queue entries + - afl-plot to support multiple plot_data ## Further down the road diff --git a/docs/Changelog.md b/docs/Changelog.md index edcdac58..1c5b3f4a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -29,9 +29,9 @@ sending a mail to . - added neverzero counting to trace-pc/pcgard - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) + - skipping ctors and ifuncs for instrumentation - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR for a fixed map address (eg. 0x10000) - - LTO: skipping ctors and ifuncs in fix map address instrumentation - LTO: autodictionary mode is a default - LTO: instrim instrumentation disabled, only classic support used as it is always better diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c index 7d388799..b764338e 100644 --- a/examples/aflpp_driver/aflpp_driver.c +++ b/examples/aflpp_driver/aflpp_driver.c @@ -109,7 +109,6 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. int __afl_sharedmem_fuzzing = 1; extern unsigned int * __afl_fuzz_len; extern unsigned char *__afl_fuzz_ptr; -extern unsigned char *__afl_area_ptr; // extern struct cmp_map *__afl_cmp_map; // libFuzzer interface is thin, so we don't include any libFuzzer headers. @@ -248,28 +247,8 @@ static int ExecuteFilesOnyByOne(int argc, char **argv) { } -__attribute__((constructor(1))) void __afl_protect(void) { - - setenv("__AFL_DEFER_FORKSRV", "1", 1); - __afl_area_ptr = (unsigned char *)mmap( - (void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, - MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); - if ((uint64_t)__afl_area_ptr == -1) - __afl_area_ptr = (unsigned char *)mmap((void *)0x10000, MAX_DUMMY_SIZE, - PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); - if ((uint64_t)__afl_area_ptr == -1) - __afl_area_ptr = - (unsigned char *)mmap(NULL, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); - // __afl_cmp_map = (struct cmp_map *)__afl_area_ptr; - -} - int main(int argc, char **argv) { - fprintf(stderr, "dummy map is at %p\n", __afl_area_ptr); - printf( "======================= INFO =========================\n" "This binary is built for afl++.\n" @@ -307,8 +286,6 @@ int main(int argc, char **argv) { else if (argc > 1) { __afl_sharedmem_fuzzing = 0; - munmap(__afl_area_ptr, MAX_DUMMY_SIZE); // we need to free 0x10000 - __afl_area_ptr = NULL; __afl_manual_init(); return ExecuteFilesOnyByOne(argc, argv); @@ -317,8 +294,6 @@ int main(int argc, char **argv) { assert(N > 0); // if (!getenv("AFL_DRIVER_DONT_DEFER")) - munmap(__afl_area_ptr, MAX_DUMMY_SIZE); - __afl_area_ptr = NULL; __afl_manual_init(); // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization diff --git a/examples/aflpp_driver/aflpp_driver_test.c b/examples/aflpp_driver/aflpp_driver_test.c index e4567bbf..ddc3effb 100644 --- a/examples/aflpp_driver/aflpp_driver_test.c +++ b/examples/aflpp_driver/aflpp_driver_test.c @@ -4,6 +4,16 @@ #include "hash.h" +void __attribute__((noinline)) crashme(const uint8_t *Data, size_t Size) { + + if (Data[0] == 'F') + if (Data[1] == 'A') + if (Data[2] == '$') + if (Data[3] == '$') + if (Data[4] == '$') abort(); + +} + int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { fprintf(stderr, "FUNC crc: %016llx len: %lu\n", @@ -13,11 +23,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { if (Size < 5) return 0; - if (Data[0] == 'F') - if (Data[1] == 'A') - if (Data[2] == '$') - if (Data[3] == '$') - if (Data[4] == '$') abort(); + crashme(Data, Size); return 0; diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc index 4d8c4719..2ad7f171 100644 --- a/llvm_mode/LLVMInsTrim.so.cc +++ b/llvm_mode/LLVMInsTrim.so.cc @@ -256,6 +256,8 @@ struct InsTrim : public ModulePass { u64 total_rs = 0; u64 total_hs = 0; + scanForDangerousFunctions(&M); + for (Function &F : M) { if (debug) { diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc index 0b50c547..f12bbe31 100644 --- a/llvm_mode/afl-llvm-common.cc +++ b/llvm_mode/afl-llvm-common.cc @@ -67,8 +67,11 @@ bool isIgnoreFunction(const llvm::Function *F) { "__libc_csu", "__asan", "__msan", + "__cmplog", + "__sancov", "msan.", "LLVMFuzzer", + "__decide_deferred", "maybe_duplicate_stderr", "discard_output", "close_stdout", @@ -253,21 +256,93 @@ void initInstrumentList() { } +void scanForDangerousFunctions(llvm::Module *M) { + + if (!M) return; + + for (GlobalIFunc &IF : M->ifuncs()) { + + StringRef ifunc_name = IF.getName(); + Constant *r = IF.getResolver(); + StringRef r_name = cast(r->getOperand(0))->getName(); + if (!be_quiet) + fprintf(stderr, + "Info: Found an ifunc with name %s that points to resolver " + "function %s, we will not instrument this, putting it into the " + "block list.\n", + ifunc_name.str().c_str(), r_name.str().c_str()); + denyListFunctions.push_back(r_name.str()); + + } + + GlobalVariable *GV = M->getNamedGlobal("llvm.global_ctors"); + if (GV && !GV->isDeclaration() && !GV->hasLocalLinkage()) { + + ConstantArray *InitList = dyn_cast(GV->getInitializer()); + + if (InitList) { + + for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) { + + if (ConstantStruct *CS = + dyn_cast(InitList->getOperand(i))) { + + if (CS->getNumOperands() >= 2) { + + if (CS->getOperand(1)->isNullValue()) + break; // Found a null terminator, stop here. + + ConstantInt *CI = dyn_cast(CS->getOperand(0)); + int Priority = CI ? CI->getSExtValue() : 0; + + Constant *FP = CS->getOperand(1); + if (ConstantExpr *CE = dyn_cast(FP)) + if (CE->isCast()) FP = CE->getOperand(0); + if (Function *F = dyn_cast(FP)) { + + if (!F->isDeclaration() && + strncmp(F->getName().str().c_str(), "__afl", 5) != 0) { + + if (!be_quiet) + fprintf(stderr, + "Info: Found constructor function %s with prio " + "%u, we will not instrument this, putting it into a " + "block list.\n", + F->getName().str().c_str(), Priority); + denyListFunctions.push_back(F->getName().str()); + + } + + } + + } + + } + + } + + } + + } + +} + bool isInInstrumentList(llvm::Function *F) { + bool return_default = true; + // is this a function with code? If it is external we dont instrument it // anyway and cant be in the the instrument file list. Or if it is ignored. if (!F->size() || isIgnoreFunction(F)) return false; - // if we do not have a the instrument file list return true - if (!allowListFiles.empty() || !allowListFunctions.empty()) { + if (!denyListFiles.empty() || !denyListFunctions.empty()) { - if (!allowListFunctions.empty()) { + if (!denyListFunctions.empty()) { std::string instFunction = F->getName().str(); - for (std::list::iterator it = allowListFunctions.begin(); - it != allowListFunctions.end(); ++it) { + for (std::list::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -281,10 +356,10 @@ bool isInInstrumentList(llvm::Function *F) { if (debug) SAYF(cMGN "[D] " cRST - "Function %s is in the allow function list, " - "instrumenting ... \n", + "Function %s is in the deny function list, " + "not instrumenting ... \n", instFunction.c_str()); - return true; + return false; } @@ -294,7 +369,7 @@ bool isInInstrumentList(llvm::Function *F) { } - if (!allowListFiles.empty()) { + if (!denyListFiles.empty()) { // let's try to get the filename for the function auto bb = &F->getEntryBlock(); @@ -328,8 +403,8 @@ bool isInInstrumentList(llvm::Function *F) { /* Continue only if we know where we actually are */ if (!instFilename.str().empty()) { - for (std::list::iterator it = allowListFiles.begin(); - it != allowListFiles.end(); ++it) { + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -344,10 +419,10 @@ bool isInInstrumentList(llvm::Function *F) { if (debug) SAYF(cMGN "[D] " cRST - "Function %s is in the allowlist (%s), " + "Function %s is in the denylist (%s), not " "instrumenting ... \n", F->getName().str().c_str(), instFilename.str().c_str()); - return true; + return false; } @@ -359,8 +434,6 @@ bool isInInstrumentList(llvm::Function *F) { } - } - #else if (!Loc.isUnknown()) { @@ -373,8 +446,8 @@ bool isInInstrumentList(llvm::Function *F) { /* Continue only if we know where we actually are */ if (!instFilename.str().empty()) { - for (std::list::iterator it = allowListFiles.begin(); - it != allowListFiles.end(); ++it) { + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -387,7 +460,7 @@ bool isInInstrumentList(llvm::Function *F) { if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == 0) { - return true; + return false; } @@ -399,34 +472,34 @@ bool isInInstrumentList(llvm::Function *F) { } - } - #endif - else { + else { - // we could not find out the location. in this case we say it is not - // in the the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will not be " - "instrumented (recompile with -g -O[1-3]).", - F->getName().str().c_str()); - return false; + // we could not find out the location. in this case we say it is not + // in the the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); - } + } - return false; + } } - if (!denyListFiles.empty() || !denyListFunctions.empty()) { + // if we do not have a the instrument file list return true + if (!allowListFiles.empty() || !allowListFunctions.empty()) { - if (!denyListFunctions.empty()) { + return_default = false; + + if (!allowListFunctions.empty()) { std::string instFunction = F->getName().str(); - for (std::list::iterator it = denyListFunctions.begin(); - it != denyListFunctions.end(); ++it) { + for (std::list::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -440,10 +513,10 @@ bool isInInstrumentList(llvm::Function *F) { if (debug) SAYF(cMGN "[D] " cRST - "Function %s is in the deny function list, " - "not instrumenting ... \n", + "Function %s is in the allow function list, " + "instrumenting ... \n", instFunction.c_str()); - return false; + return true; } @@ -453,7 +526,7 @@ bool isInInstrumentList(llvm::Function *F) { } - if (!denyListFiles.empty()) { + if (!allowListFiles.empty()) { // let's try to get the filename for the function auto bb = &F->getEntryBlock(); @@ -487,8 +560,8 @@ bool isInInstrumentList(llvm::Function *F) { /* Continue only if we know where we actually are */ if (!instFilename.str().empty()) { - for (std::list::iterator it = denyListFiles.begin(); - it != denyListFiles.end(); ++it) { + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -503,10 +576,10 @@ bool isInInstrumentList(llvm::Function *F) { if (debug) SAYF(cMGN "[D] " cRST - "Function %s is in the denylist (%s), not " + "Function %s is in the allowlist (%s), " "instrumenting ... \n", F->getName().str().c_str(), instFilename.str().c_str()); - return false; + return true; } @@ -518,22 +591,20 @@ bool isInInstrumentList(llvm::Function *F) { } - } - #else if (!Loc.isUnknown()) { DILocation cDILoc(Loc.getAsMDNode(F->getContext())); unsigned int instLine = cDILoc.getLineNumber(); - StringRef instFilename = cDILoc.getFilename(); + StringRef instFilename = cDILoc.getFilename(); (void)instLine; /* Continue only if we know where we actually are */ if (!instFilename.str().empty()) { - for (std::list::iterator it = denyListFiles.begin(); - it != denyListFiles.end(); ++it) { + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -546,7 +617,7 @@ bool isInInstrumentList(llvm::Function *F) { if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == 0) { - return false; + return true; } @@ -558,27 +629,25 @@ bool isInInstrumentList(llvm::Function *F) { } - } - #endif - else { + else { - // we could not find out the location. in this case we say it is not - // in the the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will be " - "instrumented (recompile with -g -O[1-3]).", - F->getName().str().c_str()); - return true; + // we could not find out the location. in this case we say it is not + // in the the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); + return false; - } + } - return true; + } } - return true; // not reached + return return_default; } diff --git a/llvm_mode/afl-llvm-common.h b/llvm_mode/afl-llvm-common.h index 5b96be43..a1561d9c 100644 --- a/llvm_mode/afl-llvm-common.h +++ b/llvm_mode/afl-llvm-common.h @@ -37,6 +37,7 @@ bool isIgnoreFunction(const llvm::Function *F); void initInstrumentList(); bool isInInstrumentList(llvm::Function *F); unsigned long long int calculateCollisions(uint32_t edges); +void scanForDangerousFunctions(llvm::Module *M); #ifndef IS_EXTERN #define IS_EXTERN diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index fd8e48a7..6bd232ab 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -217,79 +217,9 @@ bool AFLLTOPass::runOnModule(Module &M) { } - */ - - std::vector module_block_list; - - if (map_addr) { - - for (GlobalIFunc &IF : M.ifuncs()) { - - StringRef ifunc_name = IF.getName(); - Constant *r = IF.getResolver(); - StringRef r_name = cast(r->getOperand(0))->getName(); - if (!be_quiet) - fprintf(stderr, - "Warning: Found an ifunc with name %s that points to resolver " - "function %s, we cannot instrument this, putting it into a " - "block list.\n", - ifunc_name.str().c_str(), r_name.str().c_str()); - module_block_list.push_back(r_name.str()); - - } - - GlobalVariable *GV = M.getNamedGlobal("llvm.global_ctors"); - if (GV && !GV->isDeclaration() && !GV->hasLocalLinkage()) { - - ConstantArray *InitList = dyn_cast(GV->getInitializer()); - - if (InitList) { - - for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) { - - if (ConstantStruct *CS = - dyn_cast(InitList->getOperand(i))) { - - if (CS->getNumOperands() >= 2) { - - if (CS->getOperand(1)->isNullValue()) - break; // Found a null terminator, stop here. - - ConstantInt *CI = dyn_cast(CS->getOperand(0)); - int Priority = CI ? CI->getSExtValue() : 0; - - Constant *FP = CS->getOperand(1); - if (ConstantExpr *CE = dyn_cast(FP)) - if (CE->isCast()) FP = CE->getOperand(0); - if (Function *F = dyn_cast(FP)) { - - if (!F->isDeclaration() && - strncmp(F->getName().str().c_str(), "__afl", 5) != 0 && - Priority <= 5) { - - if (!be_quiet) - fprintf(stderr, - "Warning: Found constructor function %s with prio " - "%u, we cannot instrument this, putting it into a " - "block list.\n", - F->getName().str().c_str(), Priority); - module_block_list.push_back(F->getName().str()); - - } - - } - - } - - } - - } - - } + */ - } - - } + scanForDangerousFunctions(&M); /* Instrument all the things! */ @@ -307,26 +237,6 @@ bool AFLLTOPass::runOnModule(Module &M) { if (F.size() < function_minimum_size) continue; if (isIgnoreFunction(&F)) continue; - if (module_block_list.size()) { - - for (auto bname : module_block_list) { - - std::string fname = F.getName().str(); - - if (fname.compare(bname) == 0) { - - if (!be_quiet) - WARNF( - "Skipping instrumentation of dangerous early running function " - "%s", - fname.c_str()); - - } - - } - - } - // the instrument file list check AttributeList Attrs = F.getAttributes(); if (Attrs.hasAttribute(-1, StringRef("skipinstrument"))) { diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc index 618abe48..2ea9fd84 100644 --- a/llvm_mode/afl-llvm-pass.so.cc +++ b/llvm_mode/afl-llvm-pass.so.cc @@ -297,6 +297,7 @@ bool AFLCoverage::runOnModule(Module &M) { /* Instrument all the things! */ int inst_blocks = 0; + scanForDangerousFunctions(&M); for (auto &F : M) { diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index a567593e..dacc46a6 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include @@ -842,9 +844,22 @@ void __afl_manual_init(void) { static u8 init_done; + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) { + + init_done = 1; + is_persistent = 0; + __afl_sharedmem_fuzzing = 0; + if (__afl_area_ptr == NULL) __afl_area_ptr = __afl_area_initial; + + if (getenv("AFL_DEBUG")) + fprintf(stderr, + "DEBUG: disabled instrumenation because of " + "AFL_DISABLE_LLVM_INSTRUMENTATION\n"); + + } + if (!init_done) { - __afl_map_shm(); __afl_start_forkserver(); init_done = 1; @@ -852,11 +867,11 @@ void __afl_manual_init(void) { } -/* Proper initialization routine. */ +/* Initialization of the forkserver - latest possible */ -__attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { +__attribute__((constructor())) void __afl_auto_init(void) { - is_persistent = !!getenv(PERSIST_ENV_VAR); + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; if (getenv(DEFER_ENV_VAR)) return; @@ -864,6 +879,18 @@ __attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { } +/* Initialization of the shmem - earliest possible because of LTO fixed mem. */ + +__attribute__((constructor(0))) void __afl_auto_early(void) { + + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; + + is_persistent = !!getenv(PERSIST_ENV_VAR); + + __afl_map_shm(); + +} + /* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard. It remains non-operational in the traditional, plugin-backed LLVM mode. For more info about 'trace-pc-guard', see llvm_mode/README.md. @@ -912,7 +939,8 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { #else - __afl_area_ptr[*guard] = __afl_area_ptr[*guard] + 1 + (__afl_area_ptr[*guard] == 255 ? 1 : 0); + __afl_area_ptr[*guard] = + __afl_area_ptr[*guard] + 1 + (__afl_area_ptr[*guard] == 255 ? 1 : 0); #endif diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 6819fc8a..8684bcc0 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -58,6 +58,8 @@ static list_t fsrv_list = {.element_prealloc_count = 0}; static void fsrv_exec_child(afl_forkserver_t *fsrv, char **argv) { + if (fsrv->qemu_mode) setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); + execv(fsrv->target_path, argv); } @@ -122,8 +124,8 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { Returns the time passed to read. If the wait times out, returns timeout_ms + 1; Returns 0 if an error occurred (fd closed, signal, ...); */ -static u32 __attribute__ ((hot)) read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, - volatile u8 *stop_soon_p) { +static u32 __attribute__((hot)) +read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, volatile u8 *stop_soon_p) { fd_set readfds; FD_ZERO(&readfds); @@ -322,8 +324,9 @@ static void report_error_and_exit(int error) { cloning a stopped child. So, we just execute once, and then send commands through a pipe. The other part of this logic is in afl-as.h / llvm_mode */ -void __attribute__ ((hot)) afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, - volatile u8 *stop_soon_p, u8 debug_child_output) { +void __attribute__((hot)) +afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, + u8 debug_child_output) { int st_pipe[2], ctl_pipe[2]; s32 status; diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index f6389c06..1b9df624 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -57,7 +57,7 @@ void write_bitmap(afl_state_t *afl) { This function is called after every exec() on a fairly large buffer, so it needs to be fast. We do this in 32-bit and 64-bit flavors. */ -u8 __attribute__ ((hot)) has_new_bits(afl_state_t *afl, u8 *virgin_map) { +u8 __attribute__((hot)) has_new_bits(afl_state_t *afl, u8 *virgin_map) { #ifdef WORD_SIZE_64 @@ -347,7 +347,7 @@ void init_count_class16(void) { #ifdef WORD_SIZE_64 -void __attribute__ ((hot)) classify_counts(afl_forkserver_t *fsrv) { +void __attribute__((hot)) classify_counts(afl_forkserver_t *fsrv) { u64 *mem = (u64 *)fsrv->trace_bits; @@ -376,7 +376,7 @@ void __attribute__ ((hot)) classify_counts(afl_forkserver_t *fsrv) { #else -void __attribute__ ((hot)) classify_counts(afl_forkserver_t *fsrv) { +void __attribute__((hot)) classify_counts(afl_forkserver_t *fsrv) { u32 *mem = (u32 *)fsrv->trace_bits; @@ -534,7 +534,8 @@ static void write_crash_readme(afl_state_t *afl) { save or queue the input test case for further analysis if so. Returns 1 if entry is saved, 0 otherwise. */ -u8 __attribute__ ((hot)) save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { +u8 __attribute__((hot)) +save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (unlikely(len == 0)) { return 0; } diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 97fcb3c8..d3f823c9 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -38,8 +38,8 @@ u64 time_spent_working = 0; /* Execute target application, monitoring for timeouts. Return status information. The called program will update afl->fsrv->trace_bits. */ -fsrv_run_result_t __attribute__ ((hot)) fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, - u32 timeout) { +fsrv_run_result_t __attribute__((hot)) +fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) { #ifdef PROFILING static u64 time_spent_start = 0; @@ -72,7 +72,8 @@ fsrv_run_result_t __attribute__ ((hot)) fuzz_run_target(afl_state_t *afl, afl_fo old file is unlinked and a new one is created. Otherwise, afl->fsrv.out_fd is rewound and truncated. */ -void __attribute__ ((hot)) write_to_testcase(afl_state_t *afl, void *mem, u32 len) { +void __attribute__((hot)) +write_to_testcase(afl_state_t *afl, void *mem, u32 len) { #ifdef _AFL_DOCUMENT_MUTATIONS s32 doc_fd; @@ -858,7 +859,8 @@ abort_trimming: error conditions, returning 1 if it's time to bail out. This is a helper function for fuzz_one(). */ -u8 __attribute__ ((hot)) common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { +u8 __attribute__((hot)) +common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { u8 fault; -- cgit 1.4.1 From 2c5e103278c3266c15226f097e8e9e15267c57d6 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 13 Aug 2020 12:39:18 +0200 Subject: make cmplog deterministic --- docs/Changelog.md | 2 +- src/afl-fuzz-redqueen.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 1c5b3f4a..45d640ea 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -19,7 +19,7 @@ sending a mail to . - eliminated CPU affinity race condition for -S/-M runs - expanded havoc mode added, on no cycle finds add extra splicing and MOpt into the mix - - fixed a bug in redqueen for strings + - fixed a bug in redqueen for strings and made deterministic with -s - llvm_mode: - now supports llvm 12! - support for AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST (previous diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 9716be95..4309098a 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -151,7 +151,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum) { /* Discard if the mutations change the paths or if it is too decremental in speed */ if (cksum != exec_cksum || - (stop_us - start_us > 2 * afl->queue_cur->exec_us)) { + (stop_us - start_us > 2 * afl->queue_cur->exec_us) && likely(!afl->fixed_seed)) { ranges = add_range(ranges, rng->start, rng->start + s / 2); ranges = add_range(ranges, rng->start + s / 2 + 1, rng->end); -- cgit 1.4.1 From c4e52e20c9fa41b95e110d288bc96939e5625761 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 13 Aug 2020 12:58:10 +0200 Subject: fix warning --- src/afl-fuzz-redqueen.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 4309098a..4c0c9155 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -151,7 +151,8 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum) { /* Discard if the mutations change the paths or if it is too decremental in speed */ if (cksum != exec_cksum || - (stop_us - start_us > 2 * afl->queue_cur->exec_us) && likely(!afl->fixed_seed)) { + ((stop_us - start_us > 2 * afl->queue_cur->exec_us) && + likely(!afl->fixed_seed))) { ranges = add_range(ranges, rng->start, rng->start + s / 2); ranges = add_range(ranges, rng->start + s / 2 + 1, rng->end); -- cgit 1.4.1 From 212bb990b7579831baad70735b767dbaf89e9e89 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 13 Aug 2020 21:27:11 +0200 Subject: LTO: apply laf-intel+redqueen/cmplog at link time --- GNUmakefile | 9 ++- README.md | 27 ++++++--- TODO.md | 1 - docs/Changelog.md | 2 + llvm_mode/afl-clang-fast.c | 104 +++++++++++++++++++++++---------- llvm_mode/cmplog-instructions-pass.cc | 4 ++ llvm_mode/cmplog-routines-pass.cc | 4 ++ llvm_mode/compare-transform-pass.so.cc | 3 + llvm_mode/split-compares-pass.so.cc | 4 ++ llvm_mode/split-switches-pass.so.cc | 4 ++ src/afl-showmap.c | 104 ++++++++++++++++++++++++++++----- 11 files changed, 213 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index fe5f8c03..f9020a90 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -97,7 +97,13 @@ ifneq "$(shell uname -m)" "x86_64" endif endif -CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT) +ifdef DEBUG + $(info Compiling DEBUG version of binaries) + CFLAGS += -ggdb3 -O0 -Wall -Wextra -Werror +else + CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT) +endif + override CFLAGS += -g -Wno-pointer-sign -Wno-variadic-macros -Wall -Wextra -Wpointer-arith \ -I include/ -DAFL_PATH=\"$(HELPER_PATH)\" \ -DBIN_PATH=\"$(BIN_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\" @@ -305,6 +311,7 @@ help: @echo "==========================================" @echo STATIC - compile AFL++ static @echo ASAN_BUILD - compiles with memory sanitizer for debug purposes + @echo DEBUG - no optimization, -ggdb3, all warnings and -Werror @echo PROFILING - compile afl-fuzz with profiling information @echo AFL_NO_X86 - if compiling on non-intel/amd platforms @echo "==========================================" diff --git a/README.md b/README.md index 18983832..97c0a0d7 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ These build options exist: * STATIC - compile AFL++ static * ASAN_BUILD - compiles with memory sanitizer for debug purposes +* DEBUG - no optimization, -ggdb3, all warnings and -Werror * PROFILING - compile with profiling information (gprof) * NO_PYTHON - disable python support * AFL_NO_X86 - if compiling on non-intel/amd platforms @@ -509,8 +510,8 @@ fuzz your target. On the same machine - due to the design of how afl++ works - there is a maximum number of CPU cores/threads that are useful, use more and the overall performance -degrades instead. This value depends on the target and the limit is between 48 -and 96 cores/threads per machine. +degrades instead. This value depends on the target, and the limit is between 32 +and 64 cores/threads per machine. There should be one main fuzzer (`-M main` option) and as many secondary fuzzers (eg `-S variant1`) as you have cores that you use. @@ -562,11 +563,18 @@ To have only the summary use the `-s` switch e.g.: `afl-whatsup -s output/` The `paths found` value is a bad indicator how good the coverage is. A better indicator - if you use default llvm instrumentation with at least -version 9 - to use `afl-showmap` on the target with all inputs of the -queue/ directory one after another and collecting the found edge IDs (`-o N.out`), -removing the counters of the edge IDs, making them unique - and there you have -the total number of found instrumented edges. - +version 9 - is to use `afl-showmap` with the collect coverage option `-C` on +the output directory: +``` +$ afl-showmap -C -i out -o /dev/null -- ./target -params @@ +... +[*] Using SHARED MEMORY FUZZING feature. +[*] Target map size: 9960 +[+] Processed 7849 input files. +[+] Captured 4331 tuples (highest value 255, total values 67130596) in '/dev/nul +l'. +[+] A coverage of 4331 edges were achieved out of 9960 existing (43.48%) with 7849 input files. +``` It is even better to check out the exact lines of code that have been reached - and which have not been found so far. @@ -580,6 +588,11 @@ then terminate it. The main node will pick it up and make it available to the other secondary nodes over time. Set `export AFL_NO_AFFINITY=1` if you have no free core. +Note that you in nearly all cases you can never reach full coverage. A lot of +functionality is usually behind options that were not activated or fuzz e.g. +if you fuzz a library to convert image formats and your target is the png to +tiff API then you will not touch any of the other library APIs and features. + #### e) How long to fuzz a target? This is a difficult question. diff --git a/TODO.md b/TODO.md index f8ef0e0f..e74fa1d5 100644 --- a/TODO.md +++ b/TODO.md @@ -4,7 +4,6 @@ - AFL_MAP_SIZE for qemu_mode and unicorn_mode - CPU affinity for many cores? There seems to be an issue > 96 cores - - feature for afl-showmap to generate the coverage for all queue entries - afl-plot to support multiple plot_data ## Further down the road diff --git a/docs/Changelog.md b/docs/Changelog.md index 45d640ea..2c57448b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -32,6 +32,8 @@ sending a mail to . - skipping ctors and ifuncs for instrumentation - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR for a fixed map address (eg. 0x10000) + - LTO: laf-intel and redqueen/cmplogare are now applied at link time + to prevent llvm optimizing away the splits - LTO: autodictionary mode is a default - LTO: instrim instrumentation disabled, only classic support used as it is always better diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index efaba122..10cb3fa3 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -246,33 +246,60 @@ static void edit_params(u32 argc, char **argv, char **envp) { // laf if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-switches-pass.so", obj_path); + if (lto_mode) { + + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-switches-pass.so", obj_path); + + } } if (getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/compare-transform-pass.so", obj_path); + if (lto_mode) { + + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/compare-transform-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/compare-transform-pass.so", obj_path); + + } } if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-compares-pass.so", obj_path); + if (lto_mode) { + + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/split-compares-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-compares-pass.so", obj_path); + + } } @@ -282,24 +309,37 @@ static void edit_params(u32 argc, char **argv, char **envp) { unsetenv("AFL_LD_CALLER"); if (cmplog_mode) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/cmplog-routines-pass.so", obj_path); + if (lto_mode) { - // reuse split switches from laf - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-switches-pass.so", obj_path); + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/cmplog-routines-pass.so", obj_path); + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/cmplog-instructions-pass.so", obj_path); - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/cmplog-instructions-pass.so", obj_path); + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/cmplog-routines-pass.so", obj_path); + + // reuse split switches from laf + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-switches-pass.so", obj_path); + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/cmplog-instructions-pass.so", obj_path); + + } cc_params[cc_par_cnt++] = "-fno-inline"; @@ -314,6 +354,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", AFL_REAL_LD); cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; + /* The current LTO instrim mode is not good, so we disable it if (instrument_mode == INSTRUMENT_CFG) @@ -321,6 +362,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { alloc_printf("-Wl,-mllvm=-load=%s/afl-llvm-lto-instrim.so", obj_path); else */ + cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path); cc_params[cc_par_cnt++] = lto_flag; diff --git a/llvm_mode/cmplog-instructions-pass.cc b/llvm_mode/cmplog-instructions-pass.cc index 7c48d906..6ad7dd06 100644 --- a/llvm_mode/cmplog-instructions-pass.cc +++ b/llvm_mode/cmplog-instructions-pass.cc @@ -284,3 +284,7 @@ static RegisterStandardPasses RegisterCmpLogInstructionsPass( static RegisterStandardPasses RegisterCmpLogInstructionsPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogInstructionsPass); +static RegisterStandardPasses RegisterCmpLogInstructionsPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerCmpLogInstructionsPass); + diff --git a/llvm_mode/cmplog-routines-pass.cc b/llvm_mode/cmplog-routines-pass.cc index a0f8f64f..7e5a1ca6 100644 --- a/llvm_mode/cmplog-routines-pass.cc +++ b/llvm_mode/cmplog-routines-pass.cc @@ -204,3 +204,7 @@ static RegisterStandardPasses RegisterCmpLogRoutinesPass( static RegisterStandardPasses RegisterCmpLogRoutinesPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogRoutinesPass); +static RegisterStandardPasses RegisterCmpLogRoutinesPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerCmpLogRoutinesPass); + diff --git a/llvm_mode/compare-transform-pass.so.cc b/llvm_mode/compare-transform-pass.so.cc index bed3597a..83885037 100644 --- a/llvm_mode/compare-transform-pass.so.cc +++ b/llvm_mode/compare-transform-pass.so.cc @@ -585,3 +585,6 @@ static RegisterStandardPasses RegisterCompTransPass( static RegisterStandardPasses RegisterCompTransPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerCompTransPass); +static RegisterStandardPasses RegisterCompTransPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerCompTransPass); + diff --git a/llvm_mode/split-compares-pass.so.cc b/llvm_mode/split-compares-pass.so.cc index 3630bd8c..ef3cf7f6 100644 --- a/llvm_mode/split-compares-pass.so.cc +++ b/llvm_mode/split-compares-pass.so.cc @@ -1342,3 +1342,7 @@ static RegisterStandardPasses RegisterSplitComparesPass( static RegisterStandardPasses RegisterSplitComparesTransPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitComparesPass); +static RegisterStandardPasses RegisterSplitComparesTransPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerSplitComparesPass); + diff --git a/llvm_mode/split-switches-pass.so.cc b/llvm_mode/split-switches-pass.so.cc index f025df77..3a464391 100644 --- a/llvm_mode/split-switches-pass.so.cc +++ b/llvm_mode/split-switches-pass.so.cc @@ -439,3 +439,7 @@ static RegisterStandardPasses RegisterSplitSwitchesTransPass( static RegisterStandardPasses RegisterSplitSwitchesTransPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitSwitchesTransPass); +static RegisterStandardPasses RegisterSplitSwitchesTransPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerSplitSwitchesTransPass); + diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 0aa116e5..fa9eedc4 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -68,9 +68,11 @@ static char *stdin_file; /* stdin file */ static u8 *in_dir = NULL, /* input folder */ *out_file = NULL, *at_file = NULL; /* Substitution string for @@ */ -static u8 *in_data; /* Input data */ +static u8 *in_data, /* Input data */ + *coverage_map; /* Coverage map */ -static u32 total, highest; /* tuple content information */ +static u64 total; /* tuple content information */ +static u32 tcnt, highest; /* tuple content information */ static u32 in_len, /* Input data length */ arg_offset; /* Total number of execs */ @@ -83,7 +85,8 @@ static u8 quiet_mode, /* Hide non-essential messages? */ cmin_mode, /* Generate output in afl-cmin mode? */ binary_mode, /* Write output as a binary map */ keep_cores, /* Allow coredumps? */ - remove_shm = 1; /* remove shmem? */ + remove_shm = 1, /* remove shmem? */ + collect_coverage; /* collect coverage */ static volatile u8 stop_soon, /* Ctrl-C pressed? */ child_crashed; /* Child crashed? */ @@ -175,6 +178,25 @@ static void at_exit_handler(void) { } +/* Analyze results. */ + +static void analyze_results(afl_forkserver_t *fsrv) { + + u32 i; + for (i = 0; i < map_size; i++) { + + if (fsrv->trace_bits[i]) { + + total += fsrv->trace_bits[i]; + if (fsrv->trace_bits[i] > highest) highest = fsrv->trace_bits[i]; + if (!coverage_map[i]) { coverage_map[i] = 1; } + + } + + } + +} + /* Write results. */ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { @@ -588,9 +610,14 @@ static void usage(u8 *argv0) { " (Not necessary, here for consistency with other afl-* " "tools)\n\n" "Other settings:\n" - " -i dir - process all files in this directory, -o must be a " + " -i dir - process all files in this directory, must be combined " + "with -o.\n" + " With -C, -o is a file, without -C it must be a " "directory\n" " and each bitmap will be written there individually.\n" + " -C - collect coverage, writes all edges to -o and gives a " + "summary\n" + " Must be combined with -i.\n" " -q - sink program's output and don't show messages\n" " -e - show edge coverage only, ignore hit counts\n" " -r - show real tuple values instead of AFL filter values\n" @@ -624,7 +651,6 @@ int main(int argc, char **argv_orig, char **envp) { s32 opt, i; u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0; - u32 tcnt = 0; char **use_argv; char **argv = argv_cpy_dup(argc, argv_orig); @@ -639,10 +665,14 @@ int main(int argc, char **argv_orig, char **envp) { if (getenv("AFL_QUIET") != NULL) { be_quiet = 1; } - while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqZQUWbcrh")) > 0) { + while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqCZQUWbcrh")) > 0) { switch (opt) { + case 'C': + collect_coverage = 1; + break; + case 'i': if (in_dir) { FATAL("Multiple -i options not supported"); } in_dir = optarg; @@ -820,6 +850,13 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !out_file) { usage(argv[0]); } + if (in_dir) { + + if (!out_file && !collect_coverage) + FATAL("for -i you need to specify either -C and/or -o"); + + } + if (fsrv->qemu_mode && !mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; } if (unicorn_mode && !mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; } @@ -910,7 +947,7 @@ int main(int argc, char **argv_orig, char **envp) { if (in_dir) { - DIR * dir_in, *dir_out; + DIR * dir_in, *dir_out = NULL; struct dirent *dir_ent; int done = 0; u8 infile[PATH_MAX], outfile[PATH_MAX]; @@ -924,20 +961,43 @@ int main(int argc, char **argv_orig, char **envp) { fsrv->dev_null_fd = open("/dev/null", O_RDWR); if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); } + // if a queue subdirectory exists switch to that + u8 *dn = alloc_printf("%s/queue", in_dir); + if ((dir_in = opendir(in_dir))) { + + closedir(dir_in); + in_dir = dn; + + } else + + ck_free(dn); + if (!be_quiet) ACTF("Reading from directory '%s'...", in_dir); + if (!(dir_in = opendir(in_dir))) { PFATAL("cannot open directory %s", in_dir); } - if (!(dir_out = opendir(out_file))) { + if (!collect_coverage) { + + if (!(dir_out = opendir(out_file))) { + + if (mkdir(out_file, 0700)) { - if (mkdir(out_file, 0700)) { + PFATAL("cannot create output directory %s", out_file); - PFATAL("cannot create output directory %s", out_file); + } } + } else { + + if ((coverage_map = (u8 *)malloc(map_size)) == NULL) + FATAL("coult not grab memory"); + edges_only = 0; + raw_instr_output = 1; + } u8 *use_dir = "."; @@ -978,6 +1038,7 @@ int main(int argc, char **argv_orig, char **envp) { afl_fsrv_start(fsrv, use_argv, &stop_soon, get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0); + map_size = fsrv->map_size; if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); @@ -1005,7 +1066,8 @@ int main(int argc, char **argv_orig, char **envp) { if (-1 == stat(infile, &statbuf) || !S_ISREG(statbuf.st_mode)) continue; #endif - snprintf(outfile, sizeof(outfile), "%s/%s", out_file, dir_ent->d_name); + if (!collect_coverage) + snprintf(outfile, sizeof(outfile), "%s/%s", out_file, dir_ent->d_name); if (read_file(infile)) { @@ -1019,7 +1081,10 @@ int main(int argc, char **argv_orig, char **envp) { showmap_run_target_forkserver(fsrv, in_data, in_len); ck_free(in_data); - tcnt = write_results_to_file(fsrv, outfile); + if (collect_coverage) + analyze_results(fsrv); + else + tcnt = write_results_to_file(fsrv, outfile); } @@ -1030,6 +1095,13 @@ int main(int argc, char **argv_orig, char **envp) { closedir(dir_in); if (dir_out) { closedir(dir_out); } + if (collect_coverage) { + + memcpy(fsrv->trace_bits, coverage_map, map_size); + tcnt = write_results_to_file(fsrv, out_file); + + } + } else { if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) @@ -1043,8 +1115,14 @@ int main(int argc, char **argv_orig, char **envp) { if (!quiet_mode) { if (!tcnt) { FATAL("No instrumentation detected" cRST); } - OKF("Captured %u tuples (highest value %u, total values %u) in '%s'." cRST, + OKF("Captured %u tuples (highest value %u, total values %llu) in " + "'%s'." cRST, tcnt, highest, total, out_file); + if (collect_coverage) + OKF("A coverage of %u edges were achieved out of %u existing (%.02f%%) " + "with %llu input files.", + tcnt, map_size, ((float)tcnt * 100) / (float)map_size, + fsrv->total_execs); } -- cgit 1.4.1 From b5d1a021efaede5e084418fe552330590ee43641 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 13 Aug 2020 22:34:11 +0200 Subject: fix llvm and afl-showmap --- docs/Changelog.md | 1 + llvm_mode/LLVMInsTrim.so.cc | 2 +- llvm_mode/afl-clang-fast.c | 4 ++++ llvm_mode/afl-llvm-common.cc | 4 ++++ llvm_mode/afl-llvm-pass.so.cc | 2 +- src/afl-showmap.c | 2 +- 6 files changed, 12 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 2c57448b..5044dce5 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -29,6 +29,7 @@ sending a mail to . - added neverzero counting to trace-pc/pcgard - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) + - fixes for llvm 4.0 - skipping ctors and ifuncs for instrumentation - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR for a fixed map address (eg. 0x10000) diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc index 2ad7f171..9812b804 100644 --- a/llvm_mode/LLVMInsTrim.so.cc +++ b/llvm_mode/LLVMInsTrim.so.cc @@ -94,7 +94,7 @@ struct InsTrim : public ModulePass { } -#if LLVM_VERSION_MAJOR >= 4 || \ +#if LLVM_VERSION_MAJOR > 4 || \ (LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1) #define AFL_HAVE_VECTOR_INTRINSICS 1 #endif diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 10cb3fa3..0597ba17 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -371,8 +371,12 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { +#if LLVM_VERSION_MAJOR >= 4 cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default +#else + FATAL("pcguard instrumentation requires llvm 4.0.1+"); +#endif } else { diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc index 4b864cf7..4a94ae89 100644 --- a/llvm_mode/afl-llvm-common.cc +++ b/llvm_mode/afl-llvm-common.cc @@ -260,6 +260,8 @@ void scanForDangerousFunctions(llvm::Module *M) { if (!M) return; +#if LLVM_VERSION_MAJOR >= 4 + for (GlobalIFunc &IF : M->ifuncs()) { StringRef ifunc_name = IF.getName(); @@ -325,6 +327,8 @@ void scanForDangerousFunctions(llvm::Module *M) { } +#endif + } static std::string getSourceName(llvm::Function *F) { diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc index 2ea9fd84..92823187 100644 --- a/llvm_mode/afl-llvm-pass.so.cc +++ b/llvm_mode/afl-llvm-pass.so.cc @@ -112,7 +112,7 @@ uint64_t PowerOf2Ceil(unsigned in) { #endif /* #if LLVM_VERSION_STRING >= "4.0.1" */ -#if LLVM_VERSION_MAJOR >= 4 || \ +#if LLVM_VERSION_MAJOR > 4 || \ (LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1) #define AFL_HAVE_VECTOR_INTRINSICS 1 #endif diff --git a/src/afl-showmap.c b/src/afl-showmap.c index fa9eedc4..47c615d8 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -963,7 +963,7 @@ int main(int argc, char **argv_orig, char **envp) { // if a queue subdirectory exists switch to that u8 *dn = alloc_printf("%s/queue", in_dir); - if ((dir_in = opendir(in_dir))) { + if ((dir_in = opendir(dn)) != NULL) { closedir(dir_in); in_dir = dn; -- cgit 1.4.1 From 83df65a66b8df37d0759bf9b31a61f50234d6c40 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 14 Aug 2020 00:46:15 +0200 Subject: cleaned up maybe_add_auto calls --- include/afl-fuzz.h | 4 +--- include/forkserver.h | 4 ++-- src/afl-forkserver.c | 28 +++++++++++++++++++--------- src/afl-fuzz-extras.c | 8 ++------ src/afl-fuzz-one.c | 8 ++++---- src/afl-fuzz-redqueen.c | 12 ++++++------ src/afl-fuzz-state.c | 5 +++-- 7 files changed, 37 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 51ab0e85..cd6f7173 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -608,8 +608,6 @@ typedef struct afl_state { u32 document_counter; #endif - void *maybe_add_auto; - /* statistics file */ double last_bitmap_cvg, last_stability, last_eps; @@ -911,7 +909,7 @@ u8 has_new_bits(afl_state_t *, u8 *); void load_extras_file(afl_state_t *, u8 *, u32 *, u32 *, u32); void load_extras(afl_state_t *, u8 *); -void maybe_add_auto(void *, u8 *, u32); +void maybe_add_auto(afl_state_t *, u8 *, u32); void save_auto(afl_state_t *); void load_auto(afl_state_t *); void destroy_extras(afl_state_t *); diff --git a/include/forkserver.h b/include/forkserver.h index 717493db..b413b4bf 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -89,9 +89,9 @@ typedef struct afl_forkserver { /* Function to kick off the forkserver child */ void (*init_child_func)(struct afl_forkserver *fsrv, char **argv); - u8 *function_opt; /* for autodictionary: afl ptr */ + u8 *afl_ptr; /* for autodictionary: afl ptr */ - void (*function_ptr)(void *afl_tmp, u8 *mem, u32 len); + void (*autodict_func)(void *afl_ptr, u8 *mem, u32 len); } afl_forkserver_t; diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 8684bcc0..01fc829a 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -324,8 +324,7 @@ static void report_error_and_exit(int error) { cloning a stopped child. So, we just execute once, and then send commands through a pipe. The other part of this logic is in afl-as.h / llvm_mode */ -void __attribute__((hot)) -afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, +void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, u8 debug_child_output) { int st_pipe[2], ctl_pipe[2]; @@ -631,13 +630,18 @@ afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, if ((status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT) { - if (fsrv->function_ptr == NULL || fsrv->function_opt == NULL) { + if (fsrv->autodict_func == NULL || fsrv->afl_ptr == NULL) { // this is not afl-fuzz - we deny and return - if (fsrv->use_shmem_fuzz) + if (fsrv->use_shmem_fuzz) { + status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); - else + + } else { + status = (FS_OPT_ENABLED); + + } if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { FATAL("Writing to forkserver failed."); @@ -650,11 +654,16 @@ afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, if (!be_quiet) { ACTF("Using AUTODICT feature."); } - if (fsrv->use_shmem_fuzz) + if (fsrv->use_shmem_fuzz) { + status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ); - else + + } else { + status = (FS_OPT_ENABLED | FS_OPT_AUTODICT); + } + if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { FATAL("Writing to forkserver failed."); @@ -673,7 +682,8 @@ afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, } - u32 len = status, offset = 0, count = 0; + u32 offset = 0, count = 0; + u32 len = status; u8 *dict = ck_alloc(len); if (dict == NULL) { @@ -704,7 +714,7 @@ afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, while (offset < (u32)status && (u8)dict[offset] + offset < (u32)status) { - fsrv->function_ptr(fsrv->function_opt, dict + offset + 1, + fsrv->autodict_func(fsrv->afl_ptr, dict + offset + 1, (u8)dict[offset]); offset += (1 + dict[offset]); count++; diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 097871c8..2f3a2d53 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -354,13 +354,9 @@ static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) { } /* Maybe add automatic extra. */ -/* Ugly hack: afl state is transfered as u8* because we import data via - afl-forkserver.c - which is shared with other afl tools that do not - have the afl state struct */ -void maybe_add_auto(void *afl_tmp, u8 *mem, u32 len) { +void maybe_add_auto(afl_state_t *afl, u8 *mem, u32 len) { - afl_state_t *afl = (afl_state_t *)afl_tmp; u32 i; /* Allow users to specify that they don't want auto dictionaries. */ @@ -544,7 +540,7 @@ void load_auto(afl_state_t *afl) { if (len >= MIN_AUTO_EXTRA && len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, tmp, len); + maybe_add_auto(afl, tmp, len); } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 452c5298..57b53c9f 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -681,7 +681,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, a_collect, a_len); + maybe_add_auto(afl, a_collect, a_len); } @@ -692,7 +692,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, a_collect, a_len); + maybe_add_auto(afl, a_collect, a_len); } @@ -2882,7 +2882,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, a_collect, a_len); + maybe_add_auto(afl, a_collect, a_len); } @@ -2893,7 +2893,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) { - maybe_add_auto((u8 *)afl, a_collect, a_len); + maybe_add_auto(afl, a_collect, a_len); } diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 4c0c9155..f21dd0b0 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -500,7 +500,7 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { } - maybe_add_auto((u8 *)afl, (u8 *)&v, shape); + maybe_add_auto(afl, (u8 *)&v, shape); u64 rev; switch (shape) { @@ -509,15 +509,15 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { break; case 2: rev = SWAP16((u16)v); - maybe_add_auto((u8 *)afl, (u8 *)&rev, shape); + maybe_add_auto(afl, (u8 *)&rev, shape); break; case 4: rev = SWAP32((u32)v); - maybe_add_auto((u8 *)afl, (u8 *)&rev, shape); + maybe_add_auto(afl, (u8 *)&rev, shape); break; case 8: rev = SWAP64(v); - maybe_add_auto((u8 *)afl, (u8 *)&rev, shape); + maybe_add_auto(afl, (u8 *)&rev, shape); break; } @@ -772,8 +772,8 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { if (afl->pass_stats[key].total == 0) { - maybe_add_auto((u8 *)afl, o->v0, SHAPE_BYTES(h->shape)); - maybe_add_auto((u8 *)afl, o->v1, SHAPE_BYTES(h->shape)); + maybe_add_auto(afl, o->v0, SHAPE_BYTES(h->shape)); + maybe_add_auto(afl, o->v1, SHAPE_BYTES(h->shape)); } diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index e2d62bc6..97e4ee93 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -112,8 +112,9 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->fsrv.use_stdin = 1; afl->fsrv.map_size = map_size; - afl->fsrv.function_opt = (u8 *)afl; - afl->fsrv.function_ptr = &maybe_add_auto; + // afl_state_t is not available in forkserver.c + afl->fsrv.afl_ptr = (void *)afl; + afl->fsrv.autodict_func = (void (*)(void *, u8 *, u32))&maybe_add_auto; afl->cal_cycles = CAL_CYCLES; afl->cal_cycles_long = CAL_CYCLES_LONG; -- cgit 1.4.1 From 69f8c62955ecd494fb21c348511b2b7a0e012274 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 14 Aug 2020 00:46:48 +0200 Subject: code-format --- include/forkserver.h | 2 +- src/afl-forkserver.c | 9 +++++---- src/afl-fuzz-extras.c | 2 +- src/afl-fuzz-state.c | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/include/forkserver.h b/include/forkserver.h index b413b4bf..0a7390ed 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -89,7 +89,7 @@ typedef struct afl_forkserver { /* Function to kick off the forkserver child */ void (*init_child_func)(struct afl_forkserver *fsrv, char **argv); - u8 *afl_ptr; /* for autodictionary: afl ptr */ + u8 *afl_ptr; /* for autodictionary: afl ptr */ void (*autodict_func)(void *afl_ptr, u8 *mem, u32 len); diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 01fc829a..33dfde97 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -324,8 +324,8 @@ static void report_error_and_exit(int error) { cloning a stopped child. So, we just execute once, and then send commands through a pipe. The other part of this logic is in afl-as.h / llvm_mode */ -void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, - u8 debug_child_output) { +void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, + volatile u8 *stop_soon_p, u8 debug_child_output) { int st_pipe[2], ctl_pipe[2]; s32 status; @@ -642,6 +642,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_ status = (FS_OPT_ENABLED); } + if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { FATAL("Writing to forkserver failed."); @@ -658,7 +659,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_ status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ); - } else { + } else { status = (FS_OPT_ENABLED | FS_OPT_AUTODICT); @@ -715,7 +716,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_ (u8)dict[offset] + offset < (u32)status) { fsrv->autodict_func(fsrv->afl_ptr, dict + offset + 1, - (u8)dict[offset]); + (u8)dict[offset]); offset += (1 + dict[offset]); count++; diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 2f3a2d53..d678279d 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -357,7 +357,7 @@ static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) { void maybe_add_auto(afl_state_t *afl, u8 *mem, u32 len) { - u32 i; + u32 i; /* Allow users to specify that they don't want auto dictionaries. */ diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 97e4ee93..d4de91a4 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -114,7 +114,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->fsrv.map_size = map_size; // afl_state_t is not available in forkserver.c afl->fsrv.afl_ptr = (void *)afl; - afl->fsrv.autodict_func = (void (*)(void *, u8 *, u32))&maybe_add_auto; + afl->fsrv.autodict_func = (void (*)(void *, u8 *, u32)) & maybe_add_auto; afl->cal_cycles = CAL_CYCLES; afl->cal_cycles_long = CAL_CYCLES_LONG; -- cgit 1.4.1 From d1bc0207cc6e579fe914dcbb0b70653783b64598 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 14 Aug 2020 01:33:03 +0200 Subject: no longer using alloc for autodict --- include/afl-fuzz.h | 12 +++++- include/alloc-inl.h | 115 -------------------------------------------------- src/afl-fuzz-extras.c | 46 ++++++++------------ 3 files changed, 28 insertions(+), 145 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index cd6f7173..034e8de2 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -172,6 +172,14 @@ struct extra_data { }; +struct auto_extra_data { + + u8 data[MAX_AUTO_EXTRA]; /* Dictionary token data */ + u32 len; /* Dictionary token length */ + u32 hit_cnt; /* Use count in the corpus */ + +}; + /* Fuzzing stages */ enum { @@ -571,8 +579,8 @@ typedef struct afl_state { struct extra_data *extras; /* Extra tokens to fuzz with */ u32 extras_cnt; /* Total number of tokens read */ - struct extra_data *a_extras; /* Automatically selected extras */ - u32 a_extras_cnt; /* Total number of tokens available */ + struct auto_extra_data a_extras[MAX_AUTO_EXTRAS]; /* Automatically selected extras */ + u32 a_extras_cnt; /* Total number of tokens available */ /* afl_postprocess API - Now supported via custom mutators */ diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 832b2de4..0518a8c9 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -176,44 +176,6 @@ static inline u8 *DFL_ck_strdup(u8 *str) { return (u8 *)memcpy(ret, str, size); } - -/* Create a buffer with a copy of a memory block. Returns NULL for zero-sized - or NULL inputs. */ - -static inline void *DFL_ck_memdup(void *mem, u32 size) { - - void *ret; - - if (!mem || !size) { return NULL; } - - ALLOC_CHECK_SIZE(size); - ret = malloc(size); - ALLOC_CHECK_RESULT(ret, size); - - return memcpy(ret, mem, size); - -} - -/* Create a buffer with a block of text, appending a NUL terminator at the end. - Returns NULL for zero-sized or NULL inputs. */ - -static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { - - u8 *ret; - - if (!mem || !size) { return NULL; } - - ALLOC_CHECK_SIZE(size); - ret = (u8 *)malloc(size + 1); - ALLOC_CHECK_RESULT(ret, size); - - memcpy(ret, mem, size); - ret[size] = 0; - - return ret; - -} - /* In non-debug mode, we just do straightforward aliasing of the above functions to user-visible names such as ck_alloc(). */ @@ -222,8 +184,6 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { #define ck_realloc DFL_ck_realloc #define ck_realloc_block DFL_ck_realloc_block #define ck_strdup DFL_ck_strdup - #define ck_memdup DFL_ck_memdup - #define ck_memdup_str DFL_ck_memdup_str #define ck_free DFL_ck_free #define alloc_report() @@ -487,55 +447,6 @@ static inline u8 *DFL_ck_strdup(u8 *str) { return memcpy(ret, str, size); -} - -/* Create a buffer with a copy of a memory block. Returns NULL for zero-sized - or NULL inputs. */ - -static inline void *DFL_ck_memdup(void *mem, u32 size) { - - void *ret; - - if (!mem || !size) return NULL; - - ALLOC_CHECK_SIZE(size); - ret = malloc(size + ALLOC_OFF_TOTAL); - ALLOC_CHECK_RESULT(ret, size); - - ret += ALLOC_OFF_HEAD; - - ALLOC_C1(ret) = ALLOC_MAGIC_C1; - ALLOC_S(ret) = size; - ALLOC_C2(ret) = ALLOC_MAGIC_C2; - - return memcpy(ret, mem, size); - -} - -/* Create a buffer with a block of text, appending a NUL terminator at the end. - Returns NULL for zero-sized or NULL inputs. */ - -static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { - - u8 *ret; - - if (!mem || !size) return NULL; - - ALLOC_CHECK_SIZE(size); - ret = malloc(size + ALLOC_OFF_TOTAL + 1); - ALLOC_CHECK_RESULT(ret, size); - - ret += ALLOC_OFF_HEAD; - - ALLOC_C1(ret) = ALLOC_MAGIC_C1; - ALLOC_S(ret) = size; - ALLOC_C2(ret) = ALLOC_MAGIC_C2; - - memcpy(ret, mem, size); - ret[size] = 0; - - return ret; - } #ifndef DEBUG_BUILD @@ -548,8 +459,6 @@ static inline u8 *DFL_ck_memdup_str(u8 *mem, u32 size) { #define ck_realloc DFL_ck_realloc #define ck_realloc_block DFL_ck_realloc_block #define ck_strdup DFL_ck_strdup - #define ck_memdup DFL_ck_memdup - #define ck_memdup_str DFL_ck_memdup_str #define ck_free DFL_ck_free #define alloc_report() @@ -713,24 +622,6 @@ static inline void *TRK_ck_strdup(u8 *str, const char *file, const char *func, } -static inline void *TRK_ck_memdup(void *mem, u32 size, const char *file, - const char *func, u32 line) { - - void *ret = DFL_ck_memdup(mem, size); - TRK_alloc_buf(ret, file, func, line); - return ret; - -} - -static inline void *TRK_ck_memdup_str(void *mem, u32 size, const char *file, - const char *func, u32 line) { - - void *ret = DFL_ck_memdup_str(mem, size); - TRK_alloc_buf(ret, file, func, line); - return ret; - -} - static inline void TRK_ck_free(void *ptr, const char *file, const char *func, u32 line) { @@ -754,12 +645,6 @@ static inline void TRK_ck_free(void *ptr, const char *file, const char *func, #define ck_strdup(_p1) TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__) - #define ck_memdup(_p1, _p2) \ - TRK_ck_memdup(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) - - #define ck_memdup_str(_p1, _p2) \ - TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) - #define ck_free(_p1) TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__) #endif /* ^!DEBUG_BUILD */ diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index d678279d..94f50394 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -25,23 +25,26 @@ #include "afl-fuzz.h" -/* Helper function for load_extras. */ +/* helper function for auto_extras qsort */ +static int compare_auto_extras_len(const void *ae1, const void *ae2) { + + return ((struct auto_extra_data *)ae1)->len - ((struct auto_extra_data *)ae2)->len; + +} -static int compare_extras_len(const void *p1, const void *p2) { +/* descending order */ - struct extra_data *e1 = (struct extra_data *)p1, - *e2 = (struct extra_data *)p2; +static int compare_auto_extras_use_d(const void *ae1, const void *ae2) { - return e1->len - e2->len; + return ((struct auto_extra_data *)ae2)->hit_cnt - ((struct auto_extra_data *)ae1)->hit_cnt; } -static int compare_extras_use_d(const void *p1, const void *p2) { +/* Helper function for load_extras. */ - struct extra_data *e1 = (struct extra_data *)p1, - *e2 = (struct extra_data *)p2; +static int compare_extras_len(const void *e1, const void *e2) { - return e2->hit_cnt - e1->hit_cnt; + return ((struct extra_data *)e1)->len - ((struct extra_data *)e2)->len; } @@ -371,7 +374,7 @@ void maybe_add_auto(afl_state_t *afl, u8 *mem, u32 len) { } - if (i == len) { return; } + if (i == len || unlikely(len > MAX_AUTO_EXTRA)) { return; } /* Reject builtin interesting values. */ @@ -448,10 +451,7 @@ void maybe_add_auto(afl_state_t *afl, u8 *mem, u32 len) { if (afl->a_extras_cnt < MAX_AUTO_EXTRAS) { - afl->a_extras = ck_realloc_block( - afl->a_extras, (afl->a_extras_cnt + 1) * sizeof(struct extra_data)); - - afl->a_extras[afl->a_extras_cnt].data = ck_memdup(mem, len); + memcpy(afl->a_extras[afl->a_extras_cnt].data, mem, len); afl->a_extras[afl->a_extras_cnt].len = len; ++afl->a_extras_cnt; @@ -459,9 +459,7 @@ void maybe_add_auto(afl_state_t *afl, u8 *mem, u32 len) { i = MAX_AUTO_EXTRAS / 2 + rand_below(afl, (MAX_AUTO_EXTRAS + 1) / 2); - ck_free(afl->a_extras[i].data); - - afl->a_extras[i].data = ck_memdup(mem, len); + memcpy(afl->a_extras[i].data, mem, len); afl->a_extras[i].len = len; afl->a_extras[i].hit_cnt = 0; @@ -471,13 +469,13 @@ sort_a_extras: /* First, sort all auto extras by use count, descending order. */ - qsort(afl->a_extras, afl->a_extras_cnt, sizeof(struct extra_data), - compare_extras_use_d); + qsort(afl->a_extras, afl->a_extras_cnt, sizeof(struct auto_extra_data), + compare_auto_extras_use_d); /* Then, sort the top USE_AUTO_EXTRAS entries by size. */ qsort(afl->a_extras, MIN((u32)USE_AUTO_EXTRAS, afl->a_extras_cnt), - sizeof(struct extra_data), compare_extras_len); + sizeof(struct auto_extra_data), compare_auto_extras_len); } @@ -575,13 +573,5 @@ void destroy_extras(afl_state_t *afl) { ck_free(afl->extras); - for (i = 0; i < afl->a_extras_cnt; ++i) { - - ck_free(afl->a_extras[i].data); - - } - - ck_free(afl->a_extras); - } -- cgit 1.4.1 From ce92adcb9bcaba4894b58a26b2a10b11ef249c0a Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 14 Aug 2020 08:33:36 +0200 Subject: formatting --- llvm_mode/afl-clang-fast.c | 3 ++- src/afl-forkserver.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index e311a65e..37af0dfc 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -1011,7 +1011,8 @@ int main(int argc, char **argv, char **envp) { #ifdef AFL_CLANG_FLTO SAYF( "\nafl-clang-lto specific environment variables:\n" - "AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), e.g. 0x10000\n" + "AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), e.g. " + "0x10000\n" "AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " "global var\n" "AFL_LLVM_LTO_STARTID: from which ID to start counting from for a " diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 33dfde97..25983f26 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -875,7 +875,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, " - the target was compiled with afl-clang-lto and a constructor " "was\n" " instrumented, recompiling without AFL_LLVM_MAP_ADDR might solve " - "your problem\n\n" + "your \n" + " problem\n\n" " - Less likely, there is a horrible bug in the fuzzer. If other " "options\n" -- cgit 1.4.1 From ce513c4f3e97d293e57e0ef90ec9e501871c5644 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 14 Aug 2020 10:10:23 +0200 Subject: fix llvm12 -fuseld warning --- include/afl-fuzz.h | 7 ++++--- include/alloc-inl.h | 1 + llvm_mode/GNUmakefile | 8 +++++++- llvm_mode/afl-clang-fast.c | 10 ++++++++++ src/afl-fuzz-extras.c | 6 ++++-- 5 files changed, 26 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 034e8de2..ca7d10fe 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -174,7 +174,7 @@ struct extra_data { struct auto_extra_data { - u8 data[MAX_AUTO_EXTRA]; /* Dictionary token data */ + u8 data[MAX_AUTO_EXTRA]; /* Dictionary token data */ u32 len; /* Dictionary token length */ u32 hit_cnt; /* Use count in the corpus */ @@ -579,8 +579,9 @@ typedef struct afl_state { struct extra_data *extras; /* Extra tokens to fuzz with */ u32 extras_cnt; /* Total number of tokens read */ - struct auto_extra_data a_extras[MAX_AUTO_EXTRAS]; /* Automatically selected extras */ - u32 a_extras_cnt; /* Total number of tokens available */ + struct auto_extra_data + a_extras[MAX_AUTO_EXTRAS]; /* Automatically selected extras */ + u32 a_extras_cnt; /* Total number of tokens available */ /* afl_postprocess API - Now supported via custom mutators */ diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 0518a8c9..306cc622 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -176,6 +176,7 @@ static inline u8 *DFL_ck_strdup(u8 *str) { return (u8 *)memcpy(ret, str, size); } + /* In non-debug mode, we just do straightforward aliasing of the above functions to user-visible names such as ck_alloc(). */ diff --git a/llvm_mode/GNUmakefile b/llvm_mode/GNUmakefile index 0fa9b12e..57cd9f74 100644 --- a/llvm_mode/GNUmakefile +++ b/llvm_mode/GNUmakefile @@ -206,6 +206,10 @@ AFL_CLANG_FUSELD= ifeq "$(LLVM_LTO)" "1" ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=`command -v ld` -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" AFL_CLANG_FUSELD=1 + $(info echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=ld.lld --ld-path=$(LLVM_BINDIR)/ld.lld -o .test ) + ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=ld.lld --ld-path=$(LLVM_BINDIR)/ld.lld -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + AFL_CLANG_LDPATH=1 + endif else $(warn -fuse-ld is not working, cannot enable LTO mode) LLVM_LTO = 0 @@ -218,7 +222,9 @@ CFLAGS_SAFE := -Wall -g -Wno-pointer-sign -I ../include/ \ -DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \ -DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \ -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \ - -DAFL_REAL_LD=\"$(AFL_REAL_LD)\" -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \ + -DAFL_REAL_LD=\"$(AFL_REAL_LD)\" \ + -DAFL_CLANG_LDPATH=\"$(AFL_CLANG_LDPATH)\" \ + -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \ -DCLANG_BIN=\"$(CLANG_BIN)\" -DCLANGPP_BIN=\"$(CLANGPP_BIN)\" -DUSE_BINDIR=$(USE_BINDIR) -Wno-unused-function override CFLAGS += $(CFLAGS_SAFE) diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 37af0dfc..6e8e4a1b 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -352,7 +352,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { else setenv("AFL_LLVM_LTO_AUTODICTIONARY", "1", 1); +#ifdef AFL_CLANG_LDPATH + u8 *ld_ptr = strrchr(AFL_REAL_LD, '/'); + if (!ld_ptr) ld_ptr = "ld.lld"; + cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", ld_ptr); + cc_params[cc_par_cnt++] = alloc_printf("--ld-path=%s", AFL_REAL_LD); +#else cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", AFL_REAL_LD); +#endif + cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; /* @@ -1013,6 +1021,8 @@ int main(int argc, char **argv, char **envp) { "\nafl-clang-lto specific environment variables:\n" "AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), e.g. " "0x10000\n" + "AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding " + "functions they are in into this file\n" "AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " "global var\n" "AFL_LLVM_LTO_STARTID: from which ID to start counting from for a " diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 94f50394..17f02984 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -28,7 +28,8 @@ /* helper function for auto_extras qsort */ static int compare_auto_extras_len(const void *ae1, const void *ae2) { - return ((struct auto_extra_data *)ae1)->len - ((struct auto_extra_data *)ae2)->len; + return ((struct auto_extra_data *)ae1)->len - + ((struct auto_extra_data *)ae2)->len; } @@ -36,7 +37,8 @@ static int compare_auto_extras_len(const void *ae1, const void *ae2) { static int compare_auto_extras_use_d(const void *ae1, const void *ae2) { - return ((struct auto_extra_data *)ae2)->hit_cnt - ((struct auto_extra_data *)ae1)->hit_cnt; + return ((struct auto_extra_data *)ae2)->hit_cnt - + ((struct auto_extra_data *)ae1)->hit_cnt; } -- cgit 1.4.1 From af14acf2c148b1aef10414d1dd6c929c49abc11e Mon Sep 17 00:00:00 2001 From: root Date: Fri, 14 Aug 2020 14:35:05 +0200 Subject: Revert "Merge branch 'debug' into dev" This reverts commit a7537b5511ad767d2240cf2dc6d3e261daa676f9, reversing changes made to 15e799f7ae666418e75c6a79db833c5316b21f97. --- GNUmakefile | 3 - README.md | 33 ---- afl_driver.cpp | 191 ----------------------- examples/aflpp_driver/aflpp_driver.c | 40 ++--- include/afl-fuzz.h | 36 +---- include/common.h | 1 - include/envs.h | 1 - include/forkserver.h | 2 - qemu_taint/README.md | 42 ------ qemu_taint/build_qemu_taint.sh | 7 - qemu_taint/clean.sh | 3 - src/afl-common.c | 155 ++++++++++++------- src/afl-forkserver.c | 17 +-- src/afl-fuzz-bitmap.c | 62 +------- src/afl-fuzz-init.c | 42 +----- src/afl-fuzz-one.c | 284 ++++------------------------------- src/afl-fuzz-queue.c | 177 +--------------------- src/afl-fuzz-run.c | 99 ++++-------- src/afl-fuzz-state.c | 30 ++-- src/afl-fuzz-stats.c | 5 +- src/afl-fuzz.c | 127 ++-------------- 21 files changed, 223 insertions(+), 1134 deletions(-) delete mode 100644 afl_driver.cpp delete mode 100644 qemu_taint/README.md delete mode 100755 qemu_taint/build_qemu_taint.sh delete mode 100755 qemu_taint/clean.sh (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index ae529ece..f9020a90 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -521,7 +521,6 @@ clean: $(MAKE) -C examples/argv_fuzzing clean $(MAKE) -C qemu_mode/unsigaction clean $(MAKE) -C qemu_mode/libcompcov clean - test -d qemu_taint/qemu && { cd qemu_taint ; ./clean.sh ; } rm -rf qemu_mode/qemu-3.1.1 ifeq "$(IN_REPO)" "1" test -d unicorn_mode/unicornafl && $(MAKE) -C unicorn_mode/unicornafl clean || true @@ -532,7 +531,6 @@ endif deepclean: clean rm -rf qemu_mode/qemu-3.1.1.tar.xz - rm -rf qemu_taint/qemu rm -rf unicorn_mode/unicornafl git reset --hard >/dev/null 2>&1 || true @@ -590,7 +588,6 @@ install: all $(MANPAGES) install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH) rm -f $${DESTDIR}$(BIN_PATH)/afl-as if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi - if [ -f afl-qemu-taint ]; then install -m 755 afl-qemu-taint $${DESTDIR}$(BIN_PATH); fi if [ -f afl-gcc-fast ]; then set e; install -m 755 afl-gcc-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-gcc-fast $${DESTDIR}$(BIN_PATH)/afl-g++-fast; install -m 755 afl-gcc-pass.so afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH); fi if [ -f afl-clang-fast ]; then $(MAKE) -C llvm_mode install; fi if [ -f libdislocator.so ]; then set -e; install -m 755 libdislocator.so $${DESTDIR}$(HELPER_PATH); fi diff --git a/README.md b/README.md index b3dc5e45..97c0a0d7 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,3 @@ -# qemu_taint variant. - -UPDATE: **WORKS NOW** **PLEASE TEST** **:-)** - -## HOWTO - -cd qemu_taint && ./build_qemu_taint.sh - -afl-fuzz -A ... - -## CAVEATS - - * llvm shmem persistent mode does not and can not not work - * MOpt works but totally ignores the taint information, so disabled here - * custom mutators? dunno if they work or not. depends on how they work. - * not tested with qemu_mode - * there are several debug checks to ensure the data is fine which slows down - fuzzing, if the beta experiment runs fine these will be improved and it - will result in quite a speed gain. - -## THE TAINT - -taint can be seen in out/taint/ - -the id:000 mirrors the out/queue entry, except the content it 0x00 for -untainted bytes and '!' for tainted bytes. -If a file has new tainted bytes compared to from which previous entry it -was created then there is a id:000[...].new file where the new bytes are -marked '!'. - -the mutation switches between fuzzing all tainted bytes in one cycle and -only new bytes in the other cycle. - # American Fuzzy Lop plus plus (afl++) AFL++ Logo diff --git a/afl_driver.cpp b/afl_driver.cpp deleted file mode 100644 index 7d6a6fd4..00000000 --- a/afl_driver.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -// Platform detection. Copied from FuzzerInternal.h -#ifdef __linux__ -#define LIBFUZZER_LINUX 1 -#define LIBFUZZER_APPLE 0 -#define LIBFUZZER_NETBSD 0 -#define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 0 -#elif __APPLE__ -#define LIBFUZZER_LINUX 0 -#define LIBFUZZER_APPLE 1 -#define LIBFUZZER_NETBSD 0 -#define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 0 -#elif __NetBSD__ -#define LIBFUZZER_LINUX 0 -#define LIBFUZZER_APPLE 0 -#define LIBFUZZER_NETBSD 1 -#define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 0 -#elif __FreeBSD__ -#define LIBFUZZER_LINUX 0 -#define LIBFUZZER_APPLE 0 -#define LIBFUZZER_NETBSD 0 -#define LIBFUZZER_FREEBSD 1 -#define LIBFUZZER_OPENBSD 0 -#elif __OpenBSD__ -#define LIBFUZZER_LINUX 0 -#define LIBFUZZER_APPLE 0 -#define LIBFUZZER_NETBSD 0 -#define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 1 -#else -#error "Support for your platform has not been implemented" -#endif - -// libFuzzer interface is thin, so we don't include any libFuzzer headers. -int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); -__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); - -// Notify AFL about persistent mode. -static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; -int __afl_persistent_loop(unsigned int); -static volatile char suppress_warning2 = AFL_PERSISTENT[0]; - -// Notify AFL about deferred forkserver. -static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; -void __afl_manual_init(); -static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0]; - -// Input buffer. -static const size_t kMaxAflInputSize = 1024000; -static uint8_t AflInputBuf[kMaxAflInputSize]; - -// Use this optionally defined function to output sanitizer messages even if -// user asks to close stderr. -__attribute__((weak)) void __sanitizer_set_report_fd(void *); - -// Keep track of where stderr content is being written to, so that -// dup_and_close_stderr can use the correct one. -static FILE *output_file = stderr; - -// Experimental feature to use afl_driver without AFL's deferred mode. -// Needs to run before __afl_auto_init. -__attribute__((constructor(0))) static void __decide_deferred_forkserver(void) { - if (getenv("AFL_DRIVER_DONT_DEFER")) { - if (unsetenv("__AFL_DEFER_FORKSRV")) { - perror("Failed to unset __AFL_DEFER_FORKSRV"); - abort(); - } - } -} - -// If the user asks us to duplicate stderr, then do it. -static void maybe_duplicate_stderr() { - char *stderr_duplicate_filename = - getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); - - if (!stderr_duplicate_filename) - return; - - FILE *stderr_duplicate_stream = - freopen(stderr_duplicate_filename, "a+", stderr); - - if (!stderr_duplicate_stream) { - fprintf( - stderr, - "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); - abort(); - } - output_file = stderr_duplicate_stream; -} - -// Most of these I/O functions were inspired by/copied from libFuzzer's code. -static void discard_output(int fd) { - FILE *temp = fopen("/dev/null", "w"); - if (!temp) - abort(); - dup2(fileno(temp), fd); - fclose(temp); -} - -static void close_stdout() { discard_output(STDOUT_FILENO); } - -// Prevent the targeted code from writing to "stderr" but allow sanitizers and -// this driver to do so. -static void dup_and_close_stderr() { - int output_fileno = fileno(output_file); - int output_fd = dup(output_fileno); - if (output_fd <= 0) - abort(); - FILE *new_output_file = fdopen(output_fd, "w"); - if (!new_output_file) - abort(); - if (!__sanitizer_set_report_fd) - return; - __sanitizer_set_report_fd(reinterpret_cast(output_fd)); - discard_output(output_fileno); -} - -// Close stdout and/or stderr if user asks for it. -static void maybe_close_fd_mask() { - char *fd_mask_str = getenv("AFL_DRIVER_CLOSE_FD_MASK"); - if (!fd_mask_str) - return; - int fd_mask = atoi(fd_mask_str); - if (fd_mask & 2) - dup_and_close_stderr(); - if (fd_mask & 1) - close_stdout(); -} - -// Define LLVMFuzzerMutate to avoid link failures for targets that use it -// with libFuzzer's LLVMFuzzerCustomMutator. -size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { - assert(false && "LLVMFuzzerMutate should not be called from afl_driver"); - return 0; -} - -int main(int argc, char **argv) { - printf( - "======================= INFO =========================\n" - "This binary is built for AFL-fuzz.\n" - "To run the target function on individual input(s) execute this:\n" - " %s < INPUT_FILE\n" - "To fuzz with afl-fuzz execute this:\n" - " afl-fuzz [afl-flags] %s [-N]\n" - "afl-fuzz will run N iterations before " - "re-spawning the process (default: 1000)\n" - "======================================================\n", - argv[0], argv[0]); - - maybe_duplicate_stderr(); - maybe_close_fd_mask(); - if (LLVMFuzzerInitialize) - LLVMFuzzerInitialize(&argc, &argv); - // Do any other expensive one-time initialization here. - - int N = 100000; - if (argc == 2 && argv[1][0] == '-') - N = atoi(argv[1] + 1); - else if(argc == 2 && (N = atoi(argv[1])) > 0) - printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N); - - assert(N > 0); - - if (!getenv("AFL_DRIVER_DONT_DEFER")) - __afl_manual_init(); - - // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization - // on the first execution of LLVMFuzzerTestOneInput is ignored. - uint8_t dummy_input[1] = {0}; - LLVMFuzzerTestOneInput(dummy_input, 1); - - while (__afl_persistent_loop(N)) { - ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize); - if (n_read > 0) { - LLVMFuzzerTestOneInput(AflInputBuf, n_read); - } - } - - printf("%s: successfully executed input(s)\n", argv[0]); -} diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c index 8e0b554a..b764338e 100644 --- a/examples/aflpp_driver/aflpp_driver.c +++ b/examples/aflpp_driver/aflpp_driver.c @@ -106,7 +106,10 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. #error "Support for your platform has not been implemented" #endif -int __afl_sharedmem_fuzzing = 0; +int __afl_sharedmem_fuzzing = 1; +extern unsigned int * __afl_fuzz_len; +extern unsigned char *__afl_fuzz_ptr; +// extern struct cmp_map *__afl_cmp_map; // libFuzzer interface is thin, so we don't include any libFuzzer headers. int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); @@ -272,7 +275,6 @@ int main(int argc, char **argv) { // Do any other expensive one-time initialization here. uint8_t dummy_input[64] = {0}; - uint8_t buf[1024000]; memcpy(dummy_input, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT)); memcpy(dummy_input + 32, (void *)AFL_DEFER_FORKSVR, sizeof(AFL_DEFER_FORKSVR)); @@ -283,24 +285,16 @@ int main(int argc, char **argv) { printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N); else if (argc > 1) { - if (!getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) { - - __afl_manual_init(); - - } - + __afl_sharedmem_fuzzing = 0; + __afl_manual_init(); return ExecuteFilesOnyByOne(argc, argv); } assert(N > 0); - if (!getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) { - - fprintf(stderr, "performing manual init\n"); - __afl_manual_init(); - - } + // if (!getenv("AFL_DRIVER_DONT_DEFER")) + __afl_manual_init(); // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization // on the first execution of LLVMFuzzerTestOneInput is ignored. @@ -309,13 +303,25 @@ int main(int argc, char **argv) { int num_runs = 0; while (__afl_persistent_loop(N)) { - ssize_t r = read(0, buf, sizeof(buf)); +#ifdef _DEBUG + fprintf(stderr, "CLIENT crc: %016llx len: %u\n", + hash64(__afl_fuzz_ptr, *__afl_fuzz_len, 0xa5b35705), + *__afl_fuzz_len); + fprintf(stderr, "RECV:"); + for (int i = 0; i < *__afl_fuzz_len; i++) + fprintf(stderr, "%02x", __afl_fuzz_ptr[i]); + fprintf(stderr, "\n"); +#endif + if (*__afl_fuzz_len) { + + num_runs++; + LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len); - if (r > 0) { LLVMFuzzerTestOneInput(buf, r); } + } } - printf("%s: successfully executed input(s)\n", argv[0]); + printf("%s: successfully executed %d input(s)\n", argv[0], num_runs); } diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index ad7b0cd6..ca7d10fe 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -133,10 +133,8 @@ extern s32 struct queue_entry { - u8 * fname; /* File name for the test case */ - u8 * fname_taint; /* File name for taint data */ - u32 len; /* Input length */ - struct queue_entry *prev; /* previous queue entry, if any */ + u8 *fname; /* File name for the test case */ + u32 len; /* Input length */ u8 cal_failed, /* Calibration failed? */ trim_done, /* Trimmed? */ @@ -150,10 +148,7 @@ struct queue_entry { is_ascii; /* Is the input just ascii text? */ u32 bitmap_size, /* Number of bits set in bitmap */ - fuzz_level, /* Number of fuzzing iterations */ - taint_bytes_all, /* Number of tainted bytes */ - taint_bytes_new, /* Number of new tainted bytes */ - taint_bytes_highest; /* highest offset in input */ + fuzz_level; /* Number of fuzzing iterations */ u64 exec_us, /* Execution time (us) */ handicap, /* Number of queue cycles behind */ @@ -385,8 +380,6 @@ typedef struct afl_state { char **argv; /* argv if needed */ - char **argv_taint; /* argv for taint mode */ - /* MOpt: Lots of globals, but mostly for the status UI and other things where it really makes no sense to haul them around as function parameters. */ @@ -438,9 +431,7 @@ typedef struct afl_state { *in_bitmap, /* Input bitmap */ *file_extension, /* File extension */ *orig_cmdline, /* Original command line */ - *infoexec, /* Command to execute on a new crash */ - *taint_input_file, /* fuzz_input_one input file */ - *taint_src, *taint_map; + *infoexec; /* Command to execute on a new crash */ u32 hang_tmout; /* Timeout used for hang det (ms) */ @@ -451,9 +442,7 @@ typedef struct afl_state { custom_only, /* Custom mutator only mode */ python_only, /* Python-only mode */ is_main_node, /* if this is the main node */ - is_secondary_node, /* if this is a secondary instance */ - taint_needs_splode, /* explode fuzz input */ - taint_mode; + is_secondary_node; /* if this is a secondary instance */ u32 stats_update_freq; /* Stats update frequency (execs) */ @@ -514,8 +503,7 @@ typedef struct afl_state { useless_at_start, /* Number of useless starting paths */ var_byte_count, /* Bitmap bytes with var behavior */ current_entry, /* Current queue entry ID */ - havoc_div, /* Cycle count divisor for havoc */ - taint_len, taint_count; + havoc_div; /* Cycle count divisor for havoc */ u64 total_crashes, /* Total number of crashes */ unique_crashes, /* Crashes with unique signatures */ @@ -602,9 +590,6 @@ typedef struct afl_state { char * cmplog_binary; afl_forkserver_t cmplog_fsrv; /* cmplog has its own little forkserver */ - /* Taint mode */ - afl_forkserver_t taint_fsrv; /* taint mode has its own little forkserver */ - /* Custom mutators */ struct custom_mutator *mutator; @@ -856,8 +841,7 @@ struct custom_mutator { }; -void afl_state_init_1(afl_state_t *, uint32_t map_size); -void afl_state_init_2(afl_state_t *, uint32_t map_size); +void afl_state_init(afl_state_t *, uint32_t map_size); void afl_state_deinit(afl_state_t *); /* Set stop_soon flag on all childs, kill all childs */ @@ -903,7 +887,7 @@ void deinit_py(void *); void mark_as_det_done(afl_state_t *, struct queue_entry *); void mark_as_variable(afl_state_t *, struct queue_entry *); void mark_as_redundant(afl_state_t *, struct queue_entry *, u8); -void add_to_queue(afl_state_t *, u8 *, u8 *, u32, struct queue_entry *, u8); +void add_to_queue(afl_state_t *, u8 *, u32, u8); void destroy_queue(afl_state_t *); void update_bitmap_score(afl_state_t *, struct queue_entry *); void cull_queue(afl_state_t *); @@ -913,9 +897,7 @@ u32 calculate_score(afl_state_t *, struct queue_entry *); void write_bitmap(afl_state_t *); u32 count_bits(afl_state_t *, u8 *); -u32 count_bits_len(afl_state_t *, u8 *, u32); u32 count_bytes(afl_state_t *, u8 *); -u32 count_bytes_len(afl_state_t *, u8 *, u32); u32 count_non_255_bytes(afl_state_t *, u8 *); #ifdef WORD_SIZE_64 void simplify_trace(afl_state_t *, u64 *); @@ -993,8 +975,6 @@ void check_if_tty(afl_state_t *); void setup_signal_handlers(void); void save_cmdline(afl_state_t *, u32, char **); void read_foreign_testcases(afl_state_t *, int); -void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, - u8 *mem, u32 len); /* CmpLog */ diff --git a/include/common.h b/include/common.h index c7d57e07..87a7425b 100644 --- a/include/common.h +++ b/include/common.h @@ -55,7 +55,6 @@ extern u8 *doc_path; /* path to documentation dir */ @returns the path, allocating the string */ u8 *find_binary(u8 *fname); -u8 *find_afl_binary(u8 *fname, u8 *own_loc); /* Read a bitmap from file fname to memory This is for the -B option again. */ diff --git a/include/envs.h b/include/envs.h index bd97b9cd..96ae91ba 100644 --- a/include/envs.h +++ b/include/envs.h @@ -123,7 +123,6 @@ static char *afl_environment_variables[] = { "AFL_SKIP_BIN_CHECK", "AFL_SKIP_CPUFREQ", "AFL_SKIP_CRASHES", - "AFL_TAINT_INPUT", "AFL_TMIN_EXACT", "AFL_TMPDIR", "AFL_TOKEN_FILE", diff --git a/include/forkserver.h b/include/forkserver.h index 59a9f150..0a7390ed 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -80,8 +80,6 @@ typedef struct afl_forkserver { u8 qemu_mode; /* if running in qemu mode or not */ - u8 taint_mode; /* if running taint analysis or not */ - u32 *shmem_fuzz_len; /* length of the fuzzing test case */ u8 *shmem_fuzz; /* allocated memory for fuzzing */ diff --git a/qemu_taint/README.md b/qemu_taint/README.md deleted file mode 100644 index 6a7d19af..00000000 --- a/qemu_taint/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# qemu_taint - -First level taint implementation with qemu for linux user mode - -**THIS IS NOT WORKING YET** **WIP** - -## What is this for - -On new queue entries (newly discovered paths into the target) this tainter -is run with the new input and the data gathered which bytes in the input -file are actually touched. - -Only touched bytes are then fuzzed by afl-fuzz - -## How to build - -./build_qemu_taint.sh - -## How to use - -Add the -A flag to afl-fuzz - -## Caveats - -For some targets this is amazing and improves fuzzing a lot, but if a target -copies all input bytes first (e.g. for creating a crc checksum or just to -safely work with the data), then this is not helping at all. - -## Future - -Two fuzz modes for a queue entry which will be switched back and forth: - - 1. fuzz all touched bytes - 2. fuzz only bytes that are newly touched (compared to the one this queue - entry is based on) - -## TODO - - * Direct trim: trim to highest touched byte, that is all we need to do - * add 5-25% dummy bytes to the queue entries? (maybe create a 2nd one?) - * Disable trim? - diff --git a/qemu_taint/build_qemu_taint.sh b/qemu_taint/build_qemu_taint.sh deleted file mode 100755 index b54c3e04..00000000 --- a/qemu_taint/build_qemu_taint.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -test -d qemu || git clone https://github.com/vanhauser-thc/qemu_taint qemu || exit 1 -cd qemu || exit 1 -test -d .git && { git stash ; git pull ; } -cp -fv ../../include/config.h ../../include/types.h . || exit 1 -./build.sh || exit 1 -cp -fv ./afl-qemu-taint ../.. diff --git a/qemu_taint/clean.sh b/qemu_taint/clean.sh deleted file mode 100755 index 10c44cac..00000000 --- a/qemu_taint/clean.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -rm -f afl-qemu-taint qemu/afl-qemu-taint ../afl-qemu-taint -test -d qemu && { cd qemu ; ./clean.sh ; } diff --git a/src/afl-common.c b/src/afl-common.c index cefed8dc..367dec72 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -138,73 +138,62 @@ void argv_cpy_free(char **argv) { } -u8 *find_afl_binary(u8 *fname, u8 *own_loc) { - - u8 *tmp, *rsl, *own_copy, *cp; - - tmp = getenv("AFL_PATH"); - - if (tmp) { - - cp = alloc_printf("%s/%s", tmp, fname); - - if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); } - - return cp; +/* Rewrite argv for QEMU. */ - } +char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { - if (own_loc) { + char **new_argv = ck_alloc(sizeof(char *) * (argc + 4)); + u8 * tmp, *cp = NULL, *rsl, *own_copy; - own_copy = ck_strdup(own_loc); - rsl = strrchr(own_copy, '/'); + memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1)); + new_argv[argc - 1] = NULL; - if (rsl) { + new_argv[2] = *target_path_p; + new_argv[1] = "--"; - *rsl = 0; + /* Now we need to actually find the QEMU binary to put in argv[0]. */ - cp = alloc_printf("%s/%s", own_copy, fname); - ck_free(own_copy); + tmp = getenv("AFL_PATH"); - if (!access(cp, X_OK)) { return cp; } + if (tmp) { - } else { + cp = alloc_printf("%s/afl-qemu-trace", tmp); - ck_free(own_copy); + if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); } - } + *target_path_p = new_argv[0] = cp; + return new_argv; } - cp = alloc_printf("%s/%s", BIN_PATH, fname); - if (!access(cp, X_OK)) { return cp; } + own_copy = ck_strdup(own_loc); + rsl = strrchr(own_copy, '/'); - ck_free(cp); + if (rsl) { - return NULL; + *rsl = 0; -} + cp = alloc_printf("%s/afl-qemu-trace", own_copy); + ck_free(own_copy); -/* Rewrite argv for QEMU. */ + if (!access(cp, X_OK)) { -char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { + *target_path_p = new_argv[0] = cp; + return new_argv; - char **new_argv = ck_alloc(sizeof(char *) * (argc + 4)); - u8 * cp = NULL; + } - memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1)); - new_argv[argc - 1] = NULL; + } else { - new_argv[2] = *target_path_p; - new_argv[1] = "--"; + ck_free(own_copy); - /* Now we need to actually find the QEMU binary to put in argv[0]. */ + } - cp = find_afl_binary("afl-qemu-trace", own_loc); + if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { - if (cp) { + if (cp) { ck_free(cp); } + *target_path_p = new_argv[0] = ck_strdup(BIN_PATH "/afl-qemu-trace"); - *target_path_p = new_argv[0] = cp; return new_argv; } @@ -236,7 +225,7 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { char **new_argv = ck_alloc(sizeof(char *) * (argc + 3)); - u8 * cp = NULL; + u8 * tmp, *cp = NULL, *rsl, *own_copy; memcpy(&new_argv[2], &argv[1], (int)(sizeof(char *)) * (argc - 1)); new_argv[argc - 1] = NULL; @@ -245,16 +234,66 @@ char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { /* Now we need to actually find the QEMU binary to put in argv[0]. */ - cp = find_afl_binary("afl-qemu-trace", own_loc); + tmp = getenv("AFL_PATH"); + + if (tmp) { + + cp = alloc_printf("%s/afl-qemu-trace", tmp); - if (cp) { + if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); } ck_free(cp); - cp = find_afl_binary("afl-wine-trace", own_loc); - if (cp) { + cp = alloc_printf("%s/afl-wine-trace", tmp); - *target_path_p = new_argv[0] = cp; + if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); } + + *target_path_p = new_argv[0] = cp; + return new_argv; + + } + + own_copy = ck_strdup(own_loc); + rsl = strrchr(own_copy, '/'); + + if (rsl) { + + *rsl = 0; + + cp = alloc_printf("%s/afl-qemu-trace", own_copy); + + if (cp && !access(cp, X_OK)) { + + ck_free(cp); + + cp = alloc_printf("%s/afl-wine-trace", own_copy); + + if (!access(cp, X_OK)) { + + *target_path_p = new_argv[0] = cp; + return new_argv; + + } + + } + + ck_free(own_copy); + + } else { + + ck_free(own_copy); + + } + + u8 *ncp = BIN_PATH "/afl-qemu-trace"; + + if (!access(ncp, X_OK)) { + + ncp = BIN_PATH "/afl-wine-trace"; + + if (!access(ncp, X_OK)) { + + *target_path_p = new_argv[0] = ck_strdup(ncp); return new_argv; } @@ -262,21 +301,25 @@ char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { } SAYF("\n" cLRD "[-] " cRST - "Oops, unable to find the afl-qemu-trace and afl-wine-trace binaries.\n" - "The afl-qemu-trace binary must be built separately by following the " - "instructions\n" - "in qemu_mode/README.md. If you already have the binary installed, you " - "may need\n" - "to specify the location via AFL_PATH in the environment.\n\n" + "Oops, unable to find the '%s' binary. The binary must be " + "built\n" + " separately by following the instructions in " + "qemu_mode/README.md. " + "If you\n" + " already have the binary installed, you may need to specify " + "AFL_PATH in the\n" + " environment.\n\n" + " Of course, even without QEMU, afl-fuzz can still work with " "binaries that are\n" " instrumented at compile time with afl-gcc. It is also possible to " "use it as a\n" " traditional non-instrumented fuzzer by specifying '-n' in the " "command " - "line.\n"); + "line.\n", + ncp); - FATAL("Failed to locate 'afl-qemu-trace' and 'afl-wine-trace'."); + FATAL("Failed to locate '%s'.", ncp); } diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 173cc70f..25983f26 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -498,21 +498,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, char pid_buf[16]; sprintf(pid_buf, "%d", fsrv->fsrv_pid); - - if (fsrv->taint_mode) { - - setenv("__AFL_TARGET_PID3", pid_buf, 1); - - } else if (fsrv->cmplog_binary) { - + if (fsrv->cmplog_binary) setenv("__AFL_TARGET_PID2", pid_buf, 1); - - } else { - + else setenv("__AFL_TARGET_PID1", pid_buf, 1); - } - /* Close the unneeded endpoints. */ close(ctl_pipe[0]); @@ -947,7 +937,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { } else { - s32 fd; + s32 fd = fsrv->out_fd; if (fsrv->out_file) { @@ -966,7 +956,6 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { } else { - fd = fsrv->out_fd; lseek(fd, 0, SEEK_SET); } diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index db57061d..1b9df624 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -177,40 +177,6 @@ u32 count_bits(afl_state_t *afl, u8 *mem) { } -u32 count_bits_len(afl_state_t *afl, u8 *mem, u32 len) { - - u32 *ptr = (u32 *)mem; - u32 i = (len >> 2); - u32 ret = 0; - - (void)(afl); - - if (len % 4) i++; - - while (i--) { - - u32 v = *(ptr++); - - /* This gets called on the inverse, virgin bitmap; optimize for sparse - data. */ - - if (v == 0xffffffff) { - - ret += 32; - continue; - - } - - v -= ((v >> 1) & 0x55555555); - v = (v & 0x33333333) + ((v >> 2) & 0x33333333); - ret += (((v + (v >> 4)) & 0xF0F0F0F) * 0x01010101) >> 24; - - } - - return ret; - -} - /* Count the number of bytes set in the bitmap. Called fairly sporadically, mostly to update the status screen or calibrate and examine confirmed new paths. */ @@ -237,32 +203,6 @@ u32 count_bytes(afl_state_t *afl, u8 *mem) { } -u32 count_bytes_len(afl_state_t *afl, u8 *mem, u32 len) { - - u32 *ptr = (u32 *)mem; - u32 i = (len >> 2); - u32 ret = 0; - - (void)(afl); - - if (len % 4) i++; - - while (i--) { - - u32 v = *(ptr++); - - if (!v) { continue; } - if (v & 0x000000ff) { ++ret; } - if (v & 0x0000ff00) { ++ret; } - if (v & 0x00ff0000) { ++ret; } - if (v & 0xff000000) { ++ret; } - - } - - return ret; - -} - /* Count the number of non-255 bytes set in the bitmap. Used strictly for the status screen, several calls per second or so. */ @@ -655,7 +595,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { #endif /* ^!SIMPLE_FILES */ - add_to_queue(afl, queue_fn, mem, len, afl->queue_top, 0); + add_to_queue(afl, queue_fn, len, 0); if (hnb == 2) { diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 359eef85..350a8599 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -712,7 +712,7 @@ void read_testcases(afl_state_t *afl) { if (!access(dfn, F_OK)) { passed_det = 1; } - add_to_queue(afl, fn2, NULL, st.st_size, NULL, passed_det); + add_to_queue(afl, fn2, st.st_size, passed_det); } @@ -771,13 +771,9 @@ void perform_dry_run(afl_state_t *afl) { close(fd); res = calibrate_case(afl, q, use_mem, 0, 1); + ck_free(use_mem); - if (afl->stop_soon) { - - ck_free(use_mem); - return; - - } + if (afl->stop_soon) { return; } if (res == afl->crash_mode || res == FSRV_RUN_NOBITS) { @@ -964,10 +960,6 @@ void perform_dry_run(afl_state_t *afl) { } - /* perform taint gathering on the input seed */ - if (afl->taint_mode) perform_taint_run(afl, q, q->fname, use_mem, q->len); - ck_free(use_mem); - q = q->next; } @@ -1446,10 +1438,6 @@ static void handle_existing_out_dir(afl_state_t *afl) { u8 *orig_q = alloc_printf("%s/queue", afl->out_dir); - u8 *fnt = alloc_printf("%s/taint", afl->out_dir); - mkdir(fnt, 0755); // ignore errors - ck_free(fnt); - afl->in_dir = alloc_printf("%s/_resume", afl->out_dir); rename(orig_q, afl->in_dir); /* Ignore errors */ @@ -1506,20 +1494,6 @@ static void handle_existing_out_dir(afl_state_t *afl) { if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; } ck_free(fn); - if (afl->taint_mode) { - - fn = alloc_printf("%s/taint", afl->out_dir); - mkdir(fn, 0755); // ignore errors - - u8 *fn2 = alloc_printf("%s/taint/.input", afl->out_dir); - unlink(fn2); // ignore errors - ck_free(fn2); - - if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; } - ck_free(fn); - - } - /* All right, let's do out_dir>/crashes/id:* and * out_dir>/hangs/id:*. */ @@ -1747,16 +1721,6 @@ void setup_dirs_fds(afl_state_t *afl) { if (mkdir(tmp, 0700)) { PFATAL("Unable to create '%s'", tmp); } ck_free(tmp); - /* Taint directory if taint_mode. */ - - if (afl->taint_mode) { - - tmp = alloc_printf("%s/taint", afl->out_dir); - if (mkdir(tmp, 0700)) { PFATAL("Unable to create '%s'", tmp); } - ck_free(tmp); - - } - /* Top-level directory for queue metadata used for session resume and related tasks. */ diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 6d52b2b4..57b53c9f 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -458,171 +458,28 @@ u8 fuzz_one_original(afl_state_t *afl) { } - u32 tmp_val = 0; - - if (unlikely(afl->taint_mode)) { - - tmp_val = afl->queue_cycle % 2; // starts with 1 - ret_val = 0; - - if (unlikely(afl->queue_cur->cal_failed && !tmp_val)) goto abandon_entry; - if (unlikely(!afl->skip_deterministic && !afl->queue_cur->passed_det && - !tmp_val)) - goto abandon_entry; - if ((!afl->queue_cur->taint_bytes_new || - afl->queue_cur->taint_bytes_new == afl->queue_cur->len) && - !tmp_val) - goto abandon_entry; - - ret_val = 1; - - s32 dst = 0, i; - temp_len = len = afl->queue_cur->len; - s32 j = 0; // tmp - - fd = open(afl->queue_cur->fname, O_RDONLY); - afl->taint_src = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (fd < 0 || (ssize_t)afl->taint_src == -1) - FATAL("unable to open '%s'", afl->queue_cur->fname); - close(fd); - afl->taint_needs_splode = 1; - - switch (tmp_val) { - - case 1: // fuzz only tainted bytes - - // special case: all or nothing tainted. in this case we act like - // nothing is special. this is not the taint you are looking for ... - if (!afl->queue_cur->taint_bytes_all || - afl->queue_cur->taint_bytes_all == (u32)len) { - - orig_in = in_buf = afl->taint_src; - afl->taint_needs_splode = 0; - break; - - } - - fd = open(afl->taint_input_file, O_RDONLY); - temp_len = len = afl->taint_len = afl->queue_cur->taint_bytes_all; - orig_in = in_buf = - mmap(0, len >= MAX_FILE - 65536 ? MAX_FILE : len + 65536, - PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (fd < 0 || (ssize_t)in_buf == -1) - FATAL("unable to open '%s'", afl->taint_input_file); - close(fd); - - fd = open(afl->queue_cur->fname_taint, O_RDONLY); - afl->taint_map = mmap(0, afl->queue_cur->len, PROT_READ | PROT_WRITE, - MAP_PRIVATE, fd, 0); - if (fd < 0 || (ssize_t)in_buf == -1) - FATAL("unable to open '%s'", afl->queue_cur->fname_taint); - close(fd); - - for (i = 0; i < (s32)afl->queue_cur->len && dst < len; i++) - if (afl->taint_map[i]) in_buf[dst++] = afl->taint_src[i]; - - // FIXME DEBUG TODO XXX - for (i = 0; i < (s32)afl->queue_cur->len; i++) { - - switch (afl->taint_map[i]) { - - case 0x0: - break; - case '!': - j++; - break; - default: - FATAL( - "invalid taint map entry byte 0x%02x at position %d " - "(passed_det:%d)\n", - afl->taint_map[i], i, afl->queue_cur->passed_det); - - } - - } - - if (j != len) - FATAL("different taint values in map vs in queue (%d != %d)", j, len); - - break; - - case 0: // fuzz only newly tainted bytes - - fd = open(afl->taint_input_file, O_RDONLY); - temp_len = len = afl->taint_len = afl->queue_cur->taint_bytes_new; - orig_in = in_buf = - mmap(0, len >= MAX_FILE - 65536 ? MAX_FILE : len + 65536, - PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (fd < 0 || (ssize_t)in_buf == -1) - FATAL("unable to open '%s'", afl->taint_input_file); - close(fd); - - u8 *fn = alloc_printf("%s.new", afl->queue_cur->fname_taint); - if (!fn) FATAL("OOM"); - fd = open(fn, O_RDWR); - afl->taint_map = mmap(0, afl->queue_cur->len, PROT_READ | PROT_WRITE, - MAP_PRIVATE, fd, 0); - if (fd < 0 || (ssize_t)in_buf == -1) - FATAL("unable to open '%s' for %u bytes", fn, len); - close(fd); - ck_free(fn); - - for (i = 0; i < (s32)afl->queue_cur->len && dst < len; i++) - if (afl->taint_map[i]) in_buf[dst++] = afl->taint_src[i]; - - // FIXME DEBUG TODO XXX - for (i = 0; i < (s32)afl->queue_cur->len; i++) { - - switch (afl->taint_map[i]) { - - case 0x0: - break; - case '!': - j++; - break; - default: - FATAL( - "invalid taint map entry byte 0x%02x at position %d " - "(passed_det:%d)\n", - afl->taint_map[i], i, afl->queue_cur->passed_det); - - } - - } - - if (j != len) - FATAL("different taint values in map vs in queue (%d != %d)", j, len); - - break; - - } - - } else { - - /* Map the test case into memory. */ - - fd = open(afl->queue_cur->fname, O_RDONLY); + /* Map the test case into memory. */ - if (unlikely(fd < 0)) { + fd = open(afl->queue_cur->fname, O_RDONLY); - PFATAL("Unable to open '%s'", afl->queue_cur->fname); + if (unlikely(fd < 0)) { - } + PFATAL("Unable to open '%s'", afl->queue_cur->fname); - len = afl->queue_cur->len; + } - orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + len = afl->queue_cur->len; - if (unlikely(orig_in == MAP_FAILED)) { + orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - PFATAL("Unable to mmap '%s' with len %d", afl->queue_cur->fname, len); + if (unlikely(orig_in == MAP_FAILED)) { - } - - close(fd); + PFATAL("Unable to mmap '%s' with len %d", afl->queue_cur->fname, len); } + close(fd); + /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every single byte anyway, so it wouldn't give us any performance or memory usage benefits. */ @@ -645,12 +502,8 @@ u8 fuzz_one_original(afl_state_t *afl) { afl->queue_cur->exec_cksum = 0; - if (unlikely(afl->taint_needs_splode)) - res = calibrate_case(afl, afl->queue_cur, afl->taint_src, - afl->queue_cycle - 1, 0); - else - res = calibrate_case(afl, afl->queue_cur, in_buf, afl->queue_cycle - 1, - 0); + res = + calibrate_case(afl, afl->queue_cur, in_buf, afl->queue_cycle - 1, 0); if (unlikely(res == FSRV_RUN_ERROR)) { @@ -673,8 +526,8 @@ u8 fuzz_one_original(afl_state_t *afl) { * TRIMMING * ************/ - if (unlikely(!afl->non_instrumented_mode && !afl->queue_cur->trim_done && - !afl->disable_trim && !afl->taint_needs_splode)) { + if (!afl->non_instrumented_mode && !afl->queue_cur->trim_done && + !afl->disable_trim) { u8 res = trim_case(afl, afl->queue_cur, in_buf); @@ -711,26 +564,13 @@ u8 fuzz_one_original(afl_state_t *afl) { if (afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized) { - int res; - if (unlikely(afl->taint_needs_splode)) { - - len = afl->queue_cur->len; - memcpy(out_buf, afl->taint_src, len); - res = input_to_state_stage(afl, afl->taint_src, out_buf, len, - afl->queue_cur->exec_cksum); - // just abandon as success - ret_val = 0; - res = 1; - - } else { + if (input_to_state_stage(afl, in_buf, out_buf, len, + afl->queue_cur->exec_cksum)) { - res = input_to_state_stage(afl, in_buf, out_buf, len, - afl->queue_cur->exec_cksum); + goto abandon_entry; } - if (unlikely(res)) { goto abandon_entry; } - } /* Skip right away if -d is given, if it has not been chosen sufficiently @@ -2293,18 +2133,8 @@ havoc_stage: if (actually_clone) { - if (unlikely(afl->taint_needs_splode)) { - - clone_len = choose_block_len(afl, afl->queue_cur->len); - clone_from = - rand_below(afl, afl->queue_cur->len - clone_len + 1); - - } else { - - clone_len = choose_block_len(afl, temp_len); - clone_from = rand_below(afl, temp_len - clone_len + 1); - - } + clone_len = choose_block_len(afl, temp_len); + clone_from = rand_below(afl, temp_len - clone_len + 1); } else { @@ -2326,11 +2156,7 @@ havoc_stage: if (actually_clone) { - if (unlikely(afl->taint_needs_splode)) - memcpy(new_buf + clone_to, afl->taint_src + clone_from, - clone_len); - else - memcpy(new_buf + clone_to, out_buf + clone_from, clone_len); + memcpy(new_buf + clone_to, out_buf + clone_from, clone_len); } else { @@ -2342,7 +2168,7 @@ havoc_stage: } /* Tail */ - memmove(new_buf + clone_to + clone_len, out_buf + clone_to, + memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); @@ -2363,49 +2189,16 @@ havoc_stage: if (temp_len < 2) { break; } - if (unlikely(afl->taint_needs_splode)) { - - copy_len = choose_block_len(afl, afl->queue_cur->len - 1); - copy_from = rand_below(afl, afl->queue_cur->len - copy_len + 1); - copy_to = rand_below(afl, temp_len + 1); - - } else { + copy_len = choose_block_len(afl, temp_len - 1); - copy_len = choose_block_len(afl, temp_len - 1); - copy_from = rand_below(afl, temp_len - copy_len + 1); - copy_to = rand_below(afl, temp_len - copy_len + 1); - - } + copy_from = rand_below(afl, temp_len - copy_len + 1); + copy_to = rand_below(afl, temp_len - copy_len + 1); if (rand_below(afl, 4)) { if (copy_from != copy_to) { - if (unlikely(afl->taint_needs_splode)) { - - if (temp_len >= (s32)(copy_to + copy_len)) { - - memcpy(out_buf + copy_to, afl->taint_src + copy_from, - copy_len); - - } else { - - u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), - copy_to + copy_len); - memcpy(new_buf, in_buf, copy_to); - memcpy(new_buf + copy_to, afl->taint_src + copy_from, - copy_len); - swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); - out_buf = new_buf; - temp_len = copy_to + copy_len; - - } - - } else { - - memmove(out_buf + copy_to, out_buf + copy_from, copy_len); - - } + memmove(out_buf + copy_to, out_buf + copy_from, copy_len); } @@ -2671,17 +2464,10 @@ havoc_stage: splices them together at some offset, then relies on the havoc code to mutate that blob. */ - u32 saved_len; - - if (unlikely(afl->taint_needs_splode)) - saved_len = afl->taint_len; - else - saved_len = afl->queue_cur->len; - retry_splicing: if (afl->use_splicing && splice_cycle++ < SPLICE_CYCLES && - afl->queued_paths > 1 && saved_len > 1) { + afl->queued_paths > 1 && afl->queue_cur->len > 1) { struct queue_entry *target; u32 tid, split_at; @@ -2694,7 +2480,7 @@ retry_splicing: if (in_buf != orig_in) { in_buf = orig_in; - len = saved_len; + len = afl->queue_cur->len; } @@ -2765,8 +2551,6 @@ retry_splicing: ret_val = 0; - goto abandon_entry; - /* we are through with this queue entry - for this iteration */ abandon_entry: @@ -2786,17 +2570,7 @@ abandon_entry: ++afl->queue_cur->fuzz_level; - if (unlikely(afl->taint_needs_splode)) { - - munmap(afl->taint_src, afl->queue_cur->len); - munmap(orig_in, afl->taint_len); - munmap(afl->taint_map, afl->queue_cur->len); - - } else { - - munmap(orig_in, afl->queue_cur->len); - - } + munmap(orig_in, afl->queue_cur->len); return ret_val; diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 43794018..f35df914 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -103,169 +103,6 @@ void mark_as_redundant(afl_state_t *afl, struct queue_entry *q, u8 state) { } -void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname, - u8 *mem, u32 len) { - - u8 * ptr, *fn = fname; - u32 bytes = 0, plen = len; - struct queue_entry *prev = q->prev; - - if (plen % 4) plen = plen + 4 - (len % 4); - - if ((ptr = strrchr(fname, '/')) != NULL) fn = ptr + 1; - q->fname_taint = alloc_printf("%s/taint/%s", afl->out_dir, fn); - - if (q->fname_taint) { - - u8 *save = ck_maybe_grow(BUF_PARAMS(out_scratch), afl->fsrv.map_size); - memcpy(save, afl->taint_fsrv.trace_bits, afl->fsrv.map_size); - - afl->taint_fsrv.map_size = plen; // speed :) - write_to_testcase(afl, mem, len); - if (afl_fsrv_run_target(&afl->taint_fsrv, afl->fsrv.exec_tmout * 4, - &afl->stop_soon) == 0) { - - bytes = q->taint_bytes_all = - count_bytes_len(afl, afl->taint_fsrv.trace_bits, plen); - if (afl->debug) - fprintf(stderr, "Debug: tainted %u out of %u bytes\n", bytes, len); - - /* DEBUG FIXME TODO XXX */ - u32 i; - for (i = 0; i < len; i++) { - - if (afl->taint_fsrv.trace_bits[i] && - afl->taint_fsrv.trace_bits[i] != '!') - FATAL("invalid taint map value %02x at pos %d", - afl->taint_fsrv.trace_bits[i], i); - - } - - if (len < plen) - for (i = len; i < plen; i++) { - - if (afl->taint_fsrv.trace_bits[i]) - FATAL("invalid taint map value %02x in padding at pos %d", - afl->taint_fsrv.trace_bits[i], i); - - } - - } - - // if all is tainted we do not need to write taint data away - if (bytes && bytes < len) { - - // save the bytes away - int w = open(q->fname_taint, O_CREAT | O_WRONLY, 0644); - if (w >= 0) { - - ck_write(w, afl->taint_fsrv.trace_bits, len, q->fname_taint); - close(w); - - // find the highest tainted offset in the input (for trim opt) - s32 i = len; - while (i > 0 && !afl->taint_fsrv.trace_bits[i - 1]) - i--; - q->taint_bytes_highest = i; - - afl->taint_count++; - - } else { - - FATAL("could not create %s", q->fname_taint); - q->taint_bytes_all = bytes = 0; - - } - - // it is possible that there is no main taint file - if the whole file - // is tainted - but a .new taint file if it had new tainted bytes - - // check if there is a previous queue entry and if it had taint - if (bytes && prev && prev->taint_bytes_all && - prev->taint_bytes_all < prev->len) { - - // check if there are new bytes in the taint vs the previous - int r = open(prev->fname_taint, O_RDONLY); - - if (r >= 0) { - - u8 *bufr = mmap(0, prev->len, PROT_READ, MAP_PRIVATE, r, 0); - - if ((ssize_t)bufr != -1) { - - u32 i; - u8 *tmp = ck_maybe_grow(BUF_PARAMS(in_scratch), plen); - memset(tmp, 0, plen); - - for (i = 0; i < len; i++) - if (afl->taint_fsrv.trace_bits[i] && (i >= prev->len || !bufr[i])) - tmp[i] = '!'; - - q->taint_bytes_new = count_bytes_len(afl, tmp, plen); - - if (afl->debug) - fprintf(stderr, "Debug: %u new taint out of %u bytes\n", bytes, - len); - - if (q->taint_bytes_new) { - - u8 *fnw = alloc_printf("%s.new", q->fname_taint); - if (fnw) { - - int w = open(fnw, O_CREAT | O_WRONLY, 0644); - if (w >= 0) { - - ck_write(w, tmp, plen, fnw); - close(w); - - } else { - - FATAL("count not create '%s'", fnw); - q->taint_bytes_new = 0; - - } - - ck_free(fnw); - - } else { - - q->taint_bytes_new = 0; - - } - - } - - munmap(bufr, prev->len); - - } - - close(r); - - } - - } - - } - - memcpy(afl->taint_fsrv.trace_bits, save, afl->fsrv.map_size); - - } - - if (!bytes) { - - q->taint_bytes_highest = q->taint_bytes_all = q->taint_bytes_new = 0; - - if (q->fname_taint) { - - ck_free(q->fname_taint); - q->fname_taint = NULL; - - } - - } - -} - /* check if ascii or UTF-8 */ static u8 check_if_text(struct queue_entry *q) { @@ -375,12 +212,10 @@ static u8 check_if_text(struct queue_entry *q) { /* Append new test case to the queue. */ -void add_to_queue(afl_state_t *afl, u8 *fname, u8 *mem, u32 len, - struct queue_entry *prev_q, u8 passed_det) { +void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { struct queue_entry *q = ck_alloc(sizeof(struct queue_entry)); - q->prev = prev_q; q->fname = fname; q->len = len; q->depth = afl->cur_depth + 1; @@ -419,13 +254,6 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u8 *mem, u32 len, afl->last_path_time = get_cur_time(); - /* trigger the tain gathering if this is not a dry run */ - if (afl->taint_mode && mem) { perform_taint_run(afl, q, fname, mem, len); } - - /* only redqueen currently uses is_ascii */ - if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(q); - - /* run custom mutators afl_custom_queue_new_entry() */ if (afl->custom_mutators_count) { LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { @@ -445,6 +273,9 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u8 *mem, u32 len, } + /* only redqueen currently uses is_ascii */ + if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(q); + } /* Destroy the entire queue. */ diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 5381723d..d3f823c9 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -350,9 +350,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } - if (unlikely(afl->taint_mode)) - q->exec_cksum = 0; - else if (q->exec_cksum) { + if (q->exec_cksum) { memcpy(afl->first_trace, afl->fsrv.trace_bits, afl->fsrv.map_size); hnb = has_new_bits(afl, afl->virgin_bits); @@ -755,65 +753,56 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { while (remove_pos < q->len) { u32 trim_avail = MIN(remove_len, q->len - remove_pos); + u64 cksum; - if (likely((!q->taint_bytes_highest) || - (q->len - trim_avail > q->taint_bytes_highest))) { + write_with_gap(afl, in_buf, q->len, remove_pos, trim_avail); - u64 cksum; + fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); + ++afl->trim_execs; - write_with_gap(afl, in_buf, q->len, remove_pos, trim_avail); + if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; } - fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); - ++afl->trim_execs; - - if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; } - - /* Note that we don't keep track of crashes or hangs here; maybe TODO? - */ - - cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); - - /* If the deletion had no impact on the trace, make it permanent. This - isn't perfect for variable-path inputs, but we're just making a - best-effort pass, so it's not a big deal if we end up with false - negatives every now and then. */ - - if (cksum == q->exec_cksum) { + /* Note that we don't keep track of crashes or hangs here; maybe TODO? + */ - u32 move_tail = q->len - remove_pos - trim_avail; + cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); - q->len -= trim_avail; - len_p2 = next_pow2(q->len); + /* If the deletion had no impact on the trace, make it permanent. This + isn't perfect for variable-path inputs, but we're just making a + best-effort pass, so it's not a big deal if we end up with false + negatives every now and then. */ - memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail, - move_tail); + if (cksum == q->exec_cksum) { - /* Let's save a clean trace, which will be needed by - update_bitmap_score once we're done with the trimming stuff. */ + u32 move_tail = q->len - remove_pos - trim_avail; - if (!needs_write) { + q->len -= trim_avail; + len_p2 = next_pow2(q->len); - needs_write = 1; - memcpy(afl->clean_trace, afl->fsrv.trace_bits, afl->fsrv.map_size); + memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail, + move_tail); - } + /* Let's save a clean trace, which will be needed by + update_bitmap_score once we're done with the trimming stuff. */ - } else { + if (!needs_write) { - remove_pos += remove_len; + needs_write = 1; + memcpy(afl->clean_trace, afl->fsrv.trace_bits, afl->fsrv.map_size); } - /* Since this can be slow, update the screen every now and then. */ - if (!(trim_exec++ % afl->stats_update_freq)) { show_stats(afl); } - ++afl->stage_cur; - } else { remove_pos += remove_len; } + /* Since this can be slow, update the screen every now and then. */ + + if (!(trim_exec++ % afl->stats_update_freq)) { show_stats(afl); } + ++afl->stage_cur; + } remove_len >>= 1; @@ -866,8 +855,6 @@ abort_trimming: } -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - /* Write a modified test case, run program, process results. Handle error conditions, returning 1 if it's time to bail out. This is a helper function for fuzz_one(). */ @@ -877,32 +864,6 @@ common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { u8 fault; - if (unlikely(afl->taint_needs_splode)) { - - s32 new_len = afl->queue_cur->len + len - afl->taint_len; - if (new_len < 4) - new_len = 4; - else if (new_len > MAX_FILE) - new_len = MAX_FILE; - u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), new_len); - - u32 i, taint = 0; - for (i = 0; i < (u32)new_len; i++) { - - if (i >= afl->taint_len || i >= afl->queue_cur->len || afl->taint_map[i]) - new_buf[i] = out_buf[taint++]; - else - new_buf[i] = afl->taint_src[i]; - - } - - swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); - - out_buf = new_buf; - len = new_len; - - } - write_to_testcase(afl, out_buf, len); fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); @@ -950,5 +911,3 @@ common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { } -#undef BUF_PARAMS - diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index a8416eb1..d4de91a4 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -75,7 +75,7 @@ static list_t afl_states = {.element_prealloc_count = 0}; /* Initializes an afl_state_t. */ -void afl_state_init_1(afl_state_t *afl, uint32_t map_size) { +void afl_state_init(afl_state_t *afl, uint32_t map_size) { /* thanks to this memset, growing vars like out_buf and out_size are NULL/0 by default. */ @@ -100,6 +100,16 @@ void afl_state_init_1(afl_state_t *afl, uint32_t map_size) { afl->cpu_aff = -1; /* Selected CPU core */ #endif /* HAVE_AFFINITY */ + afl->virgin_bits = ck_alloc(map_size); + afl->virgin_tmout = ck_alloc(map_size); + afl->virgin_crash = ck_alloc(map_size); + afl->var_bytes = ck_alloc(map_size); + afl->top_rated = ck_alloc(map_size * sizeof(void *)); + afl->clean_trace = ck_alloc(map_size); + afl->clean_trace_custom = ck_alloc(map_size); + afl->first_trace = ck_alloc(map_size); + afl->map_tmp_buf = ck_alloc(map_size); + afl->fsrv.use_stdin = 1; afl->fsrv.map_size = map_size; // afl_state_t is not available in forkserver.c @@ -151,24 +161,6 @@ void afl_state_init_1(afl_state_t *afl, uint32_t map_size) { } -void afl_state_init_2(afl_state_t *afl, uint32_t map_size) { - - afl->shm.map_size = map_size ? map_size : MAP_SIZE; - - afl->virgin_bits = ck_alloc(map_size); - afl->virgin_tmout = ck_alloc(map_size); - afl->virgin_crash = ck_alloc(map_size); - afl->var_bytes = ck_alloc(map_size); - afl->top_rated = ck_alloc(map_size * sizeof(void *)); - afl->clean_trace = ck_alloc(map_size); - afl->clean_trace_custom = ck_alloc(map_size); - afl->first_trace = ck_alloc(map_size); - afl->map_tmp_buf = ck_alloc(map_size); - - afl->fsrv.map_size = map_size; - -} - /*This sets up the environment variables for afl-fuzz into the afl_state * struct*/ diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 0cc06e12..aeb290bd 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -116,7 +116,6 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, "edges_found : %u\n" "var_byte_count : %u\n" "havoc_expansion : %u\n" - "tainted_inputs : %u\n" "afl_banner : %s\n" "afl_version : " VERSION "\n" @@ -150,8 +149,8 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, #else -1, #endif - t_bytes, afl->var_byte_count, afl->expand_havoc, afl->taint_count, - afl->use_banner, afl->unicorn_mode ? "unicorn" : "", + t_bytes, afl->var_byte_count, afl->expand_havoc, afl->use_banner, + afl->unicorn_mode ? "unicorn" : "", afl->fsrv.qemu_mode ? "qemu " : "", afl->non_instrumented_mode ? " non_instrumented " : "", afl->no_forkserver ? "no_fsrv " : "", afl->crash_mode ? "crash " : "", diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 106aa550..5dd092f2 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -53,9 +53,6 @@ static void at_exit() { ptr = getenv("__AFL_TARGET_PID2"); if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL); - ptr = getenv("__AFL_TARGET_PID3"); - if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL); - i = 0; while (list[i] != NULL) { @@ -92,8 +89,6 @@ static void usage(u8 *argv0, int more_help) { " -o dir - output directory for fuzzer findings\n\n" "Execution control settings:\n" - " -A - use first level taint analysis (see " - "qemu_taint/README.md)\n" " -p schedule - power schedules compute a seed's performance score. " "debug = 1; } map_size = get_map_size(); - afl_state_init_1(afl, map_size); + afl_state_init(afl, map_size); afl->debug = debug; afl_fsrv_init(&afl->fsrv); @@ -283,15 +277,10 @@ int main(int argc, char **argv_orig, char **envp) { while ((opt = getopt( argc, argv, - "+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QANUWe:p:s:V:E:L:hRP:")) > 0) { + "+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:")) > 0) { switch (opt) { - case 'A': - afl->taint_mode = 1; - if (!mem_limit_given) { afl->fsrv.mem_limit = MEM_LIMIT_QEMU; } - break; - case 'I': afl->infoexec = optarg; break; @@ -499,7 +488,7 @@ int main(int argc, char **argv_orig, char **envp) { if (!optarg) { FATAL("Wrong usage of -m"); } - if (!strcmp(optarg, "none") || !strcmp(optarg, "0")) { + if (!strcmp(optarg, "none")) { afl->fsrv.mem_limit = 0; break; @@ -829,15 +818,6 @@ int main(int argc, char **argv_orig, char **envp) { } - if (afl->taint_mode && afl->fsrv.map_size < MAX_FILE) { - - real_map_size = map_size; - map_size = MAX_FILE; - - } - - afl_state_init_2(afl, map_size); - if (!mem_limit_given && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260; OKF("afl++ is maintained by Marc \"van Hauser\" Heuse, Heiko \"hexcoder\" " @@ -845,7 +825,8 @@ int main(int argc, char **argv_orig, char **envp) { OKF("afl++ is open source, get it at " "https://github.com/AFLplusplus/AFLplusplus"); OKF("Power schedules from github.com/mboehme/aflfast"); - OKF("Python Mutator from github.com/choller/afl"); + OKF("Python Mutator and llvm_mode instrument file list from " + "github.com/choller/afl"); OKF("MOpt Mutator from github.com/puppet-meteor/MOpt-AFL"); if (afl->sync_id && afl->is_main_node && @@ -891,19 +872,6 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->crash_mode) { FATAL("-C and -n are mutually exclusive"); } if (afl->fsrv.qemu_mode) { FATAL("-Q and -n are mutually exclusive"); } if (afl->unicorn_mode) { FATAL("-U and -n are mutually exclusive"); } - if (afl->taint_mode) { FATAL("-A and -n are mutually exclusive"); } - - } - - if (afl->limit_time_sig != 0 && afl->taint_mode) { - - FATAL("-A and -L are mutually exclusive"); - - } - - if (afl->unicorn_mode != 0 && afl->taint_mode) { - - FATAL("-A and -U are mutually exclusive"); } @@ -1004,7 +972,7 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->afl_env.afl_preload) { - if (afl->fsrv.qemu_mode || afl->taint_mode) { + if (afl->fsrv.qemu_mode) { u8 *qemu_preload = getenv("QEMU_SET_ENV"); u8 *afl_preload = getenv("AFL_PRELOAD"); @@ -1100,13 +1068,6 @@ int main(int argc, char **argv_orig, char **envp) { afl->fsrv.trace_bits = afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); - if (real_map_size && map_size != real_map_size) { - - afl->fsrv.map_size = real_map_size; - if (afl->cmplog_binary) afl->cmplog_fsrv.map_size = real_map_size; - - } - if (!afl->in_bitmap) { memset(afl->virgin_bits, 255, afl->fsrv.map_size); } memset(afl->virgin_tmout, 255, afl->fsrv.map_size); memset(afl->virgin_crash, 255, afl->fsrv.map_size); @@ -1262,6 +1223,7 @@ int main(int argc, char **argv_orig, char **envp) { ACTF("Spawning cmplog forkserver"); afl_fsrv_init_dup(&afl->cmplog_fsrv, &afl->fsrv); + // TODO: this is semi-nice afl->cmplog_fsrv.trace_bits = afl->fsrv.trace_bits; afl->cmplog_fsrv.qemu_mode = afl->fsrv.qemu_mode; afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; @@ -1272,70 +1234,6 @@ int main(int argc, char **argv_orig, char **envp) { } - if (afl->taint_mode) { - - ACTF("Spawning qemu_taint forkserver"); - - u8 *disable = getenv("AFL_DISABLE_LLVM_INSTRUMENTATION"); - setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); - - afl_fsrv_init_dup(&afl->taint_fsrv, &afl->fsrv); - afl->taint_fsrv.taint_mode = 1; - afl->taint_fsrv.trace_bits = afl->fsrv.trace_bits; - - ck_free(afl->taint_fsrv.target_path); - afl->argv_taint = ck_alloc(sizeof(char *) * (argc + 4 - optind)); - afl->taint_fsrv.target_path = find_afl_binary("afl-qemu-taint", argv[0]); - afl->argv_taint[0] = find_afl_binary("afl-qemu-taint", argv[0]); - if (!afl->argv_taint[0]) - FATAL( - "Cannot find 'afl-qemu-taint', read qemu_taint/README.md on how to " - "build it."); - u32 idx = optind - 1, offset = 0; - do { - - idx++; - offset++; - afl->argv_taint[offset] = argv[idx]; - - } while (argv[idx] != NULL); - - if (afl->fsrv.use_stdin) - unsetenv("AFL_TAINT_INPUT"); - else - setenv("AFL_TAINT_INPUT", afl->fsrv.out_file, 1); - afl_fsrv_start(&afl->taint_fsrv, afl->argv_taint, &afl->stop_soon, - afl->afl_env.afl_debug_child_output); - - afl->taint_input_file = alloc_printf("%s/taint/.input", afl->out_dir); - int fd = open(afl->taint_input_file, O_CREAT | O_TRUNC | O_RDWR, 0644); - if (fd < 0) - FATAL("Cannot create taint inpu file '%s'", afl->taint_input_file); - lseek(fd, MAX_FILE, SEEK_SET); - ck_write(fd, "\0", 1, afl->taint_input_file); - - if (!disable) unsetenv("AFL_DISABLE_LLVM_INSTRUMENTATION"); - - OKF("Taint forkserver successfully started"); - - const rlim_t kStackSize = 128L * 1024L * 1024L; // min stack size = 128 Mb - struct rlimit rl; - rl.rlim_cur = kStackSize; - if (getrlimit(RLIMIT_STACK, &rl) != 0) - WARNF("Setting a higher stack size failed!"); - - #define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - u8 *tmp1 = ck_maybe_grow(BUF_PARAMS(eff), MAX_FILE + 4096); - u8 *tmp2 = ck_maybe_grow(BUF_PARAMS(ex), MAX_FILE + 4096); - u8 *tmp3 = ck_maybe_grow(BUF_PARAMS(in_scratch), MAX_FILE + 4096); - u8 *tmp4 = ck_maybe_grow(BUF_PARAMS(out), MAX_FILE + 4096); - u8 *tmp5 = ck_maybe_grow(BUF_PARAMS(out_scratch), MAX_FILE + 4096); - #undef BUF_PARAMS - if (!tmp1 || !tmp2 || !tmp3 || !tmp4 || !tmp5) - FATAL("memory issues. me hungry, feed me!"); - - } - perform_dry_run(afl); cull_queue(afl); @@ -1410,7 +1308,7 @@ int main(int argc, char **argv_orig, char **envp) { break; case 1: if (afl->limit_time_sig == 0 && !afl->custom_only && - !afl->python_only && !afl->taint_mode) { + !afl->python_only) { afl->limit_time_sig = -1; afl->limit_time_puppet = 0; @@ -1598,11 +1496,8 @@ stop_fuzzing: } - if (afl->cmplog_binary) afl_fsrv_deinit(&afl->cmplog_fsrv); - if (afl->taint_mode) afl_fsrv_deinit(&afl->taint_fsrv); afl_fsrv_deinit(&afl->fsrv); if (afl->orig_cmdline) { ck_free(afl->orig_cmdline); } - if (afl->argv_taint) { ck_free(afl->argv_taint); } ck_free(afl->fsrv.target_path); ck_free(afl->fsrv.out_file); ck_free(afl->sync_id); -- cgit 1.4.1 From 2f28ecd3a522faff6bcdf42b8cb0fd8cd3dc28ce Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 15 Aug 2020 20:51:57 +0200 Subject: more unlikely --- src/afl-fuzz-one.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 57b53c9f..5a4ac8ef 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -526,8 +526,8 @@ u8 fuzz_one_original(afl_state_t *afl) { * TRIMMING * ************/ - if (!afl->non_instrumented_mode && !afl->queue_cur->trim_done && - !afl->disable_trim) { + if (unlikely(!afl->non_instrumented_mode && !afl->queue_cur->trim_done && + !afl->disable_trim)) { u8 res = trim_case(afl, afl->queue_cur, in_buf); @@ -562,7 +562,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (unlikely(perf_score == 0)) { goto abandon_entry; } - if (afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized) { + if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { if (input_to_state_stage(afl, in_buf, out_buf, len, afl->queue_cur->exec_cksum)) { @@ -591,8 +591,9 @@ u8 fuzz_one_original(afl_state_t *afl) { /* Skip deterministic fuzzing if exec path checksum puts this out of scope for this main instance. */ - if (afl->main_node_max && (afl->queue_cur->exec_cksum % afl->main_node_max) != - afl->main_node_id - 1) { + if (unlikely(afl->main_node_max && + (afl->queue_cur->exec_cksum % afl->main_node_max) != + afl->main_node_id - 1)) { goto custom_mutator_stage; @@ -2753,7 +2754,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { orig_perf = perf_score = calculate_score(afl, afl->queue_cur); - if (afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized) { + if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { if (input_to_state_stage(afl, in_buf, out_buf, len, afl->queue_cur->exec_cksum)) { -- cgit 1.4.1 From 43214d6b46643f70fc7979dd37a23bda76ed3171 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 15 Aug 2020 22:10:28 +0200 Subject: more likely --- src/afl-fuzz-one.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 5a4ac8ef..0a4be320 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -95,7 +95,7 @@ static u32 choose_block_len(afl_state_t *afl, u32 limit) { default: - if (rand_below(afl, 10)) { + if (likely(rand_below(afl, 10))) { min_value = HAVOC_BLK_MEDIUM; max_value = HAVOC_BLK_LARGE; @@ -421,7 +421,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (((afl->queue_cur->was_fuzzed > 0 || afl->queue_cur->fuzz_level > 0) || !afl->queue_cur->favored) && - rand_below(afl, 100) < SKIP_TO_NEW_PROB) { + likely(rand_below(afl, 100) < SKIP_TO_NEW_PROB)) { return 1; @@ -438,11 +438,11 @@ u8 fuzz_one_original(afl_state_t *afl) { if (afl->queue_cycle > 1 && (afl->queue_cur->fuzz_level == 0 || afl->queue_cur->was_fuzzed)) { - if (rand_below(afl, 100) < SKIP_NFAV_NEW_PROB) { return 1; } + if (likely(rand_below(afl, 100) < SKIP_NFAV_NEW_PROB)) { return 1; } } else { - if (rand_below(afl, 100) < SKIP_NFAV_OLD_PROB) { return 1; } + if (likely(rand_below(afl, 100) < SKIP_NFAV_OLD_PROB)) { return 1; } } @@ -2132,7 +2132,7 @@ havoc_stage: u32 clone_from, clone_to, clone_len; u8 *new_buf; - if (actually_clone) { + if (likely(actually_clone)) { clone_len = choose_block_len(afl, temp_len); clone_from = rand_below(afl, temp_len - clone_len + 1); @@ -2155,7 +2155,7 @@ havoc_stage: /* Inserted part */ - if (actually_clone) { + if (likely(actually_clone)) { memcpy(new_buf + clone_to, out_buf + clone_from, clone_len); @@ -2195,7 +2195,7 @@ havoc_stage: copy_from = rand_below(afl, temp_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); - if (rand_below(afl, 4)) { + if (likely(rand_below(afl, 4))) { if (copy_from != copy_to) { -- cgit 1.4.1 From 7470b475a9b5e65afa78ca493867d8c980bd66db Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 18 Aug 2020 00:50:52 +0200 Subject: Reworked maybe_grow to take a single ptr, renamed to afl_realloc (#505) * maybe_grow takes a single ptr * fixed use_deflate * reworked maybe_grow_bufsize * helper to access underlying buf * remove redundant realloc_block * code format * fixes * added unit tests * renamed maybe_grow to afl_realloc * BUF_PARAMS -> AFL_BUF_PARAM --- custom_mutators/radamsa/custom_mutator_helpers.h | 2 +- examples/afl_network_proxy/afl-network-server.c | 34 +++-- examples/custom_mutators/custom_mutator_helpers.h | 4 +- include/afl-fuzz.h | 25 ++- include/alloc-inl.h | 177 ++++++++++------------ src/afl-fuzz-extras.c | 12 +- src/afl-fuzz-mutators.c | 3 +- src/afl-fuzz-one.c | 79 ++++++---- src/afl-fuzz-python.c | 20 ++- src/afl-fuzz-queue.c | 7 +- src/afl-fuzz-redqueen.c | 8 +- src/afl-fuzz-run.c | 7 +- src/afl-fuzz-state.c | 14 +- test/unittests/unit_maybe_alloc.c | 109 +++++++++---- 14 files changed, 272 insertions(+), 229 deletions(-) (limited to 'src') diff --git a/custom_mutators/radamsa/custom_mutator_helpers.h b/custom_mutators/radamsa/custom_mutator_helpers.h index 0848321f..e23c0b6a 100644 --- a/custom_mutators/radamsa/custom_mutator_helpers.h +++ b/custom_mutators/radamsa/custom_mutator_helpers.h @@ -324,7 +324,7 @@ static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) { } /* Swaps buf1 ptr and buf2 ptr, as well as their sizes */ -static inline void swap_bufs(void **buf1, size_t *size1, void **buf2, +static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2, size_t *size2) { void * scratch_buf = *buf1; diff --git a/examples/afl_network_proxy/afl-network-server.c b/examples/afl_network_proxy/afl-network-server.c index ab7874fd..c70fd47d 100644 --- a/examples/afl_network_proxy/afl-network-server.c +++ b/examples/afl_network_proxy/afl-network-server.c @@ -73,9 +73,8 @@ static u8 *in_file, /* Minimizer input test case */ static u8 *in_data; /* Input data for trimming */ static u8 *buf2; -static s32 in_len; -static u32 map_size = MAP_SIZE; -static size_t buf2_len; +static s32 in_len; +static u32 map_size = MAP_SIZE; static volatile u8 stop_soon; /* Ctrl-C pressed? */ @@ -272,7 +271,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { setenv("QEMU_SET_ENV", buf, 1); - ck_free(buf); + afl_free(buf); } else { @@ -343,7 +342,7 @@ static void usage(u8 *argv0) { } -int recv_testcase(int s, void **buf, size_t *max_len) { +int recv_testcase(int s, void **buf) { u32 size; s32 ret; @@ -358,7 +357,8 @@ int recv_testcase(int s, void **buf, size_t *max_len) { if ((size & 0xff000000) != 0xff000000) { - *buf = ck_maybe_grow(buf, max_len, size); + *buf = afl_realloc((void **)&buf, size); + if (unlikely(!buf)) { PFATAL("Alloc"); } received = 0; // fprintf(stderr, "unCOMPRESS (%u)\n", size); while (received < size && @@ -370,7 +370,8 @@ int recv_testcase(int s, void **buf, size_t *max_len) { #ifdef USE_DEFLATE u32 clen; size -= 0xff000000; - *buf = ck_maybe_grow(buf, max_len, size); + *buf = afl_realloc((void **)&buf, size); + if (unlikely(!buf)) { PFATAL("Alloc"); } received = 0; while (received < 4 && (ret = recv(s, &clen + received, 4 - received, 0)) > 0) @@ -379,15 +380,15 @@ int recv_testcase(int s, void **buf, size_t *max_len) { // fprintf(stderr, "received clen information of %d\n", clen); if (clen < 1) FATAL("did not receive valid compressed len information: %u", clen); - buf2 = ck_maybe_grow((void **)&buf2, &buf2_len, clen); + buf2 = afl_realloc((void **)&buf2, clen); + if (unlikely(!buf2)) { PFATAL("Alloc"); } received = 0; while (received < clen && (ret = recv(s, buf2 + received, clen - received, 0)) > 0) received += ret; if (received != clen) FATAL("did not receive compressed information"); if (libdeflate_deflate_decompress(decompressor, buf2, clen, (char *)*buf, - *max_len, - &received) != LIBDEFLATE_SUCCESS) + size, &received) != LIBDEFLATE_SUCCESS) FATAL("decompression failed"); // fprintf(stderr, "DECOMPRESS (%u->%u):\n", clen, received); // for (u32 i = 0; i < clen; i++) fprintf(stderr, "%02x", buf2[i]); @@ -413,7 +414,6 @@ int recv_testcase(int s, void **buf, size_t *max_len) { int main(int argc, char **argv_orig, char **envp) { s32 opt, s, sock, on = 1, port = -1; - size_t max_len = 0; u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0; char **use_argv; struct sockaddr_in6 serveraddr, clientaddr; @@ -568,7 +568,8 @@ int main(int argc, char **argv_orig, char **envp) { sharedmem_t shm = {0}; fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); - in_data = ck_maybe_grow((void **)&in_data, &max_len, 65536); + in_data = afl_realloc((void **)&in_data, 65536); + if (unlikely(!in_data)) { PFATAL("Alloc"); } atexit(at_exit_handler); setup_signal_handlers(); @@ -639,7 +640,8 @@ int main(int argc, char **argv_orig, char **envp) { #ifdef USE_DEFLATE compressor = libdeflate_alloc_compressor(1); decompressor = libdeflate_alloc_decompressor(); - buf2 = ck_maybe_grow((void **)&buf2, &buf2_len, map_size + 16); + buf2 = afl_realloc((void **)&buf2, map_size + 16); + if (unlikely(!buf2)) { PFATAL("alloc"); } lenptr = (u32 *)(buf2 + 4); fprintf(stderr, "Compiled with compression support\n"); #endif @@ -664,7 +666,7 @@ int main(int argc, char **argv_orig, char **envp) { #endif - while ((in_len = recv_testcase(s, (void **)&in_data, &max_len)) > 0) { + while ((in_len = recv_testcase(s, (void **)&in_data)) > 0) { // fprintf(stderr, "received %u\n", in_len); (void)run_target(fsrv, use_argv, in_data, in_len, 1); @@ -697,9 +699,9 @@ int main(int argc, char **argv_orig, char **envp) { afl_shm_deinit(&shm); afl_fsrv_deinit(fsrv); if (fsrv->target_path) { ck_free(fsrv->target_path); } - if (in_data) { ck_free(in_data); } + afl_free(in_data); #if USE_DEFLATE - if (buf2) { ck_free(buf2); } + afl_free(buf2); libdeflate_free_compressor(compressor); libdeflate_free_decompressor(decompressor); #endif diff --git a/examples/custom_mutators/custom_mutator_helpers.h b/examples/custom_mutators/custom_mutator_helpers.h index 0848321f..ad5acb08 100644 --- a/examples/custom_mutators/custom_mutator_helpers.h +++ b/examples/custom_mutators/custom_mutator_helpers.h @@ -324,8 +324,8 @@ static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) { } /* Swaps buf1 ptr and buf2 ptr, as well as their sizes */ -static inline void swap_bufs(void **buf1, size_t *size1, void **buf2, - size_t *size2) { +static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2, + size_t *size2) { void * scratch_buf = *buf1; size_t scratch_size = *size1; diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index ca7d10fe..dca395aa 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -126,6 +126,9 @@ #define STAGE_BUF_SIZE (64) /* usable size for stage name buf in afl_state */ +// Little helper to access the ptr to afl->##name_buf - for use in afl_realloc. +#define AFL_BUF_PARAM(name) ((void **)&afl->name##_buf) + extern s8 interesting_8[INTERESTING_8_LEN]; extern s16 interesting_16[INTERESTING_8_LEN + INTERESTING_16_LEN]; extern s32 @@ -572,7 +575,6 @@ typedef struct afl_state { // growing buf struct queue_entry **queue_buf; - size_t queue_size; struct queue_entry **top_rated; /* Top entries for bitmap bytes */ @@ -633,24 +635,18 @@ typedef struct afl_state { /*needed for afl_fuzz_one */ // TODO: see which we can reuse - u8 * out_buf; - size_t out_size; + u8 *out_buf; - u8 * out_scratch_buf; - size_t out_scratch_size; + u8 *out_scratch_buf; - u8 * eff_buf; - size_t eff_size; + u8 *eff_buf; - u8 * in_buf; - size_t in_size; + u8 *in_buf; - u8 * in_scratch_buf; - size_t in_scratch_size; + u8 *in_scratch_buf; - u8 * ex_buf; - size_t ex_size; - u32 custom_mutators_count; + u8 *ex_buf; + u32 custom_mutators_count; list_t custom_mutator_list; @@ -666,7 +662,6 @@ struct custom_mutator { char * name_short; void * dh; u8 * post_process_buf; - size_t post_process_size; u8 stacked_custom_prob, stacked_custom; void *data; /* custom mutator data ptr */ diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 306cc622..90701d18 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -30,12 +30,13 @@ #include #include #include +#include #include "config.h" #include "types.h" #include "debug.h" -/* Initial size used for ck_maybe_grow */ +/* Initial size used for afl_realloc */ #define INITIAL_GROWTH_SIZE (64) // Be careful! _WANT_ORIGINAL_AFL_ALLOC is not compatible with custom mutators @@ -76,10 +77,6 @@ \ } while (0) - /* Allocator increments for ck_realloc_block(). */ - - #define ALLOC_BLK_INC 256 - /* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized requests. */ @@ -149,15 +146,6 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) { } -/* Re-allocate a buffer with ALLOC_BLK_INC increments (used to speed up - repeated small reallocs without complicating the user code). */ - -static inline void *DFL_ck_realloc_block(void *orig, u32 size) { - - return DFL_ck_realloc(orig, size); - -} - /* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */ static inline u8 *DFL_ck_strdup(u8 *str) { @@ -183,7 +171,6 @@ static inline u8 *DFL_ck_strdup(u8 *str) { #define ck_alloc DFL_ck_alloc #define ck_alloc_nozero DFL_ck_alloc_nozero #define ck_realloc DFL_ck_realloc - #define ck_realloc_block DFL_ck_realloc_block #define ck_strdup DFL_ck_strdup #define ck_free DFL_ck_free @@ -239,10 +226,6 @@ static inline u8 *DFL_ck_strdup(u8 *str) { #define ALLOC_OFF_HEAD 8 #define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1) - /* Allocator increments for ck_realloc_block(). */ - - #define ALLOC_BLK_INC 256 - /* Sanity-checking macros for pointers. */ #define CHECK_PTR(_p) \ @@ -402,29 +385,6 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) { } -/* Re-allocate a buffer with ALLOC_BLK_INC increments (used to speed up - repeated small reallocs without complicating the user code). */ - -static inline void *DFL_ck_realloc_block(void *orig, u32 size) { - - #ifndef DEBUG_BUILD - - if (orig) { - - CHECK_PTR(orig); - - if (ALLOC_S(orig) >= size) return orig; - - size += ALLOC_BLK_INC; - - } - - #endif /* !DEBUG_BUILD */ - - return DFL_ck_realloc(orig, size); - -} - /* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */ static inline u8 *DFL_ck_strdup(u8 *str) { @@ -458,7 +418,6 @@ static inline u8 *DFL_ck_strdup(u8 *str) { #define ck_alloc DFL_ck_alloc #define ck_alloc_nozero DFL_ck_alloc_nozero #define ck_realloc DFL_ck_realloc - #define ck_realloc_block DFL_ck_realloc_block #define ck_strdup DFL_ck_strdup #define ck_free DFL_ck_free @@ -528,8 +487,8 @@ static inline void TRK_alloc_buf(void *ptr, const char *file, const char *func, /* No space available - allocate more. */ - TRK[bucket] = DFL_ck_realloc_block( - TRK[bucket], (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj)); + TRK[bucket] = DFL_ck_realloc(TRK[bucket], + (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj)); TRK[bucket][i].ptr = ptr; TRK[bucket][i].file = (char *)file; @@ -604,16 +563,6 @@ static inline void *TRK_ck_realloc(void *orig, u32 size, const char *file, } -static inline void *TRK_ck_realloc_block(void *orig, u32 size, const char *file, - const char *func, u32 line) { - - void *ret = DFL_ck_realloc_block(orig, size); - TRK_free_buf(orig, file, func, line); - TRK_alloc_buf(ret, file, func, line); - return ret; - -} - static inline void *TRK_ck_strdup(u8 *str, const char *file, const char *func, u32 line) { @@ -641,9 +590,6 @@ static inline void TRK_ck_free(void *ptr, const char *file, const char *func, #define ck_realloc(_p1, _p2) \ TRK_ck_realloc(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) - #define ck_realloc_block(_p1, _p2) \ - TRK_ck_realloc_block(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) - #define ck_strdup(_p1) TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__) #define ck_free(_p1) TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__) @@ -657,11 +603,14 @@ static inline void TRK_ck_free(void *ptr, const char *file, const char *func, */ static inline size_t next_pow2(size_t in) { - if (in == 0 || in > (size_t)-1) { - - return 0; /* avoid undefined behaviour under-/overflow */ + // Commented this out as this behavior doesn't change, according to unittests + // if (in == 0 || in > (size_t)-1) { - } + // + // return 0; /* avoid undefined behaviour under-/overflow + // */ + // + // } size_t out = in - 1; out |= out >> 1; @@ -673,32 +622,32 @@ static inline size_t next_pow2(size_t in) { } -/* This function makes sure *size is > size_needed after call. - It will realloc *buf otherwise. - *size will grow exponentially as per: - https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/ - Will return NULL and free *buf if size_needed is <1 or realloc failed. - @return For convenience, this function returns *buf. - */ -static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) { +/* AFL alloc buffer, the struct is here so we don't need to do fancy ptr + * arithmetics */ +struct afl_alloc_buf { - /* No need to realloc */ - if (likely(size_needed && *size >= size_needed)) { return *buf; } + /* The complete allocated size, including the header of len + * AFL_ALLOC_SIZE_OFFSET */ + size_t complete_size; + /* ptr to the first element of the actual buffer */ + u8 buf[0]; - /* No initial size was set */ - if (size_needed < INITIAL_GROWTH_SIZE) { size_needed = INITIAL_GROWTH_SIZE; } +}; - /* grow exponentially */ - size_t next_size = next_pow2(size_needed); +#define AFL_ALLOC_SIZE_OFFSET (offsetof(struct afl_alloc_buf, buf)) - /* handle overflow and zero size_needed */ - if (!next_size) { next_size = size_needed; } +/* Returs the container element to this ptr */ +static inline struct afl_alloc_buf *afl_alloc_bufptr(void *buf) { - /* alloc */ - *buf = realloc(*buf, next_size); - *size = *buf ? next_size : 0; + return (struct afl_alloc_buf *)((u8 *)buf - AFL_ALLOC_SIZE_OFFSET); - return *buf; +} + +/* Gets the maximum size of the buf contents (ptr->complete_size - + * AFL_ALLOC_SIZE_OFFSET) */ +static inline size_t afl_alloc_bufsize(void *buf) { + + return afl_alloc_bufptr(buf)->complete_size - AFL_ALLOC_SIZE_OFFSET; } @@ -706,45 +655,71 @@ static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) { It will realloc *buf otherwise. *size will grow exponentially as per: https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/ - Will FATAL if size_needed is <1. + Will return NULL and free *buf if size_needed is <1 or realloc failed. @return For convenience, this function returns *buf. */ -static inline void *ck_maybe_grow(void **buf, size_t *size, - size_t size_needed) { +static inline void *afl_realloc(void **buf, size_t size_needed) { + + struct afl_alloc_buf *new_buf = NULL; - /* Oops. found a bug? */ - if (unlikely(size_needed < 1)) { FATAL("cannot grow to non-positive size"); } + size_t current_size = 0; + size_t next_size = 0; + + if (likely(*buf)) { + + /* the size is always stored at buf - 1*size_t */ + new_buf = afl_alloc_bufptr(*buf); + current_size = new_buf->complete_size; + + } + + size_needed += AFL_ALLOC_SIZE_OFFSET; /* No need to realloc */ - if (likely(*size >= size_needed)) { return *buf; } + if (likely(current_size >= size_needed)) { return *buf; } /* No initial size was set */ - if (size_needed < INITIAL_GROWTH_SIZE) { size_needed = INITIAL_GROWTH_SIZE; } + if (size_needed < INITIAL_GROWTH_SIZE) { - /* grow exponentially */ - size_t next_size = next_pow2(size_needed); + next_size = INITIAL_GROWTH_SIZE; - /* handle overflow */ - if (!next_size) { next_size = size_needed; } + } else { + + /* grow exponentially */ + next_size = next_pow2(size_needed); + + /* handle overflow: fall back to the original size_needed */ + if (unlikely(!next_size)) { next_size = size_needed; } + + } /* alloc */ - *buf = ck_realloc(*buf, next_size); - *size = next_size; + new_buf = realloc(new_buf, next_size); + if (unlikely(!new_buf)) { + *buf = NULL; + return NULL; + + } + + new_buf->complete_size = next_size; + *buf = (void *)(new_buf->buf); return *buf; } +static inline void afl_free(void *buf) { + + if (buf) { free(afl_alloc_bufptr(buf)); } + +} + /* Swaps buf1 ptr and buf2 ptr, as well as their sizes */ -static inline void swap_bufs(void **buf1, size_t *size1, void **buf2, - size_t *size2) { +static inline void afl_swap_bufs(void **buf1, void **buf2) { - void * scratch_buf = *buf1; - size_t scratch_size = *size1; + void *scratch_buf = *buf1; *buf1 = *buf2; - *size1 = *size2; *buf2 = scratch_buf; - *size2 = scratch_size; } diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 17f02984..88262a98 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -152,8 +152,10 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, /* Okay, let's allocate memory and copy data between "...", handling \xNN escaping, \\, and \". */ - afl->extras = ck_realloc_block( - afl->extras, (afl->extras_cnt + 1) * sizeof(struct extra_data)); + afl->extras = + afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); + if (unlikely(!afl->extras)) { PFATAL("alloc"); } wptr = afl->extras[afl->extras_cnt].data = ck_alloc(rptr - lptr); @@ -296,8 +298,10 @@ void load_extras(afl_state_t *afl, u8 *dir) { if (min_len > st.st_size) { min_len = st.st_size; } if (max_len < st.st_size) { max_len = st.st_size; } - afl->extras = ck_realloc_block( - afl->extras, (afl->extras_cnt + 1) * sizeof(struct extra_data)); + afl->extras = + afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); + if (unlikely(!afl->extras)) { PFATAL("alloc"); } afl->extras[afl->extras_cnt].data = ck_alloc(st.st_size); afl->extras[afl->extras_cnt].len = st.st_size; diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index 0fa646f9..22578df9 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -122,9 +122,8 @@ void destroy_custom_mutators(afl_state_t *afl) { if (el->post_process_buf) { - ck_free(el->post_process_buf); + afl_free(el->post_process_buf); el->post_process_buf = NULL; - el->post_process_size = 0; } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 0a4be320..3bf0c195 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -364,8 +364,6 @@ static void locate_diffs(u8 *ptr1, u8 *ptr2, u32 len, s32 *first, s32 *last) { #endif /* !IGNORE_FINDS */ -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - /* Take the current entry from the queue, fuzz it for a while. This function is a tad too long... returns 0 if fuzzed successfully, 1 if skipped or bailed out. */ @@ -384,9 +382,6 @@ u8 fuzz_one_original(afl_state_t *afl) { u8 a_collect[MAX_AUTO_EXTRA]; u32 a_len = 0; -/* Not pretty, but saves a lot of writing */ -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - #ifdef IGNORE_FINDS /* In IGNORE_FINDS mode, skip any entries that weren't in the @@ -484,7 +479,8 @@ u8 fuzz_one_original(afl_state_t *afl) { single byte anyway, so it wouldn't give us any performance or memory usage benefits. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } afl->subseq_tmouts = 0; @@ -800,7 +796,8 @@ u8 fuzz_one_original(afl_state_t *afl) { /* Initialize effector map for the next step (see comments below). Always flag first and last byte as doing something. */ - eff_map = ck_maybe_grow(BUF_PARAMS(eff), EFF_ALEN(len)); + eff_map = afl_realloc(AFL_BUF_PARAM(eff), EFF_ALEN(len)); + if (unlikely(!eff_map)) { PFATAL("alloc"); } eff_map[0] = 1; if (EFF_APOS(len - 1) != 0) { @@ -1557,7 +1554,8 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - ex_tmp = ck_maybe_grow(BUF_PARAMS(ex), len + MAX_DICT_FILE); + ex_tmp = afl_realloc(AFL_BUF_PARAM(ex), len + MAX_DICT_FILE); + if (unlikely(!ex_tmp)) { PFATAL("alloc"); } for (i = 0; i <= (u32)len; ++i) { @@ -1733,7 +1731,8 @@ custom_mutator_stage: fd = open(target->fname, O_RDONLY); if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } - new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), target->len); + new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), target->len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } ck_read(fd, new_buf, target->len, target->fname); close(fd); @@ -1908,7 +1907,8 @@ havoc_stage: temp_len = new_len; if (out_buf != custom_havoc_buf) { - ck_maybe_grow(BUF_PARAMS(out), temp_len); + afl_realloc(AFL_BUF_PARAM(out), temp_len); + if (unlikely(!afl->out_buf)) { PFATAL("alloc"); } memcpy(out_buf, custom_havoc_buf, temp_len); } @@ -2147,7 +2147,8 @@ havoc_stage: clone_to = rand_below(afl, temp_len); new_buf = - ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); + afl_realloc(AFL_BUF_PARAM(out_scratch), temp_len + clone_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } /* Head */ @@ -2172,7 +2173,7 @@ havoc_stage: memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); - swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); + afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); out_buf = new_buf; new_buf = NULL; temp_len += clone_len; @@ -2287,7 +2288,8 @@ havoc_stage: if (temp_len + extra_len >= MAX_FILE) { break; } - out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), temp_len + extra_len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } /* Tail */ memmove(out_buf + insert_at + extra_len, out_buf + insert_at, @@ -2343,7 +2345,8 @@ havoc_stage: } u32 new_len = target->len; - u8 *new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len); + u8 *new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), new_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } ck_read(fd, new_buf, new_len, target->fname); @@ -2383,7 +2386,8 @@ havoc_stage: clone_to = rand_below(afl, temp_len); u8 *temp_buf = - ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); + afl_realloc(AFL_BUF_PARAM(out_scratch), temp_len + clone_len); + if (unlikely(!temp_buf)) { PFATAL("alloc"); } /* Head */ @@ -2397,7 +2401,7 @@ havoc_stage: memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); - swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); + afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); out_buf = temp_buf; temp_len += clone_len; @@ -2418,7 +2422,8 @@ havoc_stage: /* out_buf might have been mangled a bit, so let's restore it to its original size and shape. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } temp_len = len; memcpy(out_buf, in_buf, len); @@ -2513,7 +2518,8 @@ retry_splicing: if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } - new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), target->len); + new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), target->len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } ck_read(fd, new_buf, target->len, target->fname); @@ -2535,10 +2541,11 @@ retry_splicing: len = target->len; memcpy(new_buf, in_buf, split_at); - swap_bufs(BUF_PARAMS(in), BUF_PARAMS(in_scratch)); + afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); in_buf = new_buf; - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(out_buf, in_buf, len); goto custom_mutator_stage; @@ -2679,7 +2686,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { single byte anyway, so it wouldn't give us any performance or memory usage benefits. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } afl->subseq_tmouts = 0; @@ -3001,7 +3009,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { /* Initialize effector map for the next step (see comments below). Always flag first and last byte as doing something. */ - eff_map = ck_maybe_grow(BUF_PARAMS(eff), EFF_ALEN(len)); + eff_map = afl_realloc(AFL_BUF_PARAM(eff), EFF_ALEN(len)); + if (unlikely(!eff_map)) { PFATAL("alloc"); } eff_map[0] = 1; if (EFF_APOS(len - 1) != 0) { @@ -3758,7 +3767,8 @@ skip_interest: orig_hit_cnt = new_hit_cnt; - ex_tmp = ck_maybe_grow(BUF_PARAMS(ex), len + MAX_DICT_FILE); + ex_tmp = afl_realloc(AFL_BUF_PARAM(ex), len + MAX_DICT_FILE); + if (unlikely(!ex_tmp)) { PFATAL("alloc"); } for (i = 0; i <= (u32)len; ++i) { @@ -4196,8 +4206,9 @@ pacemaker_fuzzing: clone_to = rand_below(afl, temp_len); - new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), - temp_len + clone_len); + new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), + temp_len + clone_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } /* Head */ @@ -4223,7 +4234,7 @@ pacemaker_fuzzing: memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); - swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch)); + afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); out_buf = new_buf; temp_len += clone_len; MOpt_globals.cycles_v2[STAGE_Clone75] += 1; @@ -4340,7 +4351,8 @@ pacemaker_fuzzing: if (temp_len + extra_len >= MAX_FILE) break; - out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), temp_len + extra_len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } /* Tail */ memmove(out_buf + insert_at + extra_len, out_buf + insert_at, @@ -4373,7 +4385,8 @@ pacemaker_fuzzing: /* out_buf might have been mangled a bit, so let's restore it to its original size and shape. */ - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } temp_len = len; memcpy(out_buf, in_buf, len); @@ -4518,7 +4531,8 @@ pacemaker_fuzzing: if (fd < 0) { PFATAL("Unable to open '%s'", target->fname); } - new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), target->len); + new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), target->len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } ck_read(fd, new_buf, target->len, target->fname); @@ -4545,9 +4559,10 @@ pacemaker_fuzzing: len = target->len; memcpy(new_buf, in_buf, split_at); - swap_bufs(BUF_PARAMS(in), BUF_PARAMS(in_scratch)); + afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); in_buf = new_buf; - out_buf = ck_maybe_grow(BUF_PARAMS(out), len); + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); + if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(out_buf, in_buf, len); goto havoc_stage_puppet; @@ -4880,5 +4895,3 @@ u8 fuzz_one(afl_state_t *afl) { } -#undef BUF_PARAMS - diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index a077469e..e540f548 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -40,9 +40,7 @@ static void *unsupported(afl_state_t *afl, unsigned int seed) { /* sorry for this makro... it just fills in `&py_mutator->something_buf, &py_mutator->something_size`. */ - #define BUF_PARAMS(name) \ - (void **)&((py_mutator_t *)py_mutator)->name##_buf, \ - &((py_mutator_t *)py_mutator)->name##_size + #define BUF_PARAMS(name) (void **)&((py_mutator_t *)py_mutator)->name##_buf static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, u8 *add_buf, size_t add_buf_size, size_t max_size) { @@ -97,7 +95,8 @@ static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, mutated_size = PyByteArray_Size(py_value); - *out_buf = ck_maybe_grow(BUF_PARAMS(fuzz), mutated_size); + *out_buf = afl_realloc(BUF_PARAMS(fuzz), mutated_size); + if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(*out_buf, PyByteArray_AsString(py_value), mutated_size); Py_DECREF(py_value); @@ -317,7 +316,6 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, mutator = ck_alloc(sizeof(struct custom_mutator)); mutator->post_process_buf = NULL; - mutator->post_process_size = 0; mutator->name = module_name; ACTF("Loading Python mutator library from '%s'...", module_name); @@ -419,7 +417,11 @@ size_t post_process_py(void *py_mutator, u8 *buf, size_t buf_size, py_out_buf_size = PyByteArray_Size(py_value); - ck_maybe_grow(BUF_PARAMS(post_process), py_out_buf_size); + if (unlikely(!afl_realloc(BUF_PARAMS(post_process), py_out_buf_size))) { + + PFATAL("alloc"); + + } memcpy(py->post_process_buf, PyByteArray_AsString(py_value), py_out_buf_size); @@ -527,7 +529,8 @@ size_t trim_py(void *py_mutator, u8 **out_buf) { if (py_value != NULL) { ret = PyByteArray_Size(py_value); - *out_buf = ck_maybe_grow(BUF_PARAMS(trim), ret); + *out_buf = afl_realloc(BUF_PARAMS(trim), ret); + if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(*out_buf, PyByteArray_AsString(py_value), ret); Py_DECREF(py_value); @@ -592,7 +595,8 @@ size_t havoc_mutation_py(void *py_mutator, u8 *buf, size_t buf_size, } else { /* A new buf is needed... */ - *out_buf = ck_maybe_grow(BUF_PARAMS(havoc), mutated_size); + *out_buf = afl_realloc(BUF_PARAMS(havoc), mutated_size); + if (unlikely(!out_buf)) { PFATAL("alloc"); } } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index f35df914..0c472845 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -26,8 +26,6 @@ #include #include -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - /* Mark deterministic checks as done for a particular queue entry. We use the .state file to avoid repeating deterministic fuzzing when resuming aborted scans. */ @@ -248,8 +246,9 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { } - struct queue_entry **queue_buf = ck_maybe_grow( - BUF_PARAMS(queue), afl->queued_paths * sizeof(struct queue_entry *)); + struct queue_entry **queue_buf = afl_realloc( + AFL_BUF_PARAM(queue), afl->queued_paths * sizeof(struct queue_entry *)); + if (unlikely(!queue_buf)) { PFATAL("alloc"); } queue_buf[afl->queued_paths - 1] = q; afl->last_path_time = get_cur_time(); diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index f21dd0b0..1ae6ab54 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -313,8 +313,6 @@ static unsigned long long strntoull(const char *str, size_t sz, char **end, } -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u64 pattern, u64 repl, u64 o_pattern, u32 idx, u8 *orig_buf, u8 *buf, u32 len, u8 do_reverse, @@ -358,7 +356,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, size_t old_len = endptr - buf_8; size_t num_len = snprintf(NULL, 0, "%lld", num); - u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), len + num_len); + u8 *new_buf = afl_realloc((void **)&afl->out_scratch_buf, len + num_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } memcpy(new_buf, buf, idx); snprintf(new_buf + idx, num_len, "%lld", num); @@ -371,7 +370,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, size_t old_len = endptr - buf_8; size_t num_len = snprintf(NULL, 0, "%llu", unum); - u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), len + num_len); + u8 *new_buf = afl_realloc((void **)&afl->out_scratch_buf, len + num_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } memcpy(new_buf, buf, idx); snprintf(new_buf + idx, num_len, "%llu", unum); diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index d3f823c9..d71ec339 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -135,8 +135,6 @@ write_to_testcase(afl_state_t *afl, void *mem, u32 len) { } -#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size - /* The same, but with an adjustable gap. Used for trimming. */ static void write_with_gap(afl_state_t *afl, u8 *mem, u32 len, u32 skip_at, @@ -149,7 +147,8 @@ static void write_with_gap(afl_state_t *afl, u8 *mem, u32 len, u32 skip_at, This memory is used to carry out the post_processing(if present) after copying the testcase by removing the gaps. This can break though */ - u8 *mem_trimmed = ck_maybe_grow(BUF_PARAMS(out_scratch), len - skip_len + 1); + u8 *mem_trimmed = afl_realloc(AFL_BUF_PARAM(out_scratch), len - skip_len + 1); + if (unlikely(!mem_trimmed)) { PFATAL("alloc"); } ssize_t new_size = len - skip_len; void * new_mem = mem; @@ -288,8 +287,6 @@ static void write_with_gap(afl_state_t *afl, u8 *mem, u32 len, u32 skip_at, } -#undef BUF_PARAMS - /* Calibrate a new test case. This is done when processing the input directory to warn about flaky or otherwise problematic test cases early on; and when new paths are discovered to detect variable behavior and so on. */ diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index d4de91a4..e68e7786 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -421,13 +421,13 @@ void afl_state_deinit(afl_state_t *afl) { if (afl->pass_stats) { ck_free(afl->pass_stats); } if (afl->orig_cmp_map) { ck_free(afl->orig_cmp_map); } - if (afl->queue_buf) { free(afl->queue_buf); } - if (afl->out_buf) { free(afl->out_buf); } - if (afl->out_scratch_buf) { free(afl->out_scratch_buf); } - if (afl->eff_buf) { free(afl->eff_buf); } - if (afl->in_buf) { free(afl->in_buf); } - if (afl->in_scratch_buf) { free(afl->in_scratch_buf); } - if (afl->ex_buf) { free(afl->ex_buf); } + afl_free(afl->queue_buf); + afl_free(afl->out_buf); + afl_free(afl->out_scratch_buf); + afl_free(afl->eff_buf); + afl_free(afl->in_buf); + afl_free(afl->in_scratch_buf); + afl_free(afl->ex_buf); ck_free(afl->virgin_bits); ck_free(afl->virgin_tmout); diff --git a/test/unittests/unit_maybe_alloc.c b/test/unittests/unit_maybe_alloc.c index 889ced8a..e452e2f2 100644 --- a/test/unittests/unit_maybe_alloc.c +++ b/test/unittests/unit_maybe_alloc.c @@ -42,7 +42,24 @@ int __wrap_printf(const char *format, ...) { return 1; } -#define BUF_PARAMS (void **)&buf, &size +#define VOID_BUF (void **)&buf + +static void *create_fake_maybe_grow_of(size_t size) { + + size += AFL_ALLOC_SIZE_OFFSET; + + // fake a realloc buf + + struct afl_alloc_buf *buf = malloc(size); + if (!buf) { + perror("Could not allocate fake buf"); + return NULL; + } + buf->complete_size = size; // The size + void *actual_buf = (void *)(buf->buf); + return actual_buf; + +} /* static int setup(void **state) { @@ -52,29 +69,55 @@ static int setup(void **state) { } */ +static void test_pow2(void **state) { + (void)state; + + assert_int_equal(next_pow2(64), 64); + assert_int_equal(next_pow2(63), 64); + assert_int_not_equal(next_pow2(65), 65); + assert_int_equal(next_pow2(0x100), 0x100); + assert_int_equal(next_pow2(0x180), 0x200); + assert_int_equal(next_pow2(108), 0x80); + assert_int_equal(next_pow2(0), 0); + assert_int_equal(next_pow2(1), 1); + assert_int_equal(next_pow2(2), 2); + assert_int_equal(next_pow2(3), 4); + assert_int_equal(next_pow2(0xFFFFFF), 0x1000000); + assert_int_equal(next_pow2(0xFFFFFFF), 0x10000000); + assert_int_equal(next_pow2(0xFFFFFF0), 0x10000000); + assert_int_equal(next_pow2(SIZE_MAX), 0); + assert_int_equal(next_pow2(-1), 0); + assert_int_equal(next_pow2(-2), 0); + +} + static void test_null_allocs(void **state) { (void)state; void *buf = NULL; - size_t size = 0; - void *ptr = ck_maybe_grow(BUF_PARAMS, 100); + void *ptr = afl_realloc(VOID_BUF, 100); + if (unlikely(!buf)) { PFATAL("alloc"); } + size_t size = afl_alloc_bufsize(buf); assert_true(buf == ptr); assert_true(size >= 100); - ck_free(ptr); + afl_free(ptr); } static void test_nonpow2_size(void **state) { (void)state; - char *buf = ck_alloc(150); - size_t size = 150; + char *buf = create_fake_maybe_grow_of(150); + buf[140] = '5'; - char *ptr = ck_maybe_grow(BUF_PARAMS, 160); + + char *ptr = afl_realloc(VOID_BUF, 160); + if (unlikely(!ptr)) { PFATAL("alloc"); } + size_t size = afl_alloc_bufsize(buf); assert_ptr_equal(buf, ptr); assert_true(size >= 160); assert_true(buf[140] == '5'); - ck_free(ptr); + afl_free(ptr); } @@ -83,32 +126,37 @@ static void test_zero_size(void **state) { char *buf = NULL; size_t size = 0; - assert_non_null(maybe_grow(BUF_PARAMS, 0)); - free(buf); + char *new_buf = afl_realloc(VOID_BUF, 0); + assert_non_null(new_buf); + assert_ptr_equal(buf, new_buf); + afl_free(buf); buf = NULL; size = 0; - char *ptr = ck_maybe_grow(BUF_PARAMS, 100); + char *ptr = afl_realloc(VOID_BUF, 100); + if (unlikely(!ptr)) { PFATAL("alloc"); } + size = afl_alloc_bufsize(buf); assert_non_null(ptr); assert_ptr_equal(buf, ptr); assert_true(size >= 100); - expect_assert_failure(ck_maybe_grow(BUF_PARAMS, 0)); - - ck_free(ptr); + afl_free(ptr); } + static void test_unchanged_size(void **state) { (void)state; - void *buf = ck_alloc(100); - size_t size = 100; - void *buf_before = buf; - void *buf_after = ck_maybe_grow(BUF_PARAMS, 100); - assert_ptr_equal(buf, buf_after); + // fake a realloc buf + void *actual_buf = create_fake_maybe_grow_of(100); + + void *buf_before = actual_buf; + void *buf_after = afl_realloc(&actual_buf, 100); + if (unlikely(!buf_after)) { PFATAL("alloc"); } + assert_ptr_equal(actual_buf, buf_after); assert_ptr_equal(buf_after, buf_before); - ck_free(buf); + afl_free(buf_after); } @@ -118,29 +166,35 @@ static void test_grow_multiple(void **state) { char *buf = NULL; size_t size = 0; - char *ptr = ck_maybe_grow(BUF_PARAMS, 100); + char *ptr = afl_realloc(VOID_BUF, 100); + if (unlikely(!ptr)) { PFATAL("alloc"); } + size = afl_alloc_bufsize(ptr); assert_ptr_equal(ptr, buf); assert_true(size >= 100); - assert_int_equal(size, next_pow2(size)); + assert_int_equal(size, next_pow2(size) - AFL_ALLOC_SIZE_OFFSET); buf[50] = '5'; - ptr = (char *)ck_maybe_grow(BUF_PARAMS, 1000); + ptr = (char *)afl_realloc(VOID_BUF, 1000); + if (unlikely(!ptr)) { PFATAL("alloc"); } + size = afl_alloc_bufsize(ptr); assert_ptr_equal(ptr, buf); assert_true(size >= 100); - assert_int_equal(size, next_pow2(size)); + assert_int_equal(size, next_pow2(size) - AFL_ALLOC_SIZE_OFFSET); buf[500] = '5'; - ptr = (char *)ck_maybe_grow(BUF_PARAMS, 10000); + ptr = (char *)afl_realloc(VOID_BUF, 10000); + if (unlikely(!ptr)) { PFATAL("alloc"); } + size = afl_alloc_bufsize(ptr); assert_ptr_equal(ptr, buf); assert_true(size >= 10000); - assert_int_equal(size, next_pow2(size)); + assert_int_equal(size, next_pow2(size) - AFL_ALLOC_SIZE_OFFSET); buf[5000] = '5'; assert_int_equal(buf[50], '5'); assert_int_equal(buf[500], '5'); assert_int_equal(buf[5000], '5'); - ck_free(buf); + afl_free(buf); } @@ -157,6 +211,7 @@ int main(int argc, char **argv) { (void)argv; const struct CMUnitTest tests[] = { + cmocka_unit_test(test_pow2), cmocka_unit_test(test_null_allocs), cmocka_unit_test(test_nonpow2_size), cmocka_unit_test(test_zero_size), -- cgit 1.4.1 From 1a94cfe2af023a33c0a0defa5933541731136922 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 18 Aug 2020 01:31:40 +0200 Subject: moved autodict extras away from extras_a --- include/afl-fuzz.h | 1 + include/forkserver.h | 2 +- src/afl-forkserver.c | 4 +-- src/afl-fuzz-extras.c | 98 ++++++++++++++++++++++++++++++++++++++++----------- src/afl-fuzz-state.c | 2 +- 5 files changed, 82 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index dca395aa..c04ba396 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -913,6 +913,7 @@ u8 has_new_bits(afl_state_t *, u8 *); void load_extras_file(afl_state_t *, u8 *, u32 *, u32 *, u32); void load_extras(afl_state_t *, u8 *); +void add_extra(afl_state_t *afl, u8 *mem, u32 len); void maybe_add_auto(afl_state_t *, u8 *, u32); void save_auto(afl_state_t *); void load_auto(afl_state_t *); diff --git a/include/forkserver.h b/include/forkserver.h index 0a7390ed..d824c1c9 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -91,7 +91,7 @@ typedef struct afl_forkserver { u8 *afl_ptr; /* for autodictionary: afl ptr */ - void (*autodict_func)(void *afl_ptr, u8 *mem, u32 len); + void (*add_extra_func)(void *afl_ptr, u8 *mem, u32 len); } afl_forkserver_t; diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 25983f26..85450e4a 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -630,7 +630,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if ((status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT) { - if (fsrv->autodict_func == NULL || fsrv->afl_ptr == NULL) { + if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) { // this is not afl-fuzz - we deny and return if (fsrv->use_shmem_fuzz) { @@ -715,7 +715,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, while (offset < (u32)status && (u8)dict[offset] + offset < (u32)status) { - fsrv->autodict_func(fsrv->afl_ptr, dict + offset + 1, + fsrv->add_extra_func(fsrv->afl_ptr, dict + offset + 1, (u8)dict[offset]); offset += (1 + dict[offset]); count++; diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 88262a98..77a6c05e 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -227,6 +227,38 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, } +static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, u8 *dir) { + + u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; + + if (!afl->extras_cnt) { + FATAL("No usable files in '%s'", dir); } + + qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), + compare_extras_len); + + OKF("Loaded %u extra tokens, size range %s to %s.", afl->extras_cnt, + stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), min_len), + stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), max_len)); + + if (max_len > 32) { + + WARNF("Some tokens are relatively large (%s) - consider trimming.", + stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), max_len)); + + } + + if (afl->extras_cnt > MAX_DET_EXTRAS) { + + WARNF("More than %d tokens - will use them probabilistically.", + MAX_DET_EXTRAS); + + } + +} + + + /* Read extras from the extras directory and sort them by size. */ void load_extras(afl_state_t *afl, u8 *dir) { @@ -256,7 +288,8 @@ void load_extras(afl_state_t *afl, u8 *dir) { if (errno == ENOTDIR) { load_extras_file(afl, dir, &min_len, &max_len, dict_level); - goto check_and_sort; + extras_check_and_sort(afl, min_len, max_len, dir); + return; } @@ -321,44 +354,67 @@ void load_extras(afl_state_t *afl, u8 *dir) { closedir(d); -check_and_sort: + extras_check_and_sort(afl, min_len, max_len, dir); - if (!afl->extras_cnt) { FATAL("No usable files in '%s'", dir); } +} - qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), - compare_extras_len); +/* Helper function for maybe_add_auto(afl, ) */ - OKF("Loaded %u extra tokens, size range %s to %s.", afl->extras_cnt, - stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), min_len), - stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), max_len)); +static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) { - if (max_len > 32) { + while (len--) { - WARNF("Some tokens are relatively large (%s) - consider trimming.", - stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), max_len)); + if (tolower(*(m1++)) ^ tolower(*(m2++))) { return 1; } } - if (afl->extras_cnt > MAX_DET_EXTRAS) { + return 0; - WARNF("More than %d tokens - will use them probabilistically.", - MAX_DET_EXTRAS); +} - } +/* Adds a new extra / dict entry. */ +void add_extra(afl_state_t *afl, u8 *mem, u32 len) { -} + u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; -/* Helper function for maybe_add_auto(afl, ) */ + if (len > MAX_DICT_FILE) { -static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) { + FATAL( + "Extra '%.*s' is too big (%s, limit is %s)", (int)len, mem, + stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), len), + stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); - while (len--) { + } else if (len > 32) { - if (tolower(*(m1++)) ^ tolower(*(m2++))) { return 1; } + WARNF( + "Extra '%.*s' is pretty large, consider trimming.", (int)len, mem + ); } - return 0; + afl->extras = + afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); + if (unlikely(!afl->extras)) { PFATAL("alloc"); } + + afl->extras[afl->extras_cnt].data = ck_alloc(len); + afl->extras[afl->extras_cnt].len = len; + + memcpy(afl->extras[afl->extras_cnt].data, mem, len); + + afl->extras_cnt++; + + qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), + compare_extras_len); + + /* We only want to print this once */ + + if (afl->extras_cnt == MAX_DET_EXTRAS + 1) { + + WARNF("More than %d tokens - will use them probabilistically.", + MAX_DET_EXTRAS); + + } } diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index e68e7786..7e9f15b7 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -114,7 +114,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->fsrv.map_size = map_size; // afl_state_t is not available in forkserver.c afl->fsrv.afl_ptr = (void *)afl; - afl->fsrv.autodict_func = (void (*)(void *, u8 *, u32)) & maybe_add_auto; + afl->fsrv.add_extra_func = (void (*)(void *, u8 *, u32)) &add_extra; afl->cal_cycles = CAL_CYCLES; afl->cal_cycles_long = CAL_CYCLES_LONG; -- cgit 1.4.1 From b504b9313a47c597296a2dab4fd8a591d93242b4 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 18 Aug 2020 01:36:49 +0200 Subject: code-format, changelog --- docs/Changelog.md | 1 + src/afl-forkserver.c | 2 +- src/afl-fuzz-extras.c | 26 ++++++++++---------------- src/afl-fuzz-state.c | 2 +- 4 files changed, 13 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 55b0c7dd..d9c2a9c0 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -22,6 +22,7 @@ sending a mail to . - expanded havoc mode added, on no cycle finds add extra splicing and MOpt into the mix - fixed a bug in redqueen for strings and made deterministic with -s + - Compiletime autodictionary fixes - llvm_mode: - now supports llvm 12! - support for AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST (previous diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 85450e4a..8277116b 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -716,7 +716,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, (u8)dict[offset] + offset < (u32)status) { fsrv->add_extra_func(fsrv->afl_ptr, dict + offset + 1, - (u8)dict[offset]); + (u8)dict[offset]); offset += (1 + dict[offset]); count++; diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 77a6c05e..094c30b9 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -227,12 +227,12 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, } -static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, u8 *dir) { +static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, + u8 *dir) { u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; - if (!afl->extras_cnt) { - FATAL("No usable files in '%s'", dir); } + if (!afl->extras_cnt) { FATAL("No usable files in '%s'", dir); } qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), compare_extras_len); @@ -257,8 +257,6 @@ static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, u8 } - - /* Read extras from the extras directory and sort them by size. */ void load_extras(afl_state_t *afl, u8 *dir) { @@ -379,22 +377,18 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { if (len > MAX_DICT_FILE) { - FATAL( - "Extra '%.*s' is too big (%s, limit is %s)", (int)len, mem, - stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), len), - stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); + FATAL("Extra '%.*s' is too big (%s, limit is %s)", (int)len, mem, + stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), len), + stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); } else if (len > 32) { - WARNF( - "Extra '%.*s' is pretty large, consider trimming.", (int)len, mem - ); + WARNF("Extra '%.*s' is pretty large, consider trimming.", (int)len, mem); } - afl->extras = - afl_realloc((void **)&afl->extras, - (afl->extras_cnt + 1) * sizeof(struct extra_data)); + afl->extras = afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); if (unlikely(!afl->extras)) { PFATAL("alloc"); } afl->extras[afl->extras_cnt].data = ck_alloc(len); @@ -410,7 +404,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { /* We only want to print this once */ if (afl->extras_cnt == MAX_DET_EXTRAS + 1) { - + WARNF("More than %d tokens - will use them probabilistically.", MAX_DET_EXTRAS); diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 7e9f15b7..dd0e316c 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -114,7 +114,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->fsrv.map_size = map_size; // afl_state_t is not available in forkserver.c afl->fsrv.afl_ptr = (void *)afl; - afl->fsrv.add_extra_func = (void (*)(void *, u8 *, u32)) &add_extra; + afl->fsrv.add_extra_func = (void (*)(void *, u8 *, u32)) & add_extra; afl->cal_cycles = CAL_CYCLES; afl->cal_cycles_long = CAL_CYCLES_LONG; -- cgit 1.4.1 From 47878f697485e0543ce9e5d81369aa1ecc56e55e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 21 Aug 2020 23:33:35 +0200 Subject: add execs_done to plot file --- docs/Changelog.md | 1 + include/afl-fuzz.h | 2 +- src/afl-fuzz-stats.c | 17 ++++++++++------- 3 files changed, 12 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index d00d59d7..8bbb4e19 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -13,6 +13,7 @@ sending a mail to . - afl-fuzz: - Fix for auto dictionary entries found during fuzzing to not throw out a -x dictionary + - added total execs done to plot file - llvm_mode: - Ported SanCov to LTO, and made it the default for LTO. better instrumentation locations diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index c04ba396..1deeddd3 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -624,7 +624,7 @@ typedef struct afl_state { /* plot file saves from last run */ u32 plot_prev_qp, plot_prev_pf, plot_prev_pnf, plot_prev_ce, plot_prev_md; - u64 plot_prev_qc, plot_prev_uc, plot_prev_uh; + u64 plot_prev_qc, plot_prev_uc, plot_prev_uh, plot_prev_ed; u64 stats_last_stats_ms, stats_last_plot_ms, stats_last_ms, stats_last_execs; double stats_avg_exec; diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index aeb290bd..0ce35cb7 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -206,7 +206,8 @@ void maybe_update_plot_file(afl_state_t *afl, double bitmap_cvg, double eps) { afl->plot_prev_qc == afl->queue_cycle && afl->plot_prev_uc == afl->unique_crashes && afl->plot_prev_uh == afl->unique_hangs && - afl->plot_prev_md == afl->max_depth) || + afl->plot_prev_md == afl->max_depth && + afl->plot_prev_ed == afl->fsrv.total_execs) || unlikely(!afl->queue_cycle) || unlikely(get_cur_time() - afl->start_time <= 60)) { @@ -222,6 +223,7 @@ void maybe_update_plot_file(afl_state_t *afl, double bitmap_cvg, double eps) { afl->plot_prev_uc = afl->unique_crashes; afl->plot_prev_uh = afl->unique_hangs; afl->plot_prev_md = afl->max_depth; + afl->plot_prev_ed = afl->fsrv.total_execs; /* Fields in the file: @@ -229,12 +231,13 @@ void maybe_update_plot_file(afl_state_t *afl, double bitmap_cvg, double eps) { favored_not_fuzzed, afl->unique_crashes, afl->unique_hangs, afl->max_depth, execs_per_sec */ - fprintf(afl->fsrv.plot_file, - "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f\n", - get_cur_time() / 1000, afl->queue_cycle - 1, afl->current_entry, - afl->queued_paths, afl->pending_not_fuzzed, afl->pending_favored, - bitmap_cvg, afl->unique_crashes, afl->unique_hangs, afl->max_depth, - eps); /* ignore errors */ + fprintf( + afl->fsrv.plot_file, + "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu\n", + get_cur_time() / 1000, afl->queue_cycle - 1, afl->current_entry, + afl->queued_paths, afl->pending_not_fuzzed, afl->pending_favored, + bitmap_cvg, afl->unique_crashes, afl->unique_hangs, afl->max_depth, eps, + afl->plot_prev_ed); /* ignore errors */ fflush(afl->fsrv.plot_file); -- cgit 1.4.1 From 5ec91fce23ddf3b81076ea4cb4a4553c9c302c3e Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 21 Aug 2020 23:03:08 +0200 Subject: fix for bad free (#520) --- src/afl-fuzz-extras.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 094c30b9..1452c55e 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -627,7 +627,7 @@ void destroy_extras(afl_state_t *afl) { } - ck_free(afl->extras); + afl_free(afl->extras); } -- cgit 1.4.1 From c4f71ab201da991fd16b2691f76020bfdb6459a4 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 22 Aug 2020 10:01:45 +0200 Subject: enable autodict for cmplog compile, it is ensure not be used in the forkserver --- llvm_mode/SanitizerCoverageLTO.so.cc | 2 -- llvm_mode/afl-llvm-lto-instrumentation.so.cc | 6 +----- src/afl-forkserver.c | 1 + 3 files changed, 2 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/llvm_mode/SanitizerCoverageLTO.so.cc b/llvm_mode/SanitizerCoverageLTO.so.cc index 1d659096..1dd65188 100644 --- a/llvm_mode/SanitizerCoverageLTO.so.cc +++ b/llvm_mode/SanitizerCoverageLTO.so.cc @@ -437,8 +437,6 @@ bool ModuleSanitizerCoverage::instrumentModule( if ((afl_global_id = atoi(ptr)) < 0) FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is negative\n", ptr); - if (getenv("AFL_LLVM_CMPLOG")) autodictionary = 0; - if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { if ((documentFile = fopen(ptr, "a")) == NULL) diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index 12509ab2..0d82d09e 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -87,7 +87,7 @@ class AFLLTOPass : public ModulePass { bool runOnModule(Module &M) override; protected: - int afl_global_id = 1, autodictionary = 0; + int afl_global_id = 1, autodictionary = 1; uint32_t function_minimum_size = 1; uint32_t inst_blocks = 0, inst_funcs = 0, total_instr = 0; uint64_t map_addr = 0x10000; @@ -128,8 +128,6 @@ bool AFLLTOPass::runOnModule(Module &M) { be_quiet = 1; - if (getenv("AFL_LLVM_CMPLOG")) autodictionary = 0; - if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { if ((documentFile = fopen(ptr, "a")) == NULL) @@ -137,8 +135,6 @@ bool AFLLTOPass::runOnModule(Module &M) { } - if (getenv("AFL_LLVM_LTO_AUTODICTIONARY")) autodictionary = 1; - // we make this the default as the fixed map has problems with // defered forkserver, early constructors, ifuncs and maybe more /*if (getenv("AFL_LLVM_MAP_DYNAMIC"))*/ diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 8277116b..52a14602 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -115,6 +115,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->out_file = NULL; fsrv_to->init_child_func = fsrv_exec_child; + //Note: do not copy ->add_extra_func list_append(&fsrv_list, fsrv_to); -- cgit 1.4.1 From 1301552101af899557a93a7535d8a57874fe6edf Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 23 Aug 2020 01:48:36 +0200 Subject: added AFL_MAX_DET_EXTRAS env var --- include/afl-fuzz.h | 5 +++-- include/envs.h | 1 + src/afl-forkserver.c | 2 +- src/afl-fuzz-extras.c | 8 ++++---- src/afl-fuzz-one.c | 16 ++++++++-------- src/afl-fuzz-state.c | 7 +++++++ src/afl-fuzz.c | 19 +++++++++++++++++-- 7 files changed, 41 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 1deeddd3..148e6e84 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -353,7 +353,7 @@ typedef struct afl_env_vars { afl_cal_fast, afl_cycle_schedules, afl_expand_havoc; u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, - *afl_hang_tmout, *afl_skip_crashes, *afl_preload; + *afl_hang_tmout, *afl_skip_crashes, *afl_preload, *afl_max_det_extras; } afl_env_vars_t; @@ -506,7 +506,8 @@ typedef struct afl_state { useless_at_start, /* Number of useless starting paths */ var_byte_count, /* Bitmap bytes with var behavior */ current_entry, /* Current queue entry ID */ - havoc_div; /* Cycle count divisor for havoc */ + havoc_div, /* Cycle count divisor for havoc */ + max_det_extras; /* deterministic extra count (dicts)*/ u64 total_crashes, /* Total number of crashes */ unique_crashes, /* Crashes with unique signatures */ diff --git a/include/envs.h b/include/envs.h index 96ae91ba..4d50d0ff 100644 --- a/include/envs.h +++ b/include/envs.h @@ -102,6 +102,7 @@ static char *afl_environment_variables[] = { "AFL_NO_X86", // not really an env but we dont want to warn on it "AFL_MAP_SIZE", "AFL_MAPSIZE", + "AFL_MAX_DET_EXTRAS", "AFL_PATH", "AFL_PERFORMANCE_FILE", "AFL_PRELOAD", diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 52a14602..9d9e81cd 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -115,7 +115,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->out_file = NULL; fsrv_to->init_child_func = fsrv_exec_child; - //Note: do not copy ->add_extra_func + // Note: do not copy ->add_extra_func list_append(&fsrv_list, fsrv_to); diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 1452c55e..03c5152a 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -248,10 +248,10 @@ static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, } - if (afl->extras_cnt > MAX_DET_EXTRAS) { + if (afl->extras_cnt > afl->max_det_extras) { WARNF("More than %d tokens - will use them probabilistically.", - MAX_DET_EXTRAS); + afl->max_det_extras); } @@ -403,10 +403,10 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { /* We only want to print this once */ - if (afl->extras_cnt == MAX_DET_EXTRAS + 1) { + if (afl->extras_cnt == afl->max_det_extras + 1) { WARNF("More than %d tokens - will use them probabilistically.", - MAX_DET_EXTRAS); + afl->max_det_extras); } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 3bf0c195..c0c036db 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1509,13 +1509,13 @@ skip_interest: for (j = 0; j < afl->extras_cnt; ++j) { - /* Skip extras probabilistically if afl->extras_cnt > MAX_DET_EXTRAS. Also - skip them if there's no room to insert the payload, if the token + /* Skip extras probabilistically if afl->extras_cnt > AFL_MAX_DET_EXTRAS. + Also skip them if there's no room to insert the payload, if the token is redundant, or if its entire span has no bytes set in the effector map. */ - if ((afl->extras_cnt > MAX_DET_EXTRAS && - rand_below(afl, afl->extras_cnt) >= MAX_DET_EXTRAS) || + if ((afl->extras_cnt > afl->max_det_extras && + rand_below(afl, afl->extras_cnt) >= afl->max_det_extras) || afl->extras[j].len > len - i || !memcmp(afl->extras[j].data, out_buf + i, afl->extras[j].len) || !memchr(eff_map + EFF_APOS(i), 1, @@ -3722,13 +3722,13 @@ skip_interest: for (j = 0; j < afl->extras_cnt; ++j) { - /* Skip extras probabilistically if afl->extras_cnt > MAX_DET_EXTRAS. Also - skip them if there's no room to insert the payload, if the token + /* Skip extras probabilistically if afl->extras_cnt > AFL_MAX_DET_EXTRAS. + Also skip them if there's no room to insert the payload, if the token is redundant, or if its entire span has no bytes set in the effector map. */ - if ((afl->extras_cnt > MAX_DET_EXTRAS && - rand_below(afl, afl->extras_cnt) >= MAX_DET_EXTRAS) || + if ((afl->extras_cnt > afl->max_det_extras && + rand_below(afl, afl->extras_cnt) >= afl->max_det_extras) || afl->extras[j].len > len - i || !memcmp(afl->extras[j].data, out_buf + i, afl->extras[j].len) || !memchr(eff_map + EFF_APOS(i), 1, diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index dd0e316c..74798584 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -349,6 +349,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_preload = (u8 *)get_afl_env(afl_environment_variables[i]); + } else if (!strncmp(env, "AFL_MAX_DET_EXTRAS", + + afl_environment_variable_len)) { + + afl->afl_env.afl_max_det_extras = + (u8 *)get_afl_env(afl_environment_variables[i]); + } } else { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 5dd092f2..664cc076 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -177,6 +177,8 @@ static void usage(u8 *argv0, int more_help) { "AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" " the target was compiled for\n" + "AFL_MAX_DET_EXTRAS: if the dict/extras file contains more tokens than this threshold,\n" + " the tokens will sometimes be skipped during fuzzing.\n" "AFL_NO_AFFINITY: do not check for an unused cpu core to use for fuzzing\n" "AFL_NO_ARITH: skip arithmetic mutations in deterministic stage\n" "AFL_NO_CPU_RED: avoid red color for showing very high cpu usage\n" @@ -949,8 +951,21 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->afl_env.afl_hang_tmout) { - afl->hang_tmout = atoi(afl->afl_env.afl_hang_tmout); - if (!afl->hang_tmout) { FATAL("Invalid value of AFL_HANG_TMOUT"); } + s32 hang_tmout = atoi(afl->afl_env.afl_hang_tmout); + if (hang_tmout < 1) { FATAL("Invalid value for AFL_HANG_TMOUT"); } + afl->hang_tmout = (u32)hang_tmout; + + } + + if (afl->afl_env.afl_max_det_extras) { + + s32 max_det_extras = atoi(afl->afl_env.afl_max_det_extras); + if (max_det_extras < 1) { FATAL("Invalid value for AFL_MAX_DET_EXTRAS"); } + afl->max_det_extras = (u32)max_det_extras; + + } else { + + afl->max_det_extras = MAX_DET_EXTRAS; } -- cgit 1.4.1 From 425908a00cdcaa4d49a513d283431b8b6eed0486 Mon Sep 17 00:00:00 2001 From: Marius Muench Date: Sun, 23 Aug 2020 10:39:34 +0200 Subject: Option for specifying forkserver initialization timeout via environment variable (#522) * Addition of AFL_FORKSRV_INIT_TMOUT env var This commit introduces a new environment variable which allows to specify the timespan AFL should wait for initial contact with the forkserver. This is useful for fuzz-targets requiring a rather long setup time before the actual fuzzing can be started (e.g., unicorn). * add .swp files to .gitignore * Inherit init_tmout in afl_fsrv_init_dup Without this patch, the forkserver would spawn with a timeout of 0 in cmplog mode, leading to an immediate crash. Additionally, this commit removes a spurious whitespace. * Initialize afl->fsrv.init_tmout in afl_fsrv_init Not all afl-components will need the new AFL_FORKSRV_INIT_TMOUT environment variable. Hence, it's initialized to the safe "default" value from before in afl_fsrv_init now. --- .gitignore | 1 + include/afl-fuzz.h | 3 ++- include/envs.h | 1 + include/forkserver.h | 1 + src/afl-forkserver.c | 6 ++++-- src/afl-fuzz-state.c | 7 +++++++ src/afl-fuzz.c | 17 +++++++++++++++++ 7 files changed, 33 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/.gitignore b/.gitignore index b2c2fc62..4307fc4c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .sync_tmp *.o *.so +*.swp *.pyc *.dSYM as diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 148e6e84..1f1dda3a 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -353,7 +353,8 @@ typedef struct afl_env_vars { afl_cal_fast, afl_cycle_schedules, afl_expand_havoc; u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, - *afl_hang_tmout, *afl_skip_crashes, *afl_preload, *afl_max_det_extras; + *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload, + *afl_max_det_extras; } afl_env_vars_t; diff --git a/include/envs.h b/include/envs.h index 4d50d0ff..c7761e19 100644 --- a/include/envs.h +++ b/include/envs.h @@ -48,6 +48,7 @@ static char *afl_environment_variables[] = { "AFL_GCC_INSTRUMENT_FILE", "AFL_GCJ", "AFL_HANG_TMOUT", + "AFL_FORKSRV_INIT_TMOUT", "AFL_HARDEN", "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES", "AFL_IMPORT_FIRST", diff --git a/include/forkserver.h b/include/forkserver.h index d824c1c9..300ecffc 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -56,6 +56,7 @@ typedef struct afl_forkserver { u8 no_unlink; /* do not unlink cur_input */ u32 exec_tmout; /* Configurable exec timeout (ms) */ + u32 init_tmout; /* Configurable init timeout (ms) */ u32 map_size; /* map size used by the target */ u32 snapshot; /* is snapshot feature used */ u64 mem_limit; /* Memory cap for child (MB) */ diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 9d9e81cd..51734579 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -79,6 +79,7 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->use_stdin = 1; fsrv->no_unlink = 0; fsrv->exec_tmout = EXEC_TIMEOUT; + fsrv->init_tmout = EXEC_TIMEOUT * FORK_WAIT_MULT; fsrv->mem_limit = MEM_LIMIT; fsrv->out_file = NULL; @@ -101,6 +102,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->out_fd = from->out_fd; fsrv_to->dev_null_fd = from->dev_null_fd; fsrv_to->exec_tmout = from->exec_tmout; + fsrv_to->init_tmout = from->init_tmout; fsrv_to->mem_limit = from->mem_limit; fsrv_to->map_size = from->map_size; fsrv_to->support_shmem_fuzz = from->support_shmem_fuzz; @@ -519,13 +521,13 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, u32 time_ms = read_s32_timed(fsrv->fsrv_st_fd, &status, - fsrv->exec_tmout * FORK_WAIT_MULT, stop_soon_p); + fsrv->init_tmout, stop_soon_p); if (!time_ms) { kill(fsrv->fsrv_pid, SIGKILL); - } else if (time_ms > fsrv->exec_tmout * FORK_WAIT_MULT) { + } else if (time_ms > fsrv->init_tmout) { fsrv->last_run_timed_out = 1; kill(fsrv->fsrv_pid, SIGKILL); diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 74798584..a8db8578 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -356,6 +356,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_max_det_extras = (u8 *)get_afl_env(afl_environment_variables[i]); + } else if (!strncmp(env, "AFL_FORKSRV_INIT_TMOUT", + + afl_environment_variable_len)) { + + afl->afl_env.afl_forksrv_init_tmout = + (u8 *) get_afl_env(afl_environment_variables[i]); + } } else { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 664cc076..ae5cb087 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -173,6 +173,7 @@ static void usage(u8 *argv0, int more_help) { "AFL_FAST_CAL: limit the calibration stage to three cycles for speedup\n" "AFL_FORCE_UI: force showing the status screen (for virtual consoles)\n" "AFL_HANG_TMOUT: override timeout value (in milliseconds)\n" + "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: don't warn about core dump handlers\n" "AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" @@ -969,6 +970,22 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->afl_env.afl_forksrv_init_tmout) { + + afl->fsrv.init_tmout = atoi(afl->afl_env.afl_forksrv_init_tmout); + if (!afl->fsrv.init_tmout) { + + FATAL("Invalid value of AFL_FORKSRV_INIT_TMOUT"); + + } + + } else { + + afl->fsrv.init_tmout = afl->fsrv.exec_tmout * FORK_WAIT_MULT; + + } + + if (afl->non_instrumented_mode == 2 && afl->no_forkserver) { FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); -- cgit 1.4.1 From e2b54bfa0540d074423260bec01a544e9beda1df Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 23 Aug 2020 10:40:46 +0200 Subject: code format --- src/afl-forkserver.c | 5 ++--- src/afl-fuzz-state.c | 2 +- src/afl-fuzz.c | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 51734579..c496975f 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -519,9 +519,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, rlen = 0; if (fsrv->exec_tmout) { - u32 time_ms = - read_s32_timed(fsrv->fsrv_st_fd, &status, - fsrv->init_tmout, stop_soon_p); + u32 time_ms = read_s32_timed(fsrv->fsrv_st_fd, &status, fsrv->init_tmout, + stop_soon_p); if (!time_ms) { diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index a8db8578..577fc34f 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -361,7 +361,7 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl_environment_variable_len)) { afl->afl_env.afl_forksrv_init_tmout = - (u8 *) get_afl_env(afl_environment_variables[i]); + (u8 *)get_afl_env(afl_environment_variables[i]); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index ae5cb087..1abd49d8 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -985,7 +985,6 @@ int main(int argc, char **argv_orig, char **envp) { } - if (afl->non_instrumented_mode == 2 && afl->no_forkserver) { FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); -- cgit 1.4.1 From 6184832ea98050c91cdefa5059e18e78c39b14de Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 23 Aug 2020 10:59:56 +0200 Subject: added more env var docs, fsrv fixes for cmin, tmin --- docs/Changelog.md | 4 ++++ docs/env_variables.md | 15 +++++++++++++++ src/afl-showmap.c | 10 ++++++++++ src/afl-tmin.c | 10 ++++++++++ 4 files changed, 39 insertions(+) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 8bbb4e19..f7bc9600 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,10 @@ sending a mail to . - Fix for auto dictionary entries found during fuzzing to not throw out a -x dictionary - added total execs done to plot file + - AFL_MAX_DET_EXTRAS env variable added to control the amount of deterministic + dict entries without recompiling. + - AFL_FORKSRV_INIT_TMOUT env variable added to control the time to wait for + the forkserver to come up without the need to increase the overall timeout. - llvm_mode: - Ported SanCov to LTO, and made it the default for LTO. better instrumentation locations diff --git a/docs/env_variables.md b/docs/env_variables.md index 94c34400..e8129a3f 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -278,6 +278,14 @@ checks or alter some of the more exotic semantics of the tool: don't want AFL to spend too much time classifying that stuff and just rapidly put all timeouts in that bin. + - Setting AFL_FORKSRV_INIT_TMOUT allows yout to specify a different timeout + to wait for the forkserver to spin up. The default is the `-t` value times + `FORK_WAIT_MULT` from `config.h` (usually 10), so for a `-t 100`, the + default would wait `1000` milis. Setting a different time here is useful + if the target has a very slow startup time, for example when doing + full-system fuzzing or emulation, but you don't want the actual runs + to wait too long for timeouts. + - AFL_NO_ARITH causes AFL to skip most of the deterministic arithmetics. This can be useful to speed up the fuzzing of text-based file formats. @@ -361,6 +369,13 @@ checks or alter some of the more exotic semantics of the tool: for an existing out folder, even if a different `-i` was provided. Without this setting, afl-fuzz will refuse execution for a long-fuzzed out dir. + - Setting AFL_MAX_DET_EXRAS will change the threshold at what number of elements + in the `-x` dictionary and LTO autodict (combined) the probabilistic mode will + kick off. In probabilistic mode, not all dictionary entires will be used all + of the times for fuzzing mutations to not make fuzzing slower by it. + The default count is `200` element. So for the 200 + 1st element, there is a + 1 in 201 chance, that one of the dictionary entry will not be used directly. + - Setting AFL_NO_FORKSRV disables the forkserver optimization, reverting to fork + execve() call for every tested input. This is useful mostly when working with unruly libraries that create threads or do other crazy diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 47c615d8..2c20e419 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1036,6 +1036,16 @@ int main(int argc, char **argv_orig, char **envp) { } + if (getenv("AFL_FORKSRV_INIT_TMOUT")) { + + s32 forksrv_init_tmout = atoi(getenv("AFL_FORKSRV_INIT_TMOUT")); + if (forksrv_init_tmout < 1) { + FATAL("Bad value specified for AFL_FORKSRV_INIT_TMOUT"); + } + fsrv->init_tmout = (u32) forksrv_init_tmout; + + } + afl_fsrv_start(fsrv, use_argv, &stop_soon, get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0); map_size = fsrv->map_size; diff --git a/src/afl-tmin.c b/src/afl-tmin.c index b50d8597..a15a6079 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -1103,6 +1103,16 @@ int main(int argc, char **argv_orig, char **envp) { } SAYF("\n"); + + if (getenv("AFL_FORKSRV_INIT_TMOUT")) { + + s32 forksrv_init_tmout = atoi(getenv("AFL_FORKSRV_INIT_TMOUT")); + if (forksrv_init_tmout < 1) { + FATAL("Bad value specified for AFL_FORKSRV_INIT_TMOUT"); + } + fsrv->init_tmout = (u32) forksrv_init_tmout; + + } shm_fuzz = ck_alloc(sizeof(sharedmem_t)); -- cgit 1.4.1 From 4d9d52e3d9b202d5da999897a0e8c574f453cf7a Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 23 Aug 2020 11:00:46 +0200 Subject: code format --- src/afl-showmap.c | 5 ++++- src/afl-tmin.c | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 2c20e419..64b52479 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1040,9 +1040,12 @@ int main(int argc, char **argv_orig, char **envp) { s32 forksrv_init_tmout = atoi(getenv("AFL_FORKSRV_INIT_TMOUT")); if (forksrv_init_tmout < 1) { + FATAL("Bad value specified for AFL_FORKSRV_INIT_TMOUT"); + } - fsrv->init_tmout = (u32) forksrv_init_tmout; + + fsrv->init_tmout = (u32)forksrv_init_tmout; } diff --git a/src/afl-tmin.c b/src/afl-tmin.c index a15a6079..59269f45 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -1103,14 +1103,17 @@ int main(int argc, char **argv_orig, char **envp) { } SAYF("\n"); - + if (getenv("AFL_FORKSRV_INIT_TMOUT")) { s32 forksrv_init_tmout = atoi(getenv("AFL_FORKSRV_INIT_TMOUT")); if (forksrv_init_tmout < 1) { + FATAL("Bad value specified for AFL_FORKSRV_INIT_TMOUT"); + } - fsrv->init_tmout = (u32) forksrv_init_tmout; + + fsrv->init_tmout = (u32)forksrv_init_tmout; } -- cgit 1.4.1 From a1442bd1ac2be2fc20b90c37a71e7c3b997fd35b Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 23 Aug 2020 11:21:49 +0200 Subject: no longer warns for prob. extras --- src/afl-fuzz-extras.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 03c5152a..2b9854d4 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -250,7 +250,7 @@ static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, if (afl->extras_cnt > afl->max_det_extras) { - WARNF("More than %d tokens - will use them probabilistically.", + OKF("More than %d tokens - will use them probabilistically.", afl->max_det_extras); } @@ -370,7 +370,7 @@ static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) { } -/* Adds a new extra / dict entry. */ +/* Adds a new extra / dict entry. Used for LTO autodict. */ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; @@ -405,7 +405,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { if (afl->extras_cnt == afl->max_det_extras + 1) { - WARNF("More than %d tokens - will use them probabilistically.", + OKF("More than %d tokens - will use them probabilistically.", afl->max_det_extras); } -- cgit 1.4.1 From b9b6f064297839103d8b2ae06accfda2a3dc6aa1 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 24 Aug 2020 12:04:29 +0200 Subject: Allow Large Extras (#523) * allow large extras * skipping large testcases again --- src/afl-fuzz-extras.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 2b9854d4..8cc2425f 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -319,8 +319,8 @@ void load_extras(afl_state_t *afl, u8 *dir) { if (st.st_size > MAX_DICT_FILE) { - FATAL( - "Extra '%s' is too big (%s, limit is %s)", fn, + WARNF( + "Extra '%s' is very big (%s, limit is %s)", fn, stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), st.st_size), stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); @@ -377,7 +377,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { if (len > MAX_DICT_FILE) { - FATAL("Extra '%.*s' is too big (%s, limit is %s)", (int)len, mem, + WARNF("Extra '%.*s' is very big (%s, limit is %s)", (int)len, mem, stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), len), stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); -- cgit 1.4.1 From c7f0d3066875bca0ec28e9429df40293339dc05c Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 24 Aug 2020 17:32:41 +0200 Subject: added afl_custom_fuzz_count --- GNUmakefile | 12 ++--- docs/Changelog.md | 3 ++ docs/custom_mutators.md | 9 ++++ include/afl-fuzz.h | 20 ++++++++ src/afl-forkserver.c | 2 +- src/afl-fuzz-extras.c | 4 +- src/afl-fuzz-mutators.c | 5 ++ src/afl-fuzz-one.c | 129 +++++++++++++++++++++++++++--------------------- src/afl-fuzz-python.c | 44 +++++++++++++++++ 9 files changed, 162 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index 3c5e10ed..cae172dd 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -37,18 +37,18 @@ MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8) afl-as.8 ASAN_OPTIONS=detect_leaks=0 ifeq "$(findstring android, $(shell $(CC) --version 2>/dev/null))" "" -ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" CFLAGS_FLTO ?= -flto=full -else - ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" - CFLAGS_FLTO ?= -flto=thin else - ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + CFLAGS_FLTO ?= -flto=thin + else + ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" CFLAGS_FLTO ?= -flto + endif endif endif endif -endif ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -fno-move-loop-invariants -fdisable-tree-cunrolli -x c - -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" SPECIAL_PERFORMANCE += -fno-move-loop-invariants -fdisable-tree-cunrolli diff --git a/docs/Changelog.md b/docs/Changelog.md index f7bc9600..45fbd528 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -18,6 +18,9 @@ sending a mail to . dict entries without recompiling. - AFL_FORKSRV_INIT_TMOUT env variable added to control the time to wait for the forkserver to come up without the need to increase the overall timeout. + - custom mutators: + - added afl_custom_fuzz_count/fuzz_count function to allow specifying the + number of fuzz attempts for custom_fuzz - llvm_mode: - Ported SanCov to LTO, and made it the default for LTO. better instrumentation locations diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index a22c809b..75dbea21 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -32,6 +32,7 @@ performed with the custom mutator. C/C++: ```c void *afl_custom_init(afl_t *afl, unsigned int seed); +uint32_t afl_custom_fuzz_count(void *data, const u8 *buf, size_t buf_size); size_t afl_custom_fuzz(void *data, uint8_t *buf, size_t buf_size, u8 **out_buf, uint8_t *add_buf, size_t add_buf_size, size_t max_size); size_t afl_custom_post_process(void *data, uint8_t *buf, size_t buf_size, uint8_t **out_buf); int32_t afl_custom_init_trim(void *data, uint8_t *buf, size_t buf_size); @@ -49,6 +50,9 @@ Python: def init(seed): pass +def fuzz_count(buf, add_buf, max_size): + return cnt + def fuzz(buf, add_buf, max_size): return mutated_out @@ -88,6 +92,11 @@ def queue_new_entry(filename_new_queue, filename_orig_queue): This method determines whether the custom fuzzer should fuzz the current queue entry or not +- `fuzz_count` (optional): + + This method can be used to instruct afl-fuzz how often to perform a fuzz + attempt on this input data. + - `fuzz` (optional): This method performs custom mutations on a given input. It also accepts an diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 1f1dda3a..01aa1a73 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -288,6 +288,7 @@ enum { enum { /* 00 */ PY_FUNC_INIT, + /* 01 */ PY_FUNC_FUZZ_COUNT, /* 01 */ PY_FUNC_FUZZ, /* 02 */ PY_FUNC_POST_PROCESS, /* 03 */ PY_FUNC_INIT_TRIM, @@ -679,6 +680,24 @@ struct custom_mutator { */ void *(*afl_custom_init)(afl_state_t *afl, unsigned int seed); + /** + * This method is called just before fuzzing a queue entry with the custom + * mutator, and receives the initial buffer. It should return the number of + * fuzzes to perform. + * + * A value of 0 means no fuzzing of this queue entry. + * + * The function is now allowed to change the data. + * + * (Optional) + * + * @param data pointer returned in afl_custom_init for this fuzz case + * @param buf Buffer containing the test case + * @param buf_size Size of the test case + * @return The amount of fuzzes to perform on this queue entry, 0 = skip + */ + u32 (*afl_custom_fuzz_count)(void *data, const u8 *buf, size_t buf_size); + /** * Perform custom mutations on a given input * @@ -867,6 +886,7 @@ u8 trim_case_custom(afl_state_t *, struct queue_entry *q, u8 *in_buf, struct custom_mutator *load_custom_mutator_py(afl_state_t *, char *); void finalize_py_module(void *); +u32 fuzz_count_py(void *, const u8 *, size_t); size_t post_process_py(void *, u8 *, size_t, u8 **); s32 init_trim_py(void *, u8 *, size_t); s32 post_trim_py(void *, u8); diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index c496975f..72f3dc3b 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -634,7 +634,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) { - // this is not afl-fuzz - we deny and return + // this is not afl-fuzz - or it is cmplog - we deny and return if (fsrv->use_shmem_fuzz) { status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 8cc2425f..d6c368d1 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -251,7 +251,7 @@ static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, if (afl->extras_cnt > afl->max_det_extras) { OKF("More than %d tokens - will use them probabilistically.", - afl->max_det_extras); + afl->max_det_extras); } @@ -406,7 +406,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { if (afl->extras_cnt == afl->max_det_extras + 1) { OKF("More than %d tokens - will use them probabilistically.", - afl->max_det_extras); + afl->max_det_extras); } diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index 22578df9..d24b7db9 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -166,6 +166,11 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { } + /* "afl_custom_fuzz_count", optional */ + mutator->afl_custom_fuzz_count = dlsym(dh, "afl_custom_fuzz_count"); + if (!mutator->afl_custom_fuzz_count) + ACTF("optional symbol 'afl_custom_fuzz_count' not found."); + /* "afl_custom_deinit", optional for backward compatibility */ mutator->afl_custom_deinit = dlsym(dh, "afl_custom_deinit"); if (!mutator->afl_custom_deinit) diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index c0c036db..03c0d3a1 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1672,7 +1672,7 @@ custom_mutator_stage: if (afl->stage_max < HAVOC_MIN) { afl->stage_max = HAVOC_MIN; } - const u32 max_seed_size = MAX_FILE; + const u32 max_seed_size = MAX_FILE, saved_max = afl->stage_max; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; @@ -1680,104 +1680,119 @@ custom_mutator_stage: if (el->afl_custom_fuzz) { + if (el->afl_custom_fuzz_count) + afl->stage_max = el->afl_custom_fuzz_count(el->data, out_buf, len); + else + afl->stage_max = saved_max; + has_custom_fuzz = true; afl->stage_short = el->name_short; - for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; - ++afl->stage_cur) { + if (afl->stage_max) { - struct queue_entry *target; - u32 tid; - u8 * new_buf; + for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; + ++afl->stage_cur) { - retry_external_pick: - /* Pick a random other queue entry for passing to external API */ + struct queue_entry *target; + u32 tid; + u8 * new_buf; - do { + retry_external_pick: + /* Pick a random other queue entry for passing to external API */ - tid = rand_below(afl, afl->queued_paths); + do { - } while (tid == afl->current_entry && afl->queued_paths > 1); + tid = rand_below(afl, afl->queued_paths); - target = afl->queue; + } while (tid == afl->current_entry && afl->queued_paths > 1); - while (tid >= 100) { + target = afl->queue; - target = target->next_100; - tid -= 100; + while (tid >= 100) { - } - - while (tid--) { + target = target->next_100; + tid -= 100; - target = target->next; + } - } + while (tid--) { - /* Make sure that the target has a reasonable length. */ + target = target->next; - while (target && (target->len < 2 || target == afl->queue_cur) && - afl->queued_paths > 3) { + } - target = target->next; - ++afl->splicing_with; + /* Make sure that the target has a reasonable length. */ - } + while (target && (target->len < 2 || target == afl->queue_cur) && + afl->queued_paths > 3) { - if (!target) { goto retry_external_pick; } + target = target->next; + ++afl->splicing_with; - /* Read the additional testcase into a new buffer. */ - fd = open(target->fname, O_RDONLY); - if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } + } - new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), target->len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - ck_read(fd, new_buf, target->len, target->fname); - close(fd); + if (!target) { goto retry_external_pick; } - u8 *mutated_buf = NULL; + /* Read the additional testcase into a new buffer. */ + fd = open(target->fname, O_RDONLY); + if (unlikely(fd < 0)) { - size_t mutated_size = - el->afl_custom_fuzz(el->data, out_buf, len, &mutated_buf, new_buf, - target->len, max_seed_size); + PFATAL("Unable to open '%s'", target->fname); - if (unlikely(!mutated_buf)) { + } - FATAL("Error in custom_fuzz. Size returned: %zd", mutated_size); + new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), target->len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } + ck_read(fd, new_buf, target->len, target->fname); + close(fd); - } + u8 *mutated_buf = NULL; - if (mutated_size > 0) { + size_t mutated_size = + el->afl_custom_fuzz(el->data, out_buf, len, &mutated_buf, new_buf, + target->len, max_seed_size); - if (common_fuzz_stuff(afl, mutated_buf, (u32)mutated_size)) { + if (unlikely(!mutated_buf)) { - goto abandon_entry; + FATAL("Error in custom_fuzz. Size returned: %zd", mutated_size); } - /* If we're finding new stuff, let's run for a bit longer, limits - permitting. */ - - if (afl->queued_paths != havoc_queued) { + if (mutated_size > 0) { - if (perf_score <= afl->havoc_max_mult * 100) { + if (common_fuzz_stuff(afl, mutated_buf, (u32)mutated_size)) { - afl->stage_max *= 2; - perf_score *= 2; + goto abandon_entry; } - havoc_queued = afl->queued_paths; + /* If we're finding new stuff, let's run for a bit longer, limits + permitting. */ + + if (afl->queued_paths != havoc_queued) { + + if (perf_score <= afl->havoc_max_mult * 100) { + + afl->stage_max *= 2; + perf_score *= 2; + + } + + havoc_queued = afl->queued_paths; + + } } - } + /* `(afl->)out_buf` may have been changed by the call to custom_fuzz + */ + /* TODO: Only do this when `mutated_buf` == `out_buf`? Branch vs + * Memcpy. + */ + memcpy(out_buf, in_buf, len); - /* `(afl->)out_buf` may have been changed by the call to custom_fuzz */ - /* TODO: Only do this when `mutated_buf` == `out_buf`? Branch vs Memcpy. - */ - memcpy(out_buf, in_buf, len); + } } diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index e540f548..68540dd7 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -347,6 +347,12 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, } + if (py_functions[PY_FUNC_FUZZ_COUNT]) { + + mutator->afl_custom_fuzz_count = fuzz_count_py; + + } + if (py_functions[PY_FUNC_POST_TRIM]) { mutator->afl_custom_post_trim = post_trim_py; @@ -477,6 +483,44 @@ s32 init_trim_py(void *py_mutator, u8 *buf, size_t buf_size) { } +u32 fuzz_count_py(void *py_mutator, const u8 *buf, size_t buf_size) { + + PyObject *py_args, *py_value; + + py_args = PyTuple_New(1); + py_value = PyByteArray_FromStringAndSize(buf, buf_size); + if (!py_value) { + + Py_DECREF(py_args); + FATAL("Failed to convert arguments"); + + } + + PyTuple_SetItem(py_args, 0, py_value); + + py_value = PyObject_CallObject( + ((py_mutator_t *)py_mutator)->py_functions[PY_FUNC_FUZZ_COUNT], py_args); + Py_DECREF(py_args); + + if (py_value != NULL) { + + #if PY_MAJOR_VERSION >= 3 + u32 retcnt = (u32)PyLong_AsLong(py_value); + #else + u32 retcnt = PyInt_AsLong(py_value); + #endif + Py_DECREF(py_value); + return retcnt; + + } else { + + PyErr_Print(); + FATAL("Call failed"); + + } + +} + s32 post_trim_py(void *py_mutator, u8 success) { PyObject *py_args, *py_value; -- cgit 1.4.1 From 6a34c5aa3e0a1da06a5268ecabfc92ab36ce96dc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 24 Aug 2020 18:06:07 +0200 Subject: fix python implementation for new function --- include/afl-fuzz.h | 2 +- src/afl-fuzz-python.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 01aa1a73..91915bf6 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -288,7 +288,6 @@ enum { enum { /* 00 */ PY_FUNC_INIT, - /* 01 */ PY_FUNC_FUZZ_COUNT, /* 01 */ PY_FUNC_FUZZ, /* 02 */ PY_FUNC_POST_PROCESS, /* 03 */ PY_FUNC_INIT_TRIM, @@ -299,6 +298,7 @@ enum { /* 08 */ PY_FUNC_QUEUE_GET, /* 09 */ PY_FUNC_QUEUE_NEW_ENTRY, /* 10 */ PY_FUNC_DEINIT, + /* 11 */ PY_FUNC_FUZZ_COUNT, PY_FUNC_COUNT }; diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index 68540dd7..32c8ac89 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -144,6 +144,7 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "fuzz"); if (!py_functions[PY_FUNC_FUZZ]) py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "mutate"); + py_functions[PY_FUNC_FUZZ_COUNT] = PyObject_GetAttrString(py_module, "fuzz_count"); if (!py_functions[PY_FUNC_FUZZ]) WARNF("fuzz function not found in python module"); py_functions[PY_FUNC_POST_PROCESS] = @@ -187,7 +188,7 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { // Implenting the havoc and queue API is optional for now if (PyErr_Occurred()) { PyErr_Print(); } - } else { + } else if (py_idx != PY_FUNC_FUZZ_COUNT) { if (PyErr_Occurred()) { PyErr_Print(); } fprintf(stderr, -- cgit 1.4.1 From 19eddbb0c76406db6d790681ef519c4d91e8814e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 24 Aug 2020 18:12:08 +0200 Subject: make py functions as optional as they are in the doc --- include/afl-fuzz.h | 24 +++++++++++++----------- src/afl-fuzz-python.c | 18 ++++++------------ 2 files changed, 19 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 91915bf6..24e8ca9b 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -288,17 +288,19 @@ enum { enum { /* 00 */ PY_FUNC_INIT, - /* 01 */ PY_FUNC_FUZZ, - /* 02 */ PY_FUNC_POST_PROCESS, - /* 03 */ PY_FUNC_INIT_TRIM, - /* 04 */ PY_FUNC_POST_TRIM, - /* 05 */ PY_FUNC_TRIM, - /* 06 */ PY_FUNC_HAVOC_MUTATION, - /* 07 */ PY_FUNC_HAVOC_MUTATION_PROBABILITY, - /* 08 */ PY_FUNC_QUEUE_GET, - /* 09 */ PY_FUNC_QUEUE_NEW_ENTRY, - /* 10 */ PY_FUNC_DEINIT, - /* 11 */ PY_FUNC_FUZZ_COUNT, + /* 01 */ PY_FUNC_DEINIT, + /* FROM HERE ON BELOW ALL ARE OPTIONAL */ + /* 02 */ PY_OPTIONAL = 2, + /* 02 */ PY_FUNC_FUZZ = 2, + /* 03 */ PY_FUNC_FUZZ_COUNT, + /* 04 */ PY_FUNC_POST_PROCESS, + /* 05 */ PY_FUNC_INIT_TRIM, + /* 06 */ PY_FUNC_POST_TRIM, + /* 07 */ PY_FUNC_TRIM, + /* 08 */ PY_FUNC_HAVOC_MUTATION, + /* 09 */ PY_FUNC_HAVOC_MUTATION_PROBABILITY, + /* 10 */ PY_FUNC_QUEUE_GET, + /* 11 */ PY_FUNC_QUEUE_NEW_ENTRY, PY_FUNC_COUNT }; diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index 32c8ac89..adb92649 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -144,7 +144,8 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "fuzz"); if (!py_functions[PY_FUNC_FUZZ]) py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "mutate"); - py_functions[PY_FUNC_FUZZ_COUNT] = PyObject_GetAttrString(py_module, "fuzz_count"); + py_functions[PY_FUNC_FUZZ_COUNT] = + PyObject_GetAttrString(py_module, "fuzz_count"); if (!py_functions[PY_FUNC_FUZZ]) WARNF("fuzz function not found in python module"); py_functions[PY_FUNC_POST_PROCESS] = @@ -170,27 +171,20 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { if (!py_functions[py_idx] || !PyCallable_Check(py_functions[py_idx])) { - if (py_idx == PY_FUNC_POST_PROCESS) { - - // Implenting the post_process API is optional for now - if (PyErr_Occurred()) { PyErr_Print(); } - - } else if (py_idx >= PY_FUNC_INIT_TRIM && py_idx <= PY_FUNC_TRIM) { + if (py_idx >= PY_FUNC_INIT_TRIM && py_idx <= PY_FUNC_TRIM) { // Implementing the trim API is optional for now if (PyErr_Occurred()) { PyErr_Print(); } py_notrim = 1; - } else if ((py_idx >= PY_FUNC_HAVOC_MUTATION) && + } else if (py_idx >= PY_OPTIONAL) { - (py_idx <= PY_FUNC_QUEUE_NEW_ENTRY)) { + // Only _init and _deinit are not optional currently - // Implenting the havoc and queue API is optional for now if (PyErr_Occurred()) { PyErr_Print(); } - } else if (py_idx != PY_FUNC_FUZZ_COUNT) { + } else { - if (PyErr_Occurred()) { PyErr_Print(); } fprintf(stderr, "Cannot find/call function with index %d in external " "Python module.\n", -- cgit 1.4.1 From 1efc6e59b7cd2a3623ad3d75622e8bf107b3dc98 Mon Sep 17 00:00:00 2001 From: Sergio Paganoni Date: Mon, 24 Aug 2020 21:18:51 +0200 Subject: Added out_file value when using stdio (#524) --- src/afl-forkserver.c | 4 ++-- src/afl-fuzz-init.c | 13 +++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 72f3dc3b..93203cb2 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -941,7 +941,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { s32 fd = fsrv->out_fd; - if (fsrv->out_file) { + if (!fsrv->use_stdin) { if (fsrv->no_unlink) { @@ -964,7 +964,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { ck_write(fd, buf, len, fsrv->out_file); - if (!fsrv->out_file) { + if (fsrv->use_stdin) { if (ftruncate(fd, len)) { PFATAL("ftruncate() failed"); } lseek(fd, 0, SEEK_SET); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 350a8599..7b7ba006 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -1841,24 +1841,21 @@ void setup_cmdline_file(afl_state_t *afl, char **argv) { void setup_stdio_file(afl_state_t *afl) { - u8 *fn; if (afl->file_extension) { - fn = alloc_printf("%s/.cur_input.%s", afl->tmp_dir, afl->file_extension); + afl->fsrv.out_file = alloc_printf("%s/.cur_input.%s", afl->tmp_dir, afl->file_extension); } else { - fn = alloc_printf("%s/.cur_input", afl->tmp_dir); + afl->fsrv.out_file = alloc_printf("%s/.cur_input", afl->tmp_dir); } - unlink(fn); /* Ignore errors */ + unlink(afl->fsrv.out_file); /* Ignore errors */ - afl->fsrv.out_fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600); + afl->fsrv.out_fd = open(afl->fsrv.out_file, O_RDWR | O_CREAT | O_EXCL, 0600); - if (afl->fsrv.out_fd < 0) { PFATAL("Unable to create '%s'", fn); } - - ck_free(fn); + if (afl->fsrv.out_fd < 0) { PFATAL("Unable to create '%s'", afl->fsrv.out_file); } } -- cgit 1.4.1 From 9a6a32775f03e6fbd6df131742bee4c30bcd94a6 Mon Sep 17 00:00:00 2001 From: h1994st Date: Thu, 27 Aug 2020 00:32:53 -0400 Subject: Prevent afl-fuzz from modifying stage_max during fuzzing --- src/afl-fuzz-one.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 03c0d3a1..bf568c38 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1767,19 +1767,23 @@ custom_mutator_stage: } - /* If we're finding new stuff, let's run for a bit longer, limits - permitting. */ + if (!el->afl_custom_fuzz_count) { - if (afl->queued_paths != havoc_queued) { + /* If we're finding new stuff, let's run for a bit longer, limits + permitting. */ - if (perf_score <= afl->havoc_max_mult * 100) { + if (afl->queued_paths != havoc_queued) { - afl->stage_max *= 2; - perf_score *= 2; + if (perf_score <= afl->havoc_max_mult * 100) { - } + afl->stage_max *= 2; + perf_score *= 2; + + } - havoc_queued = afl->queued_paths; + havoc_queued = afl->queued_paths; + + } } -- cgit 1.4.1 From 41bb359428e4559821c95831f25c772d1a8403d9 Mon Sep 17 00:00:00 2001 From: Raphaël Hertzog Date: Fri, 28 Aug 2020 23:04:25 +0200 Subject: Fix various spelling errors (#532) All those spelling errors have been caught by lintian's built-in spellchecker: https://lintian.debian.org/tags/spelling-error-in-binary.html --- docs/Changelog.md | 2 +- llvm_mode/afl-llvm-rt.o.c | 2 +- llvm_mode/split-compares-pass.so.cc | 8 ++++---- src/afl-fuzz.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 45fbd528..cb6e14b8 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -416,7 +416,7 @@ sending a mail to . - big code refactoring: * all includes are now in include/ * all afl sources are now in src/ - see src/README.md - * afl-fuzz was splitted up in various individual files for including + * afl-fuzz was split up in various individual files for including functionality in other programs (e.g. forkserver, memory map, etc.) for better readability. * new code indention everywhere diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index d00fd26f..bdafbe0b 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -339,7 +339,7 @@ static void __afl_map_shm(void) { if (__afl_area_ptr == MAP_FAILED) { - fprintf(stderr, "can not aquire mmap for address %p\n", + fprintf(stderr, "can not acquire mmap for address %p\n", (void *)__afl_map_addr); exit(1); diff --git a/llvm_mode/split-compares-pass.so.cc b/llvm_mode/split-compares-pass.so.cc index 617b55de..2e57a30a 100644 --- a/llvm_mode/split-compares-pass.so.cc +++ b/llvm_mode/split-compares-pass.so.cc @@ -1272,7 +1272,7 @@ bool SplitComparesTransform::runOnModule(Module &M) { if (!be_quiet) { errs() << "Split-floatingpoint-compare-pass: " << count - << " FP comparisons splitted\n"; + << " FP comparisons split\n"; } @@ -1290,7 +1290,7 @@ bool SplitComparesTransform::runOnModule(Module &M) { count = splitIntCompares(M, bitw); if (!be_quiet) errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " splitted\n"; + << " split\n"; bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ @@ -1301,7 +1301,7 @@ bool SplitComparesTransform::runOnModule(Module &M) { count = splitIntCompares(M, bitw); if (!be_quiet) errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " splitted\n"; + << " split\n"; bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ @@ -1312,7 +1312,7 @@ bool SplitComparesTransform::runOnModule(Module &M) { count = splitIntCompares(M, bitw); if (!be_quiet) errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " splitted\n"; + << " split\n"; bitw >>= 1; break; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 1abd49d8..5ad2ace9 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -191,7 +191,7 @@ static void usage(u8 *argv0, int more_help) { "AFL_QUIET: suppress forkserver status messages\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_SHUFFLE_QUEUE: reorder the input queue randomly on startup\n" - "AFL_SKIP_BIN_CHECK: skip the check, if the target is an excutable\n" + "AFL_SKIP_BIN_CHECK: skip the check, if the target is an executable\n" "AFL_SKIP_CPUFREQ: do not warn about variable cpu clocking\n" "AFL_SKIP_CRASHES: during initial dry run do not terminate for crashing inputs\n" "AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)\n" -- cgit 1.4.1 From 4566bcf122c251c023abce0683666921bd4df755 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 31 Aug 2020 10:57:01 +0200 Subject: code-format --- include/afl-fuzz.h | 8 ++++++-- llvm_mode/afl-clang-fast.c | 8 ++++++++ llvm_mode/afl-llvm-common.cc | 8 ++------ llvm_mode/afl-llvm-lto-instrumentation.so.cc | 8 +++----- src/afl-fuzz-init.c | 11 ++++++++--- 5 files changed, 27 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index c5b01da8..97e60347 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1027,11 +1027,15 @@ static inline u32 rand_below(afl_state_t *afl, u32 limit) { } - /* Modulo is biased - we don't want our fuzzing to be biased so let's do it right. */ - u64 unbiased_rnd; + /* Modulo is biased - we don't want our fuzzing to be biased so let's do it + * right. */ + u64 unbiased_rnd; do { + unbiased_rnd = rand_next(afl); + } while (unlikely(unbiased_rnd >= (UINT64_MAX - (UINT64_MAX % limit)))); + return unbiased_rnd % limit; } diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 6ea98111..173dc268 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -812,16 +812,24 @@ int main(int argc, char **argv, char **envp) { ptr += strlen("ngram"); while (*ptr && (*ptr < '0' || *ptr > '9')) { + ptr++; + } + if (!*ptr) { + ptr = getenv("AFL_LLVM_NGRAM_SIZE"); if (!ptr || !*ptr) { + FATAL( "you must set the NGRAM size with (e.g. for value 2) " "AFL_LLVM_INSTRUMENT=ngram-2"); + } + } + ngram_size = atoi(ptr); if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) FATAL( diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc index 7dd5a02a..189b4ec6 100644 --- a/llvm_mode/afl-llvm-common.cc +++ b/llvm_mode/afl-llvm-common.cc @@ -344,14 +344,10 @@ static std::string getSourceName(llvm::Function *F) { (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) if (Loc) { - StringRef instFilename; + StringRef instFilename; DILocation *cDILoc = dyn_cast(Loc.getAsMDNode()); - if (cDILoc) { - - instFilename = cDILoc->getFilename(); - - } + if (cDILoc) { instFilename = cDILoc->getFilename(); } if (instFilename.str().empty()) { diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index c25cad9d..b8d9fce9 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -379,11 +379,9 @@ bool AFLLTOPass::runOnModule(Module &M) { else Str1 = TmpStr.str(); bool HasStr2 = getConstantStringInfo(Str2P, TmpStr); - if (TmpStr.empty()) - HasStr2 = false; - (void) HasStr2 /* never read */ - else - Str2 = TmpStr.str(); + if (TmpStr.empty()) HasStr2 = false; + (void)HasStr2 /* never read */ + else Str2 = TmpStr.str(); if (debug) fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n", diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 7b7ba006..852fc3fb 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -1843,7 +1843,8 @@ void setup_stdio_file(afl_state_t *afl) { if (afl->file_extension) { - afl->fsrv.out_file = alloc_printf("%s/.cur_input.%s", afl->tmp_dir, afl->file_extension); + afl->fsrv.out_file = + alloc_printf("%s/.cur_input.%s", afl->tmp_dir, afl->file_extension); } else { @@ -1851,11 +1852,15 @@ void setup_stdio_file(afl_state_t *afl) { } - unlink(afl->fsrv.out_file); /* Ignore errors */ + unlink(afl->fsrv.out_file); /* Ignore errors */ afl->fsrv.out_fd = open(afl->fsrv.out_file, O_RDWR | O_CREAT | O_EXCL, 0600); - if (afl->fsrv.out_fd < 0) { PFATAL("Unable to create '%s'", afl->fsrv.out_file); } + if (afl->fsrv.out_fd < 0) { + + PFATAL("Unable to create '%s'", afl->fsrv.out_file); + + } } -- cgit 1.4.1 From 81767287c31f454271f62c24a8331f382e14bc85 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 31 Aug 2020 13:02:40 +0200 Subject: improve documentation --- src/afl-fuzz.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 5ad2ace9..0df6c15c 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -178,8 +178,9 @@ static void usage(u8 *argv0, int more_help) { "AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" " the target was compiled for\n" - "AFL_MAX_DET_EXTRAS: if the dict/extras file contains more tokens than this threshold,\n" - " the tokens will sometimes be skipped during fuzzing.\n" + "AFL_MAX_DET_EXTRAS: if more entries are in the dictionary list than this value\n" + " then they are randomly selected instead all of them being\n" + " used. Defaults to 200.\n" "AFL_NO_AFFINITY: do not check for an unused cpu core to use for fuzzing\n" "AFL_NO_ARITH: skip arithmetic mutations in deterministic stage\n" "AFL_NO_CPU_RED: avoid red color for showing very high cpu usage\n" -- cgit 1.4.1 From d2c9e4baa74210cf49243390fb3dabb29024ade4 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 31 Aug 2020 13:12:59 +0200 Subject: fix warnings and weird code insert --- llvm_mode/afl-llvm-lto-instrumentation.so.cc | 24 +++++++++++++++++++----- src/afl-performance.c | 12 ++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index b8d9fce9..125db229 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -374,14 +374,28 @@ bool AFLLTOPass::runOnModule(Module &M) { std::string Str1, Str2; StringRef TmpStr; bool HasStr1 = getConstantStringInfo(Str1P, TmpStr); - if (TmpStr.empty()) + if (TmpStr.empty()) { + HasStr1 = false; - else + + } else { + + HasStr1 = true; Str1 = TmpStr.str(); + + } + bool HasStr2 = getConstantStringInfo(Str2P, TmpStr); - if (TmpStr.empty()) HasStr2 = false; - (void)HasStr2 /* never read */ - else Str2 = TmpStr.str(); + if (TmpStr.empty()) { + + HasStr2 = false; + + } else { + + HasStr2 = true; + Str2 = TmpStr.str(); + + } if (debug) fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n", diff --git a/src/afl-performance.c b/src/afl-performance.c index 0c1697a8..a9d7cefa 100644 --- a/src/afl-performance.c +++ b/src/afl-performance.c @@ -72,12 +72,12 @@ void jump(afl_state_t *afl) { static const uint64_t JUMP[] = {0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c}; - int i, b; + size_t i, b; uint64_t s0 = 0; uint64_t s1 = 0; uint64_t s2 = 0; uint64_t s3 = 0; - for (i = 0; i < sizeof JUMP / sizeof *JUMP; i++) + for (i = 0; i < (sizeof(JUMP) / sizeof(*JUMP)); i++) for (b = 0; b < 64; b++) { if (JUMP[i] & UINT64_C(1) << b) { @@ -110,12 +110,12 @@ void long_jump(afl_state_t *afl) { static const uint64_t LONG_JUMP[] = {0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635}; - int i, b; + size_t i, b; uint64_t s0 = 0; uint64_t s1 = 0; uint64_t s2 = 0; uint64_t s3 = 0; - for (i = 0; i < sizeof LONG_JUMP / sizeof *LONG_JUMP; i++) + for (i = 0; i < (sizeof(LONG_JUMP) / sizeof(*LONG_JUMP)); i++) for (b = 0; b < 64; b++) { if (LONG_JUMP[i] & UINT64_C(1) << b) { @@ -145,7 +145,7 @@ void long_jump(afl_state_t *afl) { u32 hash32(u8 *key, u32 len, u32 seed) { #else -u32 inline hash32(u8 *key, u32 len, u32 seed) { +inline u32 hash32(u8 *key, u32 len, u32 seed) { #endif @@ -157,7 +157,7 @@ u32 inline hash32(u8 *key, u32 len, u32 seed) { u64 hash64(u8 *key, u32 len, u64 seed) { #else -u64 inline hash64(u8 *key, u32 len, u64 seed) { +inline u64 hash64(u8 *key, u32 len, u64 seed) { #endif -- cgit 1.4.1 From 9e8b3f13e198dda74d4673866ffc4a37c976c05e Mon Sep 17 00:00:00 2001 From: domenukk Date: Mon, 31 Aug 2020 15:17:37 +0200 Subject: fixed warning on mac --- src/afl-as.c | 2 +- unicorn_mode/unicornafl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-as.c b/src/afl-as.c index 0ed47d8c..7d70bfcd 100644 --- a/src/afl-as.c +++ b/src/afl-as.c @@ -152,7 +152,7 @@ static void edit_params(int argc, char **argv) { /* The Apple case is a bit different... */ - if (!strcmp(argv[i], "-arch") && i + 1 < argc) { + if (!strcmp(argv[i], "-arch") && i + 1 < (u32)argc) { if (!strcmp(argv[i + 1], "x86_64")) use_64bit = 1; diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl index c6d66471..c21a5ebb 160000 --- a/unicorn_mode/unicornafl +++ b/unicorn_mode/unicornafl @@ -1 +1 @@ -Subproject commit c6d6647161a32bae88785a618fcd828d1711d9e6 +Subproject commit c21a5ebbbeb2dbaab84435642c49952060778243 -- cgit 1.4.1 From 155ef8875a2ca544316bade52d4fc36c545d9856 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Mon, 31 Aug 2020 15:37:46 +0100 Subject: Fix few warnings for FreeBSD case. (#536) --- src/afl-fuzz-init.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 852fc3fb..102f04b9 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -256,18 +256,18 @@ void bind_to_free_cpu(afl_state_t *afl) { } - for (i = 0; i < proccount; i++) { + for (i = 0; i < (s32)proccount; i++) { #if defined(__FreeBSD__) if (!strcmp(procs[i].ki_comm, "idle")) continue; // fix when ki_oncpu = -1 - int oncpu; + s32 oncpu; oncpu = procs[i].ki_oncpu; if (oncpu == -1) oncpu = procs[i].ki_lastcpu; - if (oncpu != -1 && oncpu < sizeof(cpu_used) && procs[i].ki_pctcpu > 60) + if (oncpu != -1 && oncpu < (s32)sizeof(cpu_used) && procs[i].ki_pctcpu > 60) cpu_used[oncpu] = 1; #elif defined(__DragonFly__) -- cgit 1.4.1 From d7d8afa512bcc0c012fac3b624eeff7b5af65439 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 31 Aug 2020 17:54:21 +0200 Subject: fix afl-gcc help output --- src/afl-gcc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-gcc.c b/src/afl-gcc.c index 22e6be8e..97564aea 100644 --- a/src/afl-gcc.c +++ b/src/afl-gcc.c @@ -415,7 +415,7 @@ int main(int argc, char **argv) { "AFL_KEEP_ASSEMBLY: leave instrumented assembly files\n" "AFL_AS_FORCE_INSTRUMENT: force instrumentation for asm sources\n"; - if (argc == 2 && strcmp(argv[1], "-h") == 0) { + if (argc == 2 && strncmp(argv[1], "-h", 2) == 0) { printf("afl-cc" VERSION " by Michal Zalewski\n\n"); printf("%s \n\n", argv[0]); -- cgit 1.4.1 From 53e63e9ded202b63de19590bb3d265d2f01ee929 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 31 Aug 2020 18:28:36 +0200 Subject: added forkserver init timeout --- afl-cmin | 1 + src/afl-showmap.c | 1 + src/afl-tmin.c | 1 + 3 files changed, 3 insertions(+) (limited to 'src') diff --git a/afl-cmin b/afl-cmin index d38e7a97..619c6dae 100755 --- a/afl-cmin +++ b/afl-cmin @@ -120,6 +120,7 @@ function usage() { "AFL_PATH: path for the afl-showmap binary\n" \ "AFL_SKIP_BIN_CHECK: skip check for target binary\n" \ "AFL_ALLOW_TMP: allow unsafe use of input/output directories under {/var}/tmp\n" +"AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" exit 1 } diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 64b52479..e596c26e 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -637,6 +637,7 @@ static void usage(u8 *argv0) { " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_QUIET: do not print extra informational output\n", + "AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" argv0, MEM_LIMIT, doc_path); exit(1); diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 59269f45..f231cde9 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -846,6 +846,7 @@ static void usage(u8 *argv0) { " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_TMIN_EXACT: require execution paths to match for crashing inputs\n" + "AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" , argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); -- cgit 1.4.1 From bea76b346ce278896d0b4a6f3b1d42e5c1c786c3 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 31 Aug 2020 18:29:45 +0200 Subject: fixed build error --- src/afl-showmap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-showmap.c b/src/afl-showmap.c index e596c26e..ae33cc48 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -636,9 +636,10 @@ static void usage(u8 *argv0) { "size\n" " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" - "AFL_QUIET: do not print extra informational output\n", + "AFL_QUIET: do not print extra informational output\n" "AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" - argv0, MEM_LIMIT, doc_path); + + ,argv0, MEM_LIMIT, doc_path); exit(1); -- cgit 1.4.1 From e4de4e350009584f5cdb8cf4c47a79fff9358cad Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 31 Aug 2020 18:32:01 +0200 Subject: update gitignore --- .gitignore | 1 + docs/Changelog.md | 1 + llvm_mode/afl-clang-fast.c | 4 ++-- src/afl-showmap.c | 8 ++++---- src/afl-tmin.c | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/.gitignore b/.gitignore index 4307fc4c..9c169c49 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ afl-cmin.8 afl-cmin.bash.8 afl-fuzz.8 afl-gcc.8 +afl-g++.8 afl-gcc-fast.8 afl-g++-fast.8 afl-gotcpu.8 diff --git a/docs/Changelog.md b/docs/Changelog.md index cb6e14b8..3966464e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,6 +10,7 @@ sending a mail to . ### Version ++2.67d (develop) + - a few QOL changes for Apple and its outdated gmake - afl-fuzz: - Fix for auto dictionary entries found during fuzzing to not throw out a -x dictionary diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 281d6b4b..ccdbca9d 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -670,11 +670,11 @@ static void edit_params(u32 argc, char **argv, char **envp) { } -#ifndef __APPLE__ + #ifndef __APPLE__ if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); -#endif + #endif #endif diff --git a/src/afl-showmap.c b/src/afl-showmap.c index ae33cc48..f4a7c336 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -636,10 +636,10 @@ static void usage(u8 *argv0) { "size\n" " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" - "AFL_QUIET: do not print extra informational output\n" - "AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" - - ,argv0, MEM_LIMIT, doc_path); + "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during " + "startup (in milliseconds)\n" + "AFL_QUIET: do not print extra informational output\n", + argv0, MEM_LIMIT, doc_path); exit(1); diff --git a/src/afl-tmin.c b/src/afl-tmin.c index f231cde9..e1d08054 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -846,7 +846,7 @@ static void usage(u8 *argv0) { " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_TMIN_EXACT: require execution paths to match for crashing inputs\n" - "AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" + "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" , argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); -- cgit 1.4.1 From 9bbbec3fa8e18a84939ffd864ecfd9017af98aba Mon Sep 17 00:00:00 2001 From: Ahmad Hazimeh Date: Mon, 31 Aug 2020 18:39:50 +0200 Subject: Fixed stack use-after-return bug in strntoll --- src/afl-fuzz-redqueen.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 1ae6ab54..392b1909 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -282,7 +282,6 @@ static long long strntoll(const char *str, size_t sz, char **end, int base) { memcpy(buf, beg, sz); buf[sz] = '\0'; ret = strtoll(buf, end, base); - if (ret == LLONG_MIN || ret == LLONG_MAX) return ret; if (end) *end = (char *)beg + (*end - buf); return ret; -- cgit 1.4.1 From 6090bb1bca81229a4c6ae178e1cef0e35bd31a96 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 31 Aug 2020 20:33:56 +0200 Subject: better fix for #539 --- src/afl-fuzz-redqueen.c | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 1ae6ab54..73d00f9a 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -264,7 +264,8 @@ static u8 its_fuzz(afl_state_t *afl, u8 *buf, u32 len, u8 *status) { } -static long long strntoll(const char *str, size_t sz, char **end, int base) { +static int strntoll(const char *str, size_t sz, char **end, int base, + long long* out) { char buf[64]; long long ret; @@ -272,24 +273,25 @@ static long long strntoll(const char *str, size_t sz, char **end, int base) { for (; beg && sz && *beg == ' '; beg++, sz--) {}; - if (!sz || sz >= sizeof(buf)) { - - if (end) *end = (char *)str; - return 0; - - } + if (!sz) + return 1; + if (sz >= sizeof(buf)) + sz = sizeof(buf) -1; memcpy(buf, beg, sz); buf[sz] = '\0'; ret = strtoll(buf, end, base); - if (ret == LLONG_MIN || ret == LLONG_MAX) return ret; + if ((ret == LLONG_MIN || ret == LLONG_MAX) && errno == ERANGE) + return 1; if (end) *end = (char *)beg + (*end - buf); - return ret; + *out = ret; + + return 0; } -static unsigned long long strntoull(const char *str, size_t sz, char **end, - int base) { +static int strntoull(const char *str, size_t sz, char **end, int base, + unsigned long long* out) { char buf[64]; unsigned long long ret; @@ -298,18 +300,20 @@ static unsigned long long strntoull(const char *str, size_t sz, char **end, for (; beg && sz && *beg == ' '; beg++, sz--) ; - if (!sz || sz >= sizeof(buf)) { - - if (end) *end = (char *)str; - return 0; - - } + if (!sz) + return 1; + if (sz >= sizeof(buf)) + sz = sizeof(buf) -1; memcpy(buf, beg, sz); buf[sz] = '\0'; ret = strtoull(buf, end, base); + if (ret == ULLONG_MAX && errno == ERANGE) + return 1; if (end) *end = (char *)beg + (*end - buf); - return ret; + *out = ret; + + return 0; } @@ -336,17 +340,16 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u8 use_num = 0, use_unum = 0; unsigned long long unum; long long num; + if (afl->queue_cur->is_ascii) { endptr = buf_8; - num = strntoll(buf_8, len - idx, (char **)&endptr, 0); - if (endptr == buf_8) { + if (strntoll(buf_8, len - idx, (char **)&endptr, 0, &num)) { - unum = strntoull(buf_8, len - idx, (char **)&endptr, 0); - if (endptr == buf_8) use_unum = 1; + if (!strntoull(buf_8, len - idx, (char **)&endptr, 0, &unum)) + use_unum = 1; } else - use_num = 1; } -- cgit 1.4.1 From bd57784664a7de62c726a0fb2aaabd41471faa0c Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 31 Aug 2020 23:59:10 +0200 Subject: code format --- src/afl-fuzz-redqueen.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 73d00f9a..9a9ac33f 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -265,7 +265,7 @@ static u8 its_fuzz(afl_state_t *afl, u8 *buf, u32 len, u8 *status) { } static int strntoll(const char *str, size_t sz, char **end, int base, - long long* out) { + long long *out) { char buf[64]; long long ret; @@ -273,16 +273,13 @@ static int strntoll(const char *str, size_t sz, char **end, int base, for (; beg && sz && *beg == ' '; beg++, sz--) {}; - if (!sz) - return 1; - if (sz >= sizeof(buf)) - sz = sizeof(buf) -1; + if (!sz) return 1; + if (sz >= sizeof(buf)) sz = sizeof(buf) - 1; memcpy(buf, beg, sz); buf[sz] = '\0'; ret = strtoll(buf, end, base); - if ((ret == LLONG_MIN || ret == LLONG_MAX) && errno == ERANGE) - return 1; + if ((ret == LLONG_MIN || ret == LLONG_MAX) && errno == ERANGE) return 1; if (end) *end = (char *)beg + (*end - buf); *out = ret; @@ -291,7 +288,7 @@ static int strntoll(const char *str, size_t sz, char **end, int base, } static int strntoull(const char *str, size_t sz, char **end, int base, - unsigned long long* out) { + unsigned long long *out) { char buf[64]; unsigned long long ret; @@ -300,16 +297,13 @@ static int strntoull(const char *str, size_t sz, char **end, int base, for (; beg && sz && *beg == ' '; beg++, sz--) ; - if (!sz) - return 1; - if (sz >= sizeof(buf)) - sz = sizeof(buf) -1; + if (!sz) return 1; + if (sz >= sizeof(buf)) sz = sizeof(buf) - 1; memcpy(buf, beg, sz); buf[sz] = '\0'; ret = strtoull(buf, end, base); - if (ret == ULLONG_MAX && errno == ERANGE) - return 1; + if (ret == ULLONG_MAX && errno == ERANGE) return 1; if (end) *end = (char *)beg + (*end - buf); *out = ret; @@ -350,6 +344,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, use_unum = 1; } else + use_num = 1; } -- cgit 1.4.1 From e4a86b40a5504c608d6ba7f44133ab39b24ac6f8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 1 Sep 2020 13:42:33 +0200 Subject: child cleanup --- src/afl-forkserver.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 93203cb2..cb4e00f9 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -240,6 +240,17 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) { if (!child_pid) { // New child + close(fsrv->out_dir_fd); + close(fsrv->dev_null_fd); + close(fsrv->dev_urandom_fd); + + if (fsrv->plot_file != NULL) { + + fclose(fsrv->plot_file); + fsrv->plot_file = NULL; + + } + signal(SIGCHLD, old_sigchld_handler); // FORKSRV_FD is for communication with AFL, we don't need it in the // child. @@ -360,12 +371,10 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if (!fsrv->fsrv_pid) { /* CHILD PROCESS */ - struct rlimit r; /* Umpf. On OpenBSD, the default fd limit for root users is set to soft 128. Let's try to fix that... */ - if (!getrlimit(RLIMIT_NOFILE, &r) && r.rlim_cur < FORKSRV_FD + 2) { r.rlim_cur = FORKSRV_FD + 2; @@ -432,7 +441,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, close(fsrv->dev_null_fd); close(fsrv->dev_urandom_fd); - if (fsrv->plot_file != NULL) { fclose(fsrv->plot_file); } + if (fsrv->plot_file != NULL) { + + fclose(fsrv->plot_file); + fsrv->plot_file = NULL; + + } /* This should improve performance a bit, since it stops the linker from doing extra work post-fork(). */ -- cgit 1.4.1 From 08f6e1d66aef1d005e85d6a7871358230a52f65d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 2 Sep 2020 17:54:54 +0200 Subject: children terminate on sigpipe --- src/afl-forkserver.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index cb4e00f9..58932bc4 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -251,6 +251,12 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) { } + // enable terminating on sigpipe in the childs + struct sigaction sa; + memset((char *)&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(SIGPIPE, &sa, NULL); + signal(SIGCHLD, old_sigchld_handler); // FORKSRV_FD is for communication with AFL, we don't need it in the // child. @@ -371,6 +377,13 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if (!fsrv->fsrv_pid) { /* CHILD PROCESS */ + + // enable terminating on sigpipe in the childs + struct sigaction sa; + memset((char *)&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(SIGPIPE, &sa, NULL); + struct rlimit r; /* Umpf. On OpenBSD, the default fd limit for root users is set to -- cgit 1.4.1 From c39a552cc0e2beea85b519e682771e0325354bda Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 2 Sep 2020 20:30:26 +0200 Subject: ignore unstable --- src/afl-fuzz-run.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index d71ec339..ee22b0f6 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -394,6 +394,8 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, unlikely(afl->first_trace[i] != afl->fsrv.trace_bits[i])) { afl->var_bytes[i] = 1; + // ignore the variable edge by setting it to fully discovered + afl->virgin_bits[i] = 0; } -- cgit 1.4.1 From 4c48d3a3ad1c0c8199b40ef47517b8a736cd5579 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 3 Sep 2020 09:59:23 +0200 Subject: update xxh3 to 0.8.0, fix is_ascii --- TODO.md | 2 - include/xxh3.h | 3187 ------------------------------------------ include/xxhash.h | 3721 +++++++++++++++++++++++++++++++++++++++++++++---- src/afl-fuzz-queue.c | 6 +- src/afl-performance.c | 5 +- 5 files changed, 3440 insertions(+), 3481 deletions(-) delete mode 100644 include/xxh3.h (limited to 'src') diff --git a/TODO.md b/TODO.md index 52065bb0..bb420518 100644 --- a/TODO.md +++ b/TODO.md @@ -7,7 +7,6 @@ - afl-plot to support multiple plot_data - afl_custom_fuzz_splice_optin() - intel-pt tracer - - honor -O flags and -fno-unroll-loops in afl-cc ## Further down the road @@ -22,7 +21,6 @@ gcc_plugin: - (wait for submission then decide) qemu_mode: - - update to 5.x (if the performance bug is gone) - non colliding instrumentation - rename qemu specific envs to AFL_QEMU (AFL_ENTRYPOINT, AFL_CODE_START/END, AFL_COMPCOV_LEVEL?) diff --git a/include/xxh3.h b/include/xxh3.h deleted file mode 100644 index 2354bde9..00000000 --- a/include/xxh3.h +++ /dev/null @@ -1,3187 +0,0 @@ -/* - * xxHash - Extremely Fast Hash algorithm - * Development source file for `xxh3` - * Copyright (C) 2019-2020 Yann Collet - * - * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You can contact the author at: - * - xxHash homepage: https://www.xxhash.com - * - xxHash source repository: https://github.com/Cyan4973/xxHash - */ - -/* - * Note: This file is separated for development purposes. - * It will be integrated into `xxhash.h` when development stage is completed. - * - * Credit: most of the work on vectorial and asm variants comes from - * @easyaspi314 - */ - -#ifndef XXH3_H_1397135465 -#define XXH3_H_1397135465 - -/* === Dependencies === */ -#ifndef XXHASH_H_5627135585666179 - /* special: when including `xxh3.h` directly, turn on XXH_INLINE_ALL */ - #undef XXH_INLINE_ALL /* avoid redefinition */ - #define XXH_INLINE_ALL -#endif -#include "xxhash.h" - -/* === Compiler specifics === */ - -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */ - #define XXH_RESTRICT restrict -#else - /* Note: it might be useful to define __restrict or __restrict__ for some C++ - * compilers */ - #define XXH_RESTRICT /* disable */ -#endif - -#if (defined(__GNUC__) && (__GNUC__ >= 3)) || \ - (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || \ - defined(__clang__) - #define XXH_likely(x) __builtin_expect(x, 1) - #define XXH_unlikely(x) __builtin_expect(x, 0) -#else - #define XXH_likely(x) (x) - #define XXH_unlikely(x) (x) -#endif - -#if defined(__GNUC__) - #if defined(__AVX2__) - #include - #elif defined(__SSE2__) - #include - #elif defined(__ARM_NEON__) || defined(__ARM_NEON) - #define inline __inline__ /* clang bug */ - #include - #undef inline - #endif -#elif defined(_MSC_VER) - #include -#endif - -/* - * One goal of XXH3 is to make it fast on both 32-bit and 64-bit, while - * remaining a true 64-bit/128-bit hash function. - * - * This is done by prioritizing a subset of 64-bit operations that can be - * emulated without too many steps on the average 32-bit machine. - * - * For example, these two lines seem similar, and run equally fast on 64-bit: - * - * xxh_u64 x; - * x ^= (x >> 47); // good - * x ^= (x >> 13); // bad - * - * However, to a 32-bit machine, there is a major difference. - * - * x ^= (x >> 47) looks like this: - * - * x.lo ^= (x.hi >> (47 - 32)); - * - * while x ^= (x >> 13) looks like this: - * - * // note: funnel shifts are not usually cheap. - * x.lo ^= (x.lo >> 13) | (x.hi << (32 - 13)); - * x.hi ^= (x.hi >> 13); - * - * The first one is significantly faster than the second, simply because the - * shift is larger than 32. This means: - * - All the bits we need are in the upper 32 bits, so we can ignore the lower - * 32 bits in the shift. - * - The shift result will always fit in the lower 32 bits, and therefore, - * we can ignore the upper 32 bits in the xor. - * - * Thanks to this optimization, XXH3 only requires these features to be - * efficient: - * - * - Usable unaligned access - * - A 32-bit or 64-bit ALU - * - If 32-bit, a decent ADC instruction - * - A 32 or 64-bit multiply with a 64-bit result - * - For the 128-bit variant, a decent byteswap helps short inputs. - * - * The first two are already required by XXH32, and almost all 32-bit and 64-bit - * platforms which can run XXH32 can run XXH3 efficiently. - * - * Thumb-1, the classic 16-bit only subset of ARM's instruction set, is one - * notable exception. - * - * First of all, Thumb-1 lacks support for the UMULL instruction which - * performs the important long multiply. This means numerous __aeabi_lmul - * calls. - * - * Second of all, the 8 functional registers are just not enough. - * Setup for __aeabi_lmul, byteshift loads, pointers, and all arithmetic need - * Lo registers, and this shuffling results in thousands more MOVs than A32. - * - * A32 and T32 don't have this limitation. They can access all 14 registers, - * do a 32->64 multiply with UMULL, and the flexible operand allowing free - * shifts is helpful, too. - * - * Therefore, we do a quick sanity check. - * - * If compiling Thumb-1 for a target which supports ARM instructions, we will - * emit a warning, as it is not a "sane" platform to compile for. - * - * Usually, if this happens, it is because of an accident and you probably need - * to specify -march, as you likely meant to compile for a newer architecture. - */ -#if defined(__thumb__) && !defined(__thumb2__) && defined(__ARM_ARCH_ISA_ARM) - #warning "XXH3 is highly inefficient without ARM or Thumb-2." -#endif - -/* ========================================== - * Vectorization detection - * ========================================== */ -#define XXH_SCALAR 0 /* Portable scalar version */ -#define XXH_SSE2 1 /* SSE2 for Pentium 4 and all x86_64 */ -#define XXH_AVX2 2 /* AVX2 for Haswell and Bulldozer */ -#define XXH_AVX512 3 /* AVX512 for Skylake and Icelake */ -#define XXH_NEON 4 /* NEON for most ARMv7-A and all AArch64 */ -#define XXH_VSX 5 /* VSX and ZVector for POWER8/z13 */ - -#ifndef XXH_VECTOR /* can be defined on command line */ - #if defined(__AVX512F__) - #define XXH_VECTOR XXH_AVX512 - #elif defined(__AVX2__) - #define XXH_VECTOR XXH_AVX2 - #elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || \ - (defined(_M_IX86_FP) && (_M_IX86_FP == 2)) - #define XXH_VECTOR XXH_SSE2 - #elif defined(__GNUC__) /* msvc support maybe later */ \ - && (defined(__ARM_NEON__) || defined(__ARM_NEON)) && \ - (defined(__LITTLE_ENDIAN__) /* We only support little endian NEON */ \ - || \ - (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) - #define XXH_VECTOR XXH_NEON - #elif (defined(__PPC64__) && defined(__POWER8_VECTOR__)) || \ - (defined(__s390x__) && defined(__VEC__)) && \ - defined(__GNUC__) /* TODO: IBM XL */ - #define XXH_VECTOR XXH_VSX - #else - #define XXH_VECTOR XXH_SCALAR - #endif -#endif - -/* - * Controls the alignment of the accumulator, - * for compatibility with aligned vector loads, which are usually faster. - */ -#ifndef XXH_ACC_ALIGN - #if defined(XXH_X86DISPATCH) - #define XXH_ACC_ALIGN 64 /* for compatibility with avx512 */ - #elif XXH_VECTOR == XXH_SCALAR /* scalar */ - #define XXH_ACC_ALIGN 8 - #elif XXH_VECTOR == XXH_SSE2 /* sse2 */ - #define XXH_ACC_ALIGN 16 - #elif XXH_VECTOR == XXH_AVX2 /* avx2 */ - #define XXH_ACC_ALIGN 32 - #elif XXH_VECTOR == XXH_NEON /* neon */ - #define XXH_ACC_ALIGN 16 - #elif XXH_VECTOR == XXH_VSX /* vsx */ - #define XXH_ACC_ALIGN 16 - #elif XXH_VECTOR == XXH_AVX512 /* avx512 */ - #define XXH_ACC_ALIGN 64 - #endif -#endif - -#if defined(XXH_X86DISPATCH) || XXH_VECTOR == XXH_SSE2 || \ - XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512 - #define XXH_SEC_ALIGN XXH_ACC_ALIGN -#else - #define XXH_SEC_ALIGN 8 -#endif - -/* - * UGLY HACK: - * GCC usually generates the best code with -O3 for xxHash. - * - * However, when targeting AVX2, it is overzealous in its unrolling resulting - * in code roughly 3/4 the speed of Clang. - * - * There are other issues, such as GCC splitting _mm256_loadu_si256 into - * _mm_loadu_si128 + _mm256_inserti128_si256. This is an optimization which - * only applies to Sandy and Ivy Bridge... which don't even support AVX2. - * - * That is why when compiling the AVX2 version, it is recommended to use either - * -O2 -mavx2 -march=haswell - * or - * -O2 -mavx2 -mno-avx256-split-unaligned-load - * for decent performance, or to use Clang instead. - * - * Fortunately, we can control the first one with a pragma that forces GCC into - * -O2, but the other one we can't control without "failed to inline always - * inline function due to target mismatch" warnings. - */ -#if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \ - && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ - && defined(__OPTIMIZE__) && \ - !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */ - #pragma GCC push_options - #pragma GCC optimize("-O2") -#endif - -#if XXH_VECTOR == XXH_NEON - /* - * NEON's setup for vmlal_u32 is a little more complicated than it is on - * SSE2, AVX2, and VSX. - * - * While PMULUDQ and VMULEUW both perform a mask, VMLAL.U32 performs an - * upcast. - * - * To do the same operation, the 128-bit 'Q' register needs to be split into - * two 64-bit 'D' registers, performing this operation:: - * - * [ a | b ] - * | '---------. .--------' | - * | x | - * | .---------' '--------. | - * [ a & 0xFFFFFFFF | b & 0xFFFFFFFF ],[ a >> 32 | b >> 32 ] - * - * Due to significant changes in aarch64, the fastest method for aarch64 is - * completely different than the fastest method for ARMv7-A. - * - * ARMv7-A treats D registers as unions overlaying Q registers, so modifying - * D11 will modify the high half of Q5. This is similar to how modifying AH - * will only affect bits 8-15 of AX on x86. - * - * VZIP takes two registers, and puts even lanes in one register and odd lanes - * in the other. - * - * On ARMv7-A, this strangely modifies both parameters in place instead of - * taking the usual 3-operand form. - * - * Therefore, if we want to do this, we can simply use a D-form VZIP.32 on the - * lower and upper halves of the Q register to end up with the high and low - * halves where we want - all in one instruction. - * - * vzip.32 d10, d11 @ d10 = { d10[0], d11[0] }; d11 = { d10[1], - * d11[1] } - * - * Unfortunately we need inline assembly for this: Instructions modifying two - * registers at once is not possible in GCC or Clang's IR, and they have to - * create a copy. - * - * aarch64 requires a different approach. - * - * In order to make it easier to write a decent compiler for aarch64, many - * quirks were removed, such as conditional execution. - * - * NEON was also affected by this. - * - * aarch64 cannot access the high bits of a Q-form register, and writes to a - * D-form register zero the high bits, similar to how writes to W-form scalar - * registers (or DWORD registers on x86_64) work. - * - * The formerly free vget_high intrinsics now require a vext (with a few - * exceptions) - * - * Additionally, VZIP was replaced by ZIP1 and ZIP2, which are the equivalent - * of PUNPCKL* and PUNPCKH* in SSE, respectively, in order to only modify one - * operand. - * - * The equivalent of the VZIP.32 on the lower and upper halves would be this - * mess: - * - * ext v2.4s, v0.4s, v0.4s, #2 // v2 = { v0[2], v0[3], v0[0], v0[1] } - * zip1 v1.2s, v0.2s, v2.2s // v1 = { v0[0], v2[0] } - * zip2 v0.2s, v0.2s, v1.2s // v0 = { v0[1], v2[1] } - * - * Instead, we use a literal downcast, vmovn_u64 (XTN), and vshrn_n_u64 - * (SHRN): - * - * shrn v1.2s, v0.2d, #32 // v1 = (uint32x2_t)(v0 >> 32); - * xtn v0.2s, v0.2d // v0 = (uint32x2_t)(v0 & 0xFFFFFFFF); - * - * This is available on ARMv7-A, but is less efficient than a single VZIP.32. - */ - - /* - * Function-like macro: - * void XXH_SPLIT_IN_PLACE(uint64x2_t &in, uint32x2_t &outLo, uint32x2_t - * &outHi) - * { - - * outLo = (uint32x2_t)(in & 0xFFFFFFFF); - * outHi = (uint32x2_t)(in >> 32); - * in = UNDEFINED; - * } - */ - #if !defined(XXH_NO_VZIP_HACK) /* define to disable */ \ - && defined(__GNUC__) && !defined(__aarch64__) && !defined(__arm64__) - #define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \ - do { \ - \ - /* Undocumented GCC/Clang operand modifier: %e0 = lower D half, %f0 = \ - * upper D half */ \ - /* https://github.com/gcc-mirror/gcc/blob/38cf91e5/gcc/config/arm/arm.c#L22486 \ - */ \ - /* https://github.com/llvm-mirror/llvm/blob/2c4ca683/lib/Target/ARM/ARMAsmPrinter.cpp#L399 \ - */ \ - __asm__("vzip.32 %e0, %f0" : "+w"(in)); \ - (outLo) = vget_low_u32(vreinterpretq_u32_u64(in)); \ - (outHi) = vget_high_u32(vreinterpretq_u32_u64(in)); \ - \ - } while (0) - - #else - #define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \ - do { \ - \ - (outLo) = vmovn_u64(in); \ - (outHi) = vshrn_n_u64((in), 32); \ - \ - } while (0) - - #endif -#endif /* XXH_VECTOR == XXH_NEON */ - -/* - * VSX and Z Vector helpers. - * - * This is very messy, and any pull requests to clean this up are welcome. - * - * There are a lot of problems with supporting VSX and s390x, due to - * inconsistent intrinsics, spotty coverage, and multiple endiannesses. - */ -#if XXH_VECTOR == XXH_VSX - #if defined(__s390x__) - #include - #else - #include - #endif - - #undef vector /* Undo the pollution */ - -typedef __vector unsigned long long xxh_u64x2; -typedef __vector unsigned char xxh_u8x16; -typedef __vector unsigned xxh_u32x4; - - #ifndef XXH_VSX_BE - #if defined(__BIG_ENDIAN__) || \ - (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) - #define XXH_VSX_BE 1 - #elif defined(__VEC_ELEMENT_REG_ORDER__) && \ - __VEC_ELEMENT_REG_ORDER__ == __ORDER_BIG_ENDIAN__ - #warning "-maltivec=be is not recommended. Please use native endianness." - #define XXH_VSX_BE 1 - #else - #define XXH_VSX_BE 0 - #endif - #endif /* !defined(XXH_VSX_BE) */ - - #if XXH_VSX_BE - /* A wrapper for POWER9's vec_revb. */ - #if defined(__POWER9_VECTOR__) || (defined(__clang__) && defined(__s390x__)) - #define XXH_vec_revb vec_revb - #else -XXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val) { - - xxh_u8x16 const vByteSwap = {0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, - 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08}; - return vec_perm(val, val, vByteSwap); - -} - - #endif - #endif /* XXH_VSX_BE */ - -/* - * Performs an unaligned load and byte swaps it on big endian. - */ -XXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr) { - - xxh_u64x2 ret; - memcpy(&ret, ptr, sizeof(xxh_u64x2)); - #if XXH_VSX_BE - ret = XXH_vec_revb(ret); - #endif - return ret; - -} - - /* - * vec_mulo and vec_mule are very problematic intrinsics on PowerPC - * - * These intrinsics weren't added until GCC 8, despite existing for a while, - * and they are endian dependent. Also, their meaning swap depending on - * version. - * */ - #if defined(__s390x__) - /* s390x is always big endian, no issue on this platform */ - #define XXH_vec_mulo vec_mulo - #define XXH_vec_mule vec_mule - #elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw) - /* Clang has a better way to control this, we can just use the builtin which - * doesn't swap. */ - #define XXH_vec_mulo __builtin_altivec_vmulouw - #define XXH_vec_mule __builtin_altivec_vmuleuw - #else -/* gcc needs inline assembly */ -/* Adapted from - * https://github.com/google/highwayhash/blob/master/highwayhash/hh_vsx.h. */ -XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mulo(xxh_u32x4 a, xxh_u32x4 b) { - - xxh_u64x2 result; - __asm__("vmulouw %0, %1, %2" : "=v"(result) : "v"(a), "v"(b)); - return result; - -} - -XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b) { - - xxh_u64x2 result; - __asm__("vmuleuw %0, %1, %2" : "=v"(result) : "v"(a), "v"(b)); - return result; - -} - - #endif /* XXH_vec_mulo, XXH_vec_mule */ -#endif /* XXH_VECTOR == XXH_VSX */ - -/* prefetch - * can be disabled, by declaring XXH_NO_PREFETCH build macro */ -#if defined(XXH_NO_PREFETCH) - #define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ -#else - #if defined(_MSC_VER) && \ - (defined(_M_X64) || \ - defined(_M_I86)) /* _mm_prefetch() is not defined outside of x86/x64 */ - #include /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ - #define XXH_PREFETCH(ptr) _mm_prefetch((const char *)(ptr), _MM_HINT_T0) - #elif defined(__GNUC__) && \ - ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))) - #define XXH_PREFETCH(ptr) \ - __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */) - #else - #define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ - #endif -#endif /* XXH_NO_PREFETCH */ - -/* ========================================== - * XXH3 default settings - * ========================================== */ - -#define XXH_SECRET_DEFAULT_SIZE 192 /* minimum XXH3_SECRET_SIZE_MIN */ - -#if (XXH_SECRET_DEFAULT_SIZE < XXH3_SECRET_SIZE_MIN) - #error "default keyset is not large enough" -#endif - -/* Pseudorandom secret taken directly from FARSH */ -XXH_ALIGN(64) -static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = { - - 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, - 0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, - 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, 0xcb, 0x79, 0xe6, 0x4e, - 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, - 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, - 0x81, 0x3a, 0x26, 0x4c, 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, - 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, 0x71, 0x64, 0x48, 0x97, - 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, - 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, - 0xc7, 0x0b, 0x4f, 0x1d, 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, - 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, - - 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, - 0xfa, 0x13, 0x63, 0xeb, 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, - 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, 0x2b, 0x16, 0xbe, 0x58, - 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, - 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, - 0xbb, 0x4b, 0x40, 0x7e, - -}; - -#ifdef XXH_OLD_NAMES - #define kSecret XXH3_kSecret -#endif - -/* - * Calculates a 32-bit to 64-bit long multiply. - * - * Wraps __emulu on MSVC x86 because it tends to call __allmul when it doesn't - * need to (but it shouldn't need to anyways, it is about 7 instructions to do - * a 64x64 multiply...). Since we know that this will _always_ emit MULL, we - * use that instead of the normal method. - * - * If you are compiling for platforms like Thumb-1 and don't have a better - * option, you may also want to write your own long multiply routine here. - * - * XXH_FORCE_INLINE xxh_u64 XXH_mult32to64(xxh_u64 x, xxh_u64 y) - * { - - * return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF); - * } - */ -#if defined(_MSC_VER) && defined(_M_IX86) - #include - #define XXH_mult32to64(x, y) __emulu((unsigned)(x), (unsigned)(y)) -#else - /* - * Downcast + upcast is usually better than masking on older compilers like - * GCC 4.2 (especially 32-bit ones), all without affecting newer compilers. - * - * The other method, (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF), will AND both - * operands and perform a full 64x64 multiply -- entirely redundant on 32-bit. - */ - #define XXH_mult32to64(x, y) ((xxh_u64)(xxh_u32)(x) * (xxh_u64)(xxh_u32)(y)) -#endif - -/* - * Calculates a 64->128-bit long multiply. - * - * Uses __uint128_t and _umul128 if available, otherwise uses a scalar version. - */ -static XXH128_hash_t XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) { - - /* - * GCC/Clang __uint128_t method. - * - * On most 64-bit targets, GCC and Clang define a __uint128_t type. - * This is usually the best way as it usually uses a native long 64-bit - * multiply, such as MULQ on x86_64 or MUL + UMULH on aarch64. - * - * Usually. - * - * Despite being a 32-bit platform, Clang (and emscripten) define this type - * despite not having the arithmetic for it. This results in a laggy - * compiler builtin call which calculates a full 128-bit multiply. - * In that case it is best to use the portable one. - * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677 - */ -#if defined(__GNUC__) && !defined(__wasm__) && defined(__SIZEOF_INT128__) || \ - (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128) - - __uint128_t const product = (__uint128_t)lhs * (__uint128_t)rhs; - XXH128_hash_t r128; - r128.low64 = (xxh_u64)(product); - r128.high64 = (xxh_u64)(product >> 64); - return r128; - - /* - * MSVC for x64's _umul128 method. - * - * xxh_u64 _umul128(xxh_u64 Multiplier, xxh_u64 Multiplicand, xxh_u64 - * *HighProduct); - * - * This compiles to single operand MUL on x64. - */ -#elif defined(_M_X64) || defined(_M_IA64) - - #ifndef _MSC_VER - #pragma intrinsic(_umul128) - #endif - xxh_u64 product_high; - xxh_u64 const product_low = _umul128(lhs, rhs, &product_high); - XXH128_hash_t r128; - r128.low64 = product_low; - r128.high64 = product_high; - return r128; - -#else - /* - * Portable scalar method. Optimized for 32-bit and 64-bit ALUs. - * - * This is a fast and simple grade school multiply, which is shown below - * with base 10 arithmetic instead of base 0x100000000. - * - * 9 3 // D2 lhs = 93 - * x 7 5 // D2 rhs = 75 - * ---------- - * 1 5 // D2 lo_lo = (93 % 10) * (75 % 10) = 15 - * 4 5 | // D2 hi_lo = (93 / 10) * (75 % 10) = 45 - * 2 1 | // D2 lo_hi = (93 % 10) * (75 / 10) = 21 - * + 6 3 | | // D2 hi_hi = (93 / 10) * (75 / 10) = 63 - * --------- - * 2 7 | // D2 cross = (15 / 10) + (45 % 10) + 21 = 27 - * + 6 7 | | // D2 upper = (27 / 10) + (45 / 10) + 63 = 67 - * --------- - * 6 9 7 5 // D4 res = (27 * 10) + (15 % 10) + (67 * 100) = 6975 - * - * The reasons for adding the products like this are: - * 1. It avoids manual carry tracking. Just like how - * (9 * 9) + 9 + 9 = 99, the same applies with this for UINT64_MAX. - * This avoids a lot of complexity. - * - * 2. It hints for, and on Clang, compiles to, the powerful UMAAL - * instruction available in ARM's Digital Signal Processing extension - * in 32-bit ARMv6 and later, which is shown below: - * - * void UMAAL(xxh_u32 *RdLo, xxh_u32 *RdHi, xxh_u32 Rn, xxh_u32 Rm) - * { - - * xxh_u64 product = (xxh_u64)*RdLo * (xxh_u64)*RdHi + Rn + Rm; - * *RdLo = (xxh_u32)(product & 0xFFFFFFFF); - * *RdHi = (xxh_u32)(product >> 32); - * } - * - * This instruction was designed for efficient long multiplication, and - * allows this to be calculated in only 4 instructions at speeds - * comparable to some 64-bit ALUs. - * - * 3. It isn't terrible on other platforms. Usually this will be a couple - * of 32-bit ADD/ADCs. - */ - - /* First calculate all of the cross products. */ - xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF); - xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32, rhs & 0xFFFFFFFF); - xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32); - xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32, rhs >> 32); - - /* Now add the products together. These will never overflow. */ - xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi; - xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32) + hi_hi; - xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF); - - XXH128_hash_t r128; - r128.low64 = lower; - r128.high64 = upper; - return r128; -#endif - -} - -/* - * Does a 64-bit to 128-bit multiply, then XOR folds it. - * - * The reason for the separate function is to prevent passing too many structs - * around by value. This will hopefully inline the multiply, but we don't force - * it. - */ -static xxh_u64 XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) { - - XXH128_hash_t product = XXH_mult64to128(lhs, rhs); - return product.low64 ^ product.high64; - -} - -/* Seems to produce slightly better code on GCC for some reason. */ -XXH_FORCE_INLINE xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) { - - XXH_ASSERT(0 <= shift && shift < 64); - return v64 ^ (v64 >> shift); - -} - -/* - * We don't need to (or want to) mix as much as XXH64. - * - * Short hashes are more evenly distributed, so it isn't necessary. - */ -static XXH64_hash_t XXH3_avalanche(xxh_u64 h64) { - - h64 = XXH_xorshift64(h64, 37); - h64 *= 0x165667919E3779F9ULL; - h64 = XXH_xorshift64(h64, 32); - return h64; - -} - -/* ========================================== - * Short keys - * ========================================== - * One of the shortcomings of XXH32 and XXH64 was that their performance was - * sub-optimal on short lengths. It used an iterative algorithm which strongly - * favored lengths that were a multiple of 4 or 8. - * - * Instead of iterating over individual inputs, we use a set of single shot - * functions which piece together a range of lengths and operate in constant - * time. - * - * Additionally, the number of multiplies has been significantly reduced. This - * reduces latency, especially when emulating 64-bit multiplies on 32-bit. - * - * Depending on the platform, this may or may not be faster than XXH32, but it - * is almost guaranteed to be faster than XXH64. - */ - -/* - * At very short lengths, there isn't enough input to fully hide secrets, or use - * the entire secret. - * - * There is also only a limited amount of mixing we can do before significantly - * impacting performance. - * - * Therefore, we use different sections of the secret and always mix two secret - * samples with an XOR. This should have no effect on performance on the - * seedless or withSeed variants because everything _should_ be constant folded - * by modern compilers. - * - * The XOR mixing hides individual parts of the secret and increases entropy. - * - * This adds an extra layer of strength for custom secrets. - */ -XXH_FORCE_INLINE XXH64_hash_t XXH3_len_1to3_64b(const xxh_u8 *input, size_t len, - const xxh_u8 *secret, - XXH64_hash_t seed) { - - XXH_ASSERT(input != NULL); - XXH_ASSERT(1 <= len && len <= 3); - XXH_ASSERT(secret != NULL); - /* - * len = 1: combined = { input[0], 0x01, input[0], input[0] } - * len = 2: combined = { input[1], 0x02, input[0], input[1] } - * len = 3: combined = { input[2], 0x03, input[0], input[1] } - */ - { - - xxh_u8 const c1 = input[0]; - xxh_u8 const c2 = input[len >> 1]; - xxh_u8 const c3 = input[len - 1]; - xxh_u32 const combined = ((xxh_u32)c1 << 16) | ((xxh_u32)c2 << 24) | - ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); - xxh_u64 const bitflip = - (XXH_readLE32(secret) ^ XXH_readLE32(secret + 4)) + seed; - xxh_u64 const keyed = (xxh_u64)combined ^ bitflip; - xxh_u64 const mixed = keyed * XXH_PRIME64_1; - return XXH3_avalanche(mixed); - - } - -} - -XXH_FORCE_INLINE XXH64_hash_t XXH3_len_4to8_64b(const xxh_u8 *input, size_t len, - const xxh_u8 *secret, - XXH64_hash_t seed) { - - XXH_ASSERT(input != NULL); - XXH_ASSERT(secret != NULL); - XXH_ASSERT(4 <= len && len < 8); - seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; - { - - xxh_u32 const input1 = XXH_readLE32(input); - xxh_u32 const input2 = XXH_readLE32(input + len - 4); - xxh_u64 const bitflip = - (XXH_readLE64(secret + 8) ^ XXH_readLE64(secret + 16)) - seed; - xxh_u64 const input64 = input2 + (((xxh_u64)input1) << 32); - xxh_u64 x = input64 ^ bitflip; - /* this mix is inspired by Pelle Evensen's rrmxmx */ - x ^= XXH_rotl64(x, 49) ^ XXH_rotl64(x, 24); - x *= 0x9FB21C651E98DF25ULL; - x ^= (x >> 35) + len; - x *= 0x9FB21C651E98DF25ULL; - return XXH_xorshift64(x, 28); - - } - -} - -XXH_FORCE_INLINE XXH64_hash_t XXH3_len_9to16_64b(const xxh_u8 *input, - size_t len, - const xxh_u8 *secret, - XXH64_hash_t seed) { - - XXH_ASSERT(input != NULL); - XXH_ASSERT(secret != NULL); - XXH_ASSERT(8 <= len && len <= 16); - { - - xxh_u64 const bitflip1 = - (XXH_readLE64(secret + 24) ^ XXH_readLE64(secret + 32)) + seed; - xxh_u64 const bitflip2 = - (XXH_readLE64(secret + 40) ^ XXH_readLE64(secret + 48)) - seed; - xxh_u64 const input_lo = XXH_readLE64(input) ^ bitflip1; - xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ bitflip2; - xxh_u64 const acc = len + XXH_swap64(input_lo) + input_hi + - XXH3_mul128_fold64(input_lo, input_hi); - return XXH3_avalanche(acc); - - } - -} - -XXH_FORCE_INLINE XXH64_hash_t XXH3_len_0to16_64b(const xxh_u8 *input, - size_t len, - const xxh_u8 *secret, - XXH64_hash_t seed) { - - XXH_ASSERT(len <= 16); - { - - if (XXH_likely(len > 8)) - return XXH3_len_9to16_64b(input, len, secret, seed); - if (XXH_likely(len >= 4)) - return XXH3_len_4to8_64b(input, len, secret, seed); - if (len) return XXH3_len_1to3_64b(input, len, secret, seed); - return XXH3_avalanche((XXH_PRIME64_1 + seed) ^ (XXH_readLE64(secret + 56) ^ - XXH_readLE64(secret + 64))); - - } - -} - -/* - * DISCLAIMER: There are known *seed-dependent* multicollisions here due to - * multiplication by zero, affecting hashes of lengths 17 to 240. - * - * However, they are very unlikely. - * - * Keep this in mind when using the unseeded XXH3_64bits() variant: As with all - * unseeded non-cryptographic hashes, it does not attempt to defend itself - * against specially crafted inputs, only random inputs. - * - * Compared to classic UMAC where a 1 in 2^31 chance of 4 consecutive bytes - * cancelling out the secret is taken an arbitrary number of times (addressed - * in XXH3_accumulate_512), this collision is very unlikely with random inputs - * and/or proper seeding: - * - * This only has a 1 in 2^63 chance of 8 consecutive bytes cancelling out, in a - * function that is only called up to 16 times per hash with up to 240 bytes of - * input. - * - * This is not too bad for a non-cryptographic hash function, especially with - * only 64 bit outputs. - * - * The 128-bit variant (which trades some speed for strength) is NOT affected - * by this, although it is always a good idea to use a proper seed if you care - * about strength. - */ -XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8 *XXH_RESTRICT input, - const xxh_u8 *XXH_RESTRICT secret, - xxh_u64 seed64) { - -#if defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ - && defined(__i386__) && defined(__SSE2__) /* x86 + SSE2 */ \ - && \ - !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable like XXH32 hack */ - /* - * UGLY HACK: - * GCC for x86 tends to autovectorize the 128-bit multiply, resulting in - * slower code. - * - * By forcing seed64 into a register, we disrupt the cost model and - * cause it to scalarize. See `XXH32_round()` - * - * FIXME: Clang's output is still _much_ faster -- On an AMD Ryzen 3600, - * XXH3_64bits @ len=240 runs at 4.6 GB/s with Clang 9, but 3.3 GB/s on - * GCC 9.2, despite both emitting scalar code. - * - * GCC generates much better scalar code than Clang for the rest of XXH3, - * which is why finding a more optimal codepath is an interest. - */ - __asm__("" : "+r"(seed64)); -#endif - { - - xxh_u64 const input_lo = XXH_readLE64(input); - xxh_u64 const input_hi = XXH_readLE64(input + 8); - return XXH3_mul128_fold64(input_lo ^ (XXH_readLE64(secret) + seed64), - input_hi ^ (XXH_readLE64(secret + 8) - seed64)); - - } - -} - -/* For mid range keys, XXH3 uses a Mum-hash variant. */ -XXH_FORCE_INLINE XXH64_hash_t XXH3_len_17to128_64b( - const xxh_u8 *XXH_RESTRICT input, size_t len, - const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { - - XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); - (void)secretSize; - XXH_ASSERT(16 < len && len <= 128); - - { - - xxh_u64 acc = len * XXH_PRIME64_1; - if (len > 32) { - - if (len > 64) { - - if (len > 96) { - - acc += XXH3_mix16B(input + 48, secret + 96, seed); - acc += XXH3_mix16B(input + len - 64, secret + 112, seed); - - } - - acc += XXH3_mix16B(input + 32, secret + 64, seed); - acc += XXH3_mix16B(input + len - 48, secret + 80, seed); - - } - - acc += XXH3_mix16B(input + 16, secret + 32, seed); - acc += XXH3_mix16B(input + len - 32, secret + 48, seed); - - } - - acc += XXH3_mix16B(input + 0, secret + 0, seed); - acc += XXH3_mix16B(input + len - 16, secret + 16, seed); - - return XXH3_avalanche(acc); - - } - -} - -#define XXH3_MIDSIZE_MAX 240 - -XXH_NO_INLINE XXH64_hash_t XXH3_len_129to240_64b( - const xxh_u8 *XXH_RESTRICT input, size_t len, - const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { - - XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); - (void)secretSize; - XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); - -#define XXH3_MIDSIZE_STARTOFFSET 3 -#define XXH3_MIDSIZE_LASTOFFSET 17 - - { - - xxh_u64 acc = len * XXH_PRIME64_1; - int const nbRounds = (int)len / 16; - int i; - for (i = 0; i < 8; i++) { - - acc += XXH3_mix16B(input + (16 * i), secret + (16 * i), seed); - - } - - acc = XXH3_avalanche(acc); - XXH_ASSERT(nbRounds >= 8); -#if defined(__clang__) /* Clang */ \ - && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ - && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ - /* - * UGLY HACK: - * Clang for ARMv7-A tries to vectorize this loop, similar to GCC x86. - * In everywhere else, it uses scalar code. - * - * For 64->128-bit multiplies, even if the NEON was 100% optimal, it - * would still be slower than UMAAL (see XXH_mult64to128). - * - * Unfortunately, Clang doesn't handle the long multiplies properly and - * converts them to the nonexistent "vmulq_u64" intrinsic, which is then - * scalarized into an ugly mess of VMOV.32 instructions. - * - * This mess is difficult to avoid without turning autovectorization - * off completely, but they are usually relatively minor and/or not - * worth it to fix. - * - * This loop is the easiest to fix, as unlike XXH32, this pragma - * _actually works_ because it is a loop vectorization instead of an - * SLP vectorization. - */ - #pragma clang loop vectorize(disable) -#endif - for (i = 8; i < nbRounds; i++) { - - acc += - XXH3_mix16B(input + (16 * i), - secret + (16 * (i - 8)) + XXH3_MIDSIZE_STARTOFFSET, seed); - - } - - /* last bytes */ - acc += XXH3_mix16B(input + len - 16, - secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, - seed); - return XXH3_avalanche(acc); - - } - -} - -/* ======= Long Keys ======= */ - -#define XXH_STRIPE_LEN 64 -#define XXH_SECRET_CONSUME_RATE \ - 8 /* nb of secret bytes consumed at each accumulation */ -#define XXH_ACC_NB (XXH_STRIPE_LEN / sizeof(xxh_u64)) - -#ifdef XXH_OLD_NAMES - #define STRIPE_LEN XXH_STRIPE_LEN - #define ACC_NB XXH_ACC_NB -#endif - -typedef enum { XXH3_acc_64bits, XXH3_acc_128bits } XXH3_accWidth_e; - -XXH_FORCE_INLINE void XXH_writeLE64(void *dst, xxh_u64 v64) { - - if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64); - memcpy(dst, &v64, sizeof(v64)); - -} - -/* Several intrinsic functions below are supposed to accept __int64 as argument, - * as documented in - * https://software.intel.com/sites/landingpage/IntrinsicsGuide/ . However, - * several environments do not define __int64 type, requiring a workaround. - */ -#if !defined(__VMS) && \ - (defined(__cplusplus) || \ - (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)) -typedef int64_t xxh_i64; -#else -/* the following type must have a width of 64-bit */ -typedef long long xxh_i64; -#endif - -/* - * XXH3_accumulate_512 is the tightest loop for long inputs, and it is the most - * optimized. - * - * It is a hardened version of UMAC, based off of FARSH's implementation. - * - * This was chosen because it adapts quite well to 32-bit, 64-bit, and SIMD - * implementations, and it is ridiculously fast. - * - * We harden it by mixing the original input to the accumulators as well as the - * product. - * - * This means that in the (relatively likely) case of a multiply by zero, the - * original input is preserved. - * - * On 128-bit inputs, we swap 64-bit pairs when we add the input to improve - * cross-pollination, as otherwise the upper and lower halves would be - * essentially independent. - * - * This doesn't matter on 64-bit hashes since they all get merged together in - * the end, so we skip the extra step. - * - * Both XXH3_64bits and XXH3_128bits use this subroutine. - */ - -#if (XXH_VECTOR == XXH_AVX512) || defined(XXH_X86DISPATCH) - - #ifndef XXH_TARGET_AVX512 - #define XXH_TARGET_AVX512 /* disable attribute target */ - #endif - -XXH_FORCE_INLINE XXH_TARGET_AVX512 void XXH3_accumulate_512_avx512( - void *XXH_RESTRICT acc, const void *XXH_RESTRICT input, - const void *XXH_RESTRICT secret, XXH3_accWidth_e accWidth) { - - XXH_ALIGN(64) __m512i *const xacc = (__m512i *)acc; - XXH_ASSERT((((size_t)acc) & 63) == 0); - XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); - - { - - /* data_vec = input[0]; */ - __m512i const data_vec = _mm512_loadu_si512(input); - /* key_vec = secret[0]; */ - __m512i const key_vec = _mm512_loadu_si512(secret); - /* data_key = data_vec ^ key_vec; */ - __m512i const data_key = _mm512_xor_si512(data_vec, key_vec); - /* data_key_lo = data_key >> 32; */ - __m512i const data_key_lo = - _mm512_shuffle_epi32(data_key, _MM_SHUFFLE(0, 3, 0, 1)); - /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ - __m512i const product = _mm512_mul_epu32(data_key, data_key_lo); - if (accWidth == XXH3_acc_128bits) { - - /* xacc[0] += swap(data_vec); */ - __m512i const data_swap = - _mm512_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2)); - __m512i const sum = _mm512_add_epi64(*xacc, data_swap); - /* xacc[0] += product; */ - *xacc = _mm512_add_epi64(product, sum); - - } else { /* XXH3_acc_64bits */ - - /* xacc[0] += data_vec; */ - __m512i const sum = _mm512_add_epi64(*xacc, data_vec); - /* xacc[0] += product; */ - *xacc = _mm512_add_epi64(product, sum); - - } - - } - -} - -/* - * XXH3_scrambleAcc: Scrambles the accumulators to improve mixing. - * - * Multiplication isn't perfect, as explained by Google in HighwayHash: - * - * // Multiplication mixes/scrambles bytes 0-7 of the 64-bit result to - * // varying degrees. In descending order of goodness, bytes - * // 3 4 2 5 1 6 0 7 have quality 228 224 164 160 100 96 36 32. - * // As expected, the upper and lower bytes are much worse. - * - * Source: - * https://github.com/google/highwayhash/blob/0aaf66b/highwayhash/hh_avx2.h#L291 - * - * Since our algorithm uses a pseudorandom secret to add some variance into the - * mix, we don't need to (or want to) mix as often or as much as HighwayHash - * does. - * - * This isn't as tight as XXH3_accumulate, but still written in SIMD to avoid - * extraction. - * - * Both XXH3_64bits and XXH3_128bits use this subroutine. - */ - -XXH_FORCE_INLINE XXH_TARGET_AVX512 void XXH3_scrambleAcc_avx512( - void *XXH_RESTRICT acc, const void *XXH_RESTRICT secret) { - - XXH_ASSERT((((size_t)acc) & 63) == 0); - XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); - { - - XXH_ALIGN(64) __m512i *const xacc = (__m512i *)acc; - const __m512i prime32 = _mm512_set1_epi32((int)XXH_PRIME32_1); - - /* xacc[0] ^= (xacc[0] >> 47) */ - __m512i const acc_vec = *xacc; - __m512i const shifted = _mm512_srli_epi64(acc_vec, 47); - __m512i const data_vec = _mm512_xor_si512(acc_vec, shifted); - /* xacc[0] ^= secret; */ - __m512i const key_vec = _mm512_loadu_si512(secret); - __m512i const data_key = _mm512_xor_si512(data_vec, key_vec); - - /* xacc[0] *= XXH_PRIME32_1; */ - __m512i const data_key_hi = - _mm512_shuffle_epi32(data_key, _MM_SHUFFLE(0, 3, 0, 1)); - __m512i const prod_lo = _mm512_mul_epu32(data_key, prime32); - __m512i const prod_hi = _mm512_mul_epu32(data_key_hi, prime32); - *xacc = _mm512_add_epi64(prod_lo, _mm512_slli_epi64(prod_hi, 32)); - - } - -} - -XXH_FORCE_INLINE XXH_TARGET_AVX512 void XXH3_initCustomSecret_avx512( - void *XXH_RESTRICT customSecret, xxh_u64 seed64) { - - XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 63) == 0); - XXH_STATIC_ASSERT(XXH_SEC_ALIGN == 64); - XXH_ASSERT(((size_t)customSecret & 63) == 0); - (void)(&XXH_writeLE64); - { - - int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m512i); - __m512i const seed = _mm512_mask_set1_epi64( - _mm512_set1_epi64((xxh_i64)seed64), 0xAA, -(xxh_i64)seed64); - - XXH_ALIGN(64) const __m512i *const src = (const __m512i *)XXH3_kSecret; - XXH_ALIGN(64) __m512i *const dest = (__m512i *)customSecret; - int i; - for (i = 0; i < nbRounds; ++i) { - - // GCC has a bug, _mm512_stream_load_si512 accepts 'void*', not 'void - // const*', this will warn "discards ‘const’ qualifier". - union { - - XXH_ALIGN(64) const __m512i *const cp; - XXH_ALIGN(64) void *const p; - - } const remote_const_void = {.cp = src + i}; - - dest[i] = - _mm512_add_epi64(_mm512_stream_load_si512(remote_const_void.p), seed); - - } - - } - -} - -#endif - -#if (XXH_VECTOR == XXH_AVX2) || defined(XXH_X86DISPATCH) - - #ifndef XXH_TARGET_AVX2 - #define XXH_TARGET_AVX2 /* disable attribute target */ - #endif - -XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_accumulate_512_avx2( - void *XXH_RESTRICT acc, const void *XXH_RESTRICT input, - const void *XXH_RESTRICT secret, XXH3_accWidth_e accWidth) { - - XXH_ASSERT((((size_t)acc) & 31) == 0); - { - - XXH_ALIGN(32) __m256i *const xacc = (__m256i *)acc; - /* Unaligned. This is mainly for pointer arithmetic, and because - * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. - */ - const __m256i *const xinput = (const __m256i *)input; - /* Unaligned. This is mainly for pointer arithmetic, and because - * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ - const __m256i *const xsecret = (const __m256i *)secret; - - size_t i; - for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m256i); i++) { - - /* data_vec = xinput[i]; */ - __m256i const data_vec = _mm256_loadu_si256(xinput + i); - /* key_vec = xsecret[i]; */ - __m256i const key_vec = _mm256_loadu_si256(xsecret + i); - /* data_key = data_vec ^ key_vec; */ - __m256i const data_key = _mm256_xor_si256(data_vec, key_vec); - /* data_key_lo = data_key >> 32; */ - __m256i const data_key_lo = - _mm256_shuffle_epi32(data_key, _MM_SHUFFLE(0, 3, 0, 1)); - /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ - __m256i const product = _mm256_mul_epu32(data_key, data_key_lo); - if (accWidth == XXH3_acc_128bits) { - - /* xacc[i] += swap(data_vec); */ - __m256i const data_swap = - _mm256_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2)); - __m256i const sum = _mm256_add_epi64(xacc[i], data_swap); - /* xacc[i] += product; */ - xacc[i] = _mm256_add_epi64(product, sum); - - } else { /* XXH3_acc_64bits */ - - /* xacc[i] += data_vec; */ - __m256i const sum = _mm256_add_epi64(xacc[i], data_vec); - /* xacc[i] += product; */ - xacc[i] = _mm256_add_epi64(product, sum); - - } - - } - - } - -} - -XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_scrambleAcc_avx2( - void *XXH_RESTRICT acc, const void *XXH_RESTRICT secret) { - - XXH_ASSERT((((size_t)acc) & 31) == 0); - { - - XXH_ALIGN(32) __m256i *const xacc = (__m256i *)acc; - /* Unaligned. This is mainly for pointer arithmetic, and because - * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ - const __m256i *const xsecret = (const __m256i *)secret; - const __m256i prime32 = _mm256_set1_epi32((int)XXH_PRIME32_1); - - size_t i; - for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m256i); i++) { - - /* xacc[i] ^= (xacc[i] >> 47) */ - __m256i const acc_vec = xacc[i]; - __m256i const shifted = _mm256_srli_epi64(acc_vec, 47); - __m256i const data_vec = _mm256_xor_si256(acc_vec, shifted); - /* xacc[i] ^= xsecret; */ - __m256i const key_vec = _mm256_loadu_si256(xsecret + i); - __m256i const data_key = _mm256_xor_si256(data_vec, key_vec); - - /* xacc[i] *= XXH_PRIME32_1; */ - __m256i const data_key_hi = - _mm256_shuffle_epi32(data_key, _MM_SHUFFLE(0, 3, 0, 1)); - __m256i const prod_lo = _mm256_mul_epu32(data_key, prime32); - __m256i const prod_hi = _mm256_mul_epu32(data_key_hi, prime32); - xacc[i] = _mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32)); - - } - - } - -} - -XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_initCustomSecret_avx2( - void *XXH_RESTRICT customSecret, xxh_u64 seed64) { - - XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 31) == 0); - XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE / sizeof(__m256i)) == 6); - XXH_STATIC_ASSERT(XXH_SEC_ALIGN <= 64); - (void)(&XXH_writeLE64); - XXH_PREFETCH(customSecret); - { - - __m256i const seed = _mm256_set_epi64x(-(xxh_i64)seed64, (xxh_i64)seed64, - -(xxh_i64)seed64, (xxh_i64)seed64); - - XXH_ALIGN(64) const __m256i *const src = (const __m256i *)XXH3_kSecret; - XXH_ALIGN(64) __m256i * dest = (__m256i *)customSecret; - - #if defined(__GNUC__) || defined(__clang__) - /* - * On GCC & Clang, marking 'dest' as modified will cause the compiler: - * - do not extract the secret from sse registers in the internal loop - * - use less common registers, and avoid pushing these reg into stack - * The asm hack causes Clang to assume that XXH3_kSecretPtr aliases with - * customSecret, and on aarch64, this prevented LDP from merging two - * loads together for free. Putting the loads together before the stores - * properly generates LDP. - */ - __asm__("" : "+r"(dest)); - #endif - - /* GCC -O2 need unroll loop manually */ - dest[0] = _mm256_add_epi64(_mm256_stream_load_si256(src + 0), seed); - dest[1] = _mm256_add_epi64(_mm256_stream_load_si256(src + 1), seed); - dest[2] = _mm256_add_epi64(_mm256_stream_load_si256(src + 2), seed); - dest[3] = _mm256_add_epi64(_mm256_stream_load_si256(src + 3), seed); - dest[4] = _mm256_add_epi64(_mm256_stream_load_si256(src + 4), seed); - dest[5] = _mm256_add_epi64(_mm256_stream_load_si256(src + 5), seed); - - } - -} - -#endif - -#if (XXH_VECTOR == XXH_SSE2) || defined(XXH_X86DISPATCH) - - #ifndef XXH_TARGET_SSE2 - #define XXH_TARGET_SSE2 /* disable attribute target */ - #endif - -XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_accumulate_512_sse2( - void *XXH_RESTRICT acc, const void *XXH_RESTRICT input, - const void *XXH_RESTRICT secret, XXH3_accWidth_e accWidth) { - - /* SSE2 is just a half-scale version of the AVX2 version. */ - XXH_ASSERT((((size_t)acc) & 15) == 0); - { - - XXH_ALIGN(16) __m128i *const xacc = (__m128i *)acc; - /* Unaligned. This is mainly for pointer arithmetic, and because - * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ - const __m128i *const xinput = (const __m128i *)input; - /* Unaligned. This is mainly for pointer arithmetic, and because - * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ - const __m128i *const xsecret = (const __m128i *)secret; - - size_t i; - for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m128i); i++) { - - /* data_vec = xinput[i]; */ - __m128i const data_vec = _mm_loadu_si128(xinput + i); - /* key_vec = xsecret[i]; */ - __m128i const key_vec = _mm_loadu_si128(xsecret + i); - /* data_key = data_vec ^ key_vec; */ - __m128i const data_key = _mm_xor_si128(data_vec, key_vec); - /* data_key_lo = data_key >> 32; */ - __m128i const data_key_lo = - _mm_shuffle_epi32(data_key, _MM_SHUFFLE(0, 3, 0, 1)); - /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ - __m128i const product = _mm_mul_epu32(data_key, data_key_lo); - if (accWidth == XXH3_acc_128bits) { - - /* xacc[i] += swap(data_vec); */ - __m128i const data_swap = - _mm_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2)); - __m128i const sum = _mm_add_epi64(xacc[i], data_swap); - /* xacc[i] += product; */ - xacc[i] = _mm_add_epi64(product, sum); - - } else { /* XXH3_acc_64bits */ - - /* xacc[i] += data_vec; */ - __m128i const sum = _mm_add_epi64(xacc[i], data_vec); - /* xacc[i] += product; */ - xacc[i] = _mm_add_epi64(product, sum); - - } - - } - - } - -} - -XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_scrambleAcc_sse2( - void *XXH_RESTRICT acc, const void *XXH_RESTRICT secret) { - - XXH_ASSERT((((size_t)acc) & 15) == 0); - { - - XXH_ALIGN(16) __m128i *const xacc = (__m128i *)acc; - /* Unaligned. This is mainly for pointer arithmetic, and because - * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ - const __m128i *const xsecret = (const __m128i *)secret; - const __m128i prime32 = _mm_set1_epi32((int)XXH_PRIME32_1); - - size_t i; - for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m128i); i++) { - - /* xacc[i] ^= (xacc[i] >> 47) */ - __m128i const acc_vec = xacc[i]; - __m128i const shifted = _mm_srli_epi64(acc_vec, 47); - __m128i const data_vec = _mm_xor_si128(acc_vec, shifted); - /* xacc[i] ^= xsecret[i]; */ - __m128i const key_vec = _mm_loadu_si128(xsecret + i); - __m128i const data_key = _mm_xor_si128(data_vec, key_vec); - - /* xacc[i] *= XXH_PRIME32_1; */ - __m128i const data_key_hi = - _mm_shuffle_epi32(data_key, _MM_SHUFFLE(0, 3, 0, 1)); - __m128i const prod_lo = _mm_mul_epu32(data_key, prime32); - __m128i const prod_hi = _mm_mul_epu32(data_key_hi, prime32); - xacc[i] = _mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32)); - - } - - } - -} - -XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2( - void *XXH_RESTRICT customSecret, xxh_u64 seed64) { - - XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); - (void)(&XXH_writeLE64); - { - - int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i); - - #if defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER < 1900 - // MSVC 32bit mode does not support _mm_set_epi64x before 2015 - XXH_ALIGN(16) - const xxh_i64 seed64x2[2] = {(xxh_i64)seed64, -(xxh_i64)seed64}; - __m128i const seed = _mm_load_si128((__m128i const *)seed64x2); - #else - __m128i const seed = _mm_set_epi64x(-(xxh_i64)seed64, (xxh_i64)seed64); - #endif - int i; - - XXH_ALIGN(64) const float *const src = (float const *)XXH3_kSecret; - XXH_ALIGN(XXH_SEC_ALIGN) __m128i *dest = (__m128i *)customSecret; - #if defined(__GNUC__) || defined(__clang__) - /* - * On GCC & Clang, marking 'dest' as modified will cause the compiler: - * - do not extract the secret from sse registers in the internal loop - * - use less common registers, and avoid pushing these reg into stack - */ - __asm__("" : "+r"(dest)); - #endif - - for (i = 0; i < nbRounds; ++i) { - - dest[i] = _mm_add_epi64(_mm_castps_si128(_mm_load_ps(src + i * 4)), seed); - - } - - } - -} - -#endif - -#if (XXH_VECTOR == XXH_NEON) - -XXH_FORCE_INLINE void XXH3_accumulate_512_neon(void *XXH_RESTRICT acc, - const void *XXH_RESTRICT input, - const void *XXH_RESTRICT secret, - XXH3_accWidth_e accWidth) { - - XXH_ASSERT((((size_t)acc) & 15) == 0); - { - - XXH_ALIGN(16) uint64x2_t *const xacc = (uint64x2_t *)acc; - /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. - */ - uint8_t const *const xinput = (const uint8_t *)input; - uint8_t const *const xsecret = (const uint8_t *)secret; - - size_t i; - for (i = 0; i < XXH_STRIPE_LEN / sizeof(uint64x2_t); i++) { - - /* data_vec = xinput[i]; */ - uint8x16_t data_vec = vld1q_u8(xinput + (i * 16)); - /* key_vec = xsecret[i]; */ - uint8x16_t key_vec = vld1q_u8(xsecret + (i * 16)); - uint64x2_t data_key; - uint32x2_t data_key_lo, data_key_hi; - if (accWidth == XXH3_acc_64bits) { - - /* xacc[i] += data_vec; */ - xacc[i] = vaddq_u64(xacc[i], vreinterpretq_u64_u8(data_vec)); - - } else { /* XXH3_acc_128bits */ - - /* xacc[i] += swap(data_vec); */ - uint64x2_t const data64 = vreinterpretq_u64_u8(data_vec); - uint64x2_t const swapped = vextq_u64(data64, data64, 1); - xacc[i] = vaddq_u64(xacc[i], swapped); - - } - - /* data_key = data_vec ^ key_vec; */ - data_key = vreinterpretq_u64_u8(veorq_u8(data_vec, key_vec)); - /* data_key_lo = (uint32x2_t) (data_key & 0xFFFFFFFF); - * data_key_hi = (uint32x2_t) (data_key >> 32); - * data_key = UNDEFINED; */ - XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi); - /* xacc[i] += (uint64x2_t) data_key_lo * (uint64x2_t) data_key_hi; */ - xacc[i] = vmlal_u32(xacc[i], data_key_lo, data_key_hi); - - } - - } - -} - -XXH_FORCE_INLINE void XXH3_scrambleAcc_neon(void *XXH_RESTRICT acc, - const void *XXH_RESTRICT secret) { - - XXH_ASSERT((((size_t)acc) & 15) == 0); - - { - - uint64x2_t * xacc = (uint64x2_t *)acc; - uint8_t const *xsecret = (uint8_t const *)secret; - uint32x2_t prime = vdup_n_u32(XXH_PRIME32_1); - - size_t i; - for (i = 0; i < XXH_STRIPE_LEN / sizeof(uint64x2_t); i++) { - - /* xacc[i] ^= (xacc[i] >> 47); */ - uint64x2_t acc_vec = xacc[i]; - uint64x2_t shifted = vshrq_n_u64(acc_vec, 47); - uint64x2_t data_vec = veorq_u64(acc_vec, shifted); - - /* xacc[i] ^= xsecret[i]; */ - uint8x16_t key_vec = vld1q_u8(xsecret + (i * 16)); - uint64x2_t data_key = veorq_u64(data_vec, vreinterpretq_u64_u8(key_vec)); - - /* xacc[i] *= XXH_PRIME32_1 */ - uint32x2_t data_key_lo, data_key_hi; - /* data_key_lo = (uint32x2_t) (xacc[i] & 0xFFFFFFFF); - * data_key_hi = (uint32x2_t) (xacc[i] >> 32); - * xacc[i] = UNDEFINED; */ - XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi); - { /* - * prod_hi = (data_key >> 32) * XXH_PRIME32_1; - * - * Avoid vmul_u32 + vshll_n_u32 since Clang 6 and 7 will - * incorrectly "optimize" this: - * tmp = vmul_u32(vmovn_u64(a), vmovn_u64(b)); - * shifted = vshll_n_u32(tmp, 32); - * to this: - * tmp = "vmulq_u64"(a, b); // no such thing! - * shifted = vshlq_n_u64(tmp, 32); - * - * However, unlike SSE, Clang lacks a 64-bit multiply routine - * for NEON, and it scalarizes two 64-bit multiplies instead. - * - * vmull_u32 has the same timing as vmul_u32, and it avoids - * this bug completely. - * See https://bugs.llvm.org/show_bug.cgi?id=39967 - */ - uint64x2_t prod_hi = vmull_u32(data_key_hi, prime); - /* xacc[i] = prod_hi << 32; */ - xacc[i] = vshlq_n_u64(prod_hi, 32); - /* xacc[i] += (prod_hi & 0xFFFFFFFF) * XXH_PRIME32_1; */ - xacc[i] = vmlal_u32(xacc[i], data_key_lo, prime); - - } - - } - - } - -} - -#endif - -#if (XXH_VECTOR == XXH_VSX) - -XXH_FORCE_INLINE void XXH3_accumulate_512_vsx(void *XXH_RESTRICT acc, - const void *XXH_RESTRICT input, - const void *XXH_RESTRICT secret, - XXH3_accWidth_e accWidth) { - - xxh_u64x2 *const xacc = (xxh_u64x2 *)acc; /* presumed aligned */ - xxh_u64x2 const *const xinput = - (xxh_u64x2 const *)input; /* no alignment restriction */ - xxh_u64x2 const *const xsecret = - (xxh_u64x2 const *)secret; /* no alignment restriction */ - xxh_u64x2 const v32 = {32, 32}; - size_t i; - for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { - - /* data_vec = xinput[i]; */ - xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + i); - /* key_vec = xsecret[i]; */ - xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i); - xxh_u64x2 const data_key = data_vec ^ key_vec; - /* shuffled = (data_key << 32) | (data_key >> 32); */ - xxh_u32x4 const shuffled = (xxh_u32x4)vec_rl(data_key, v32); - /* product = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)shuffled & - * 0xFFFFFFFF); */ - xxh_u64x2 const product = XXH_vec_mulo((xxh_u32x4)data_key, shuffled); - xacc[i] += product; - - if (accWidth == XXH3_acc_64bits) { - - xacc[i] += data_vec; - - } else { /* XXH3_acc_128bits */ - - /* swap high and low halves */ - #ifdef __s390x__ - xxh_u64x2 const data_swapped = vec_permi(data_vec, data_vec, 2); - #else - xxh_u64x2 const data_swapped = vec_xxpermdi(data_vec, data_vec, 2); - #endif - xacc[i] += data_swapped; - - } - - } - -} - -XXH_FORCE_INLINE void XXH3_scrambleAcc_vsx(void *XXH_RESTRICT acc, - const void *XXH_RESTRICT secret) { - - XXH_ASSERT((((size_t)acc) & 15) == 0); - - { - - xxh_u64x2 *const xacc = (xxh_u64x2 *)acc; - const xxh_u64x2 *const xsecret = (const xxh_u64x2 *)secret; - /* constants */ - xxh_u64x2 const v32 = {32, 32}; - xxh_u64x2 const v47 = {47, 47}; - xxh_u32x4 const prime = {XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1, - XXH_PRIME32_1}; - size_t i; - for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { - - /* xacc[i] ^= (xacc[i] >> 47); */ - xxh_u64x2 const acc_vec = xacc[i]; - xxh_u64x2 const data_vec = acc_vec ^ (acc_vec >> v47); - - /* xacc[i] ^= xsecret[i]; */ - xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i); - xxh_u64x2 const data_key = data_vec ^ key_vec; - - /* xacc[i] *= XXH_PRIME32_1 */ - /* prod_lo = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)prime & - * 0xFFFFFFFF); */ - xxh_u64x2 const prod_even = XXH_vec_mule((xxh_u32x4)data_key, prime); - /* prod_hi = ((xxh_u64x2)data_key >> 32) * ((xxh_u64x2)prime >> 32); */ - xxh_u64x2 const prod_odd = XXH_vec_mulo((xxh_u32x4)data_key, prime); - xacc[i] = prod_odd + (prod_even << v32); - - } - - } - -} - -#endif - -/* scalar variants - universal */ - -XXH_FORCE_INLINE void XXH3_accumulate_512_scalar( - void *XXH_RESTRICT acc, const void *XXH_RESTRICT input, - const void *XXH_RESTRICT secret, XXH3_accWidth_e accWidth) { - - XXH_ALIGN(XXH_ACC_ALIGN) - xxh_u64 *const xacc = (xxh_u64 *)acc; /* presumed aligned */ - const xxh_u8 *const xinput = - (const xxh_u8 *)input; /* no alignment restriction */ - const xxh_u8 *const xsecret = - (const xxh_u8 *)secret; /* no alignment restriction */ - size_t i; - XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN - 1)) == 0); - for (i = 0; i < XXH_ACC_NB; i++) { - - xxh_u64 const data_val = XXH_readLE64(xinput + 8 * i); - xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + i * 8); - - if (accWidth == XXH3_acc_64bits) { - - xacc[i] += data_val; - - } else { - - xacc[i ^ 1] += data_val; /* swap adjacent lanes */ - - } - - xacc[i] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32); - - } - -} - -XXH_FORCE_INLINE void XXH3_scrambleAcc_scalar(void *XXH_RESTRICT acc, - const void *XXH_RESTRICT secret) { - - XXH_ALIGN(XXH_ACC_ALIGN) - xxh_u64 *const xacc = (xxh_u64 *)acc; /* presumed aligned */ - const xxh_u8 *const xsecret = - (const xxh_u8 *)secret; /* no alignment restriction */ - size_t i; - XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN - 1)) == 0); - for (i = 0; i < XXH_ACC_NB; i++) { - - xxh_u64 const key64 = XXH_readLE64(xsecret + 8 * i); - xxh_u64 acc64 = xacc[i]; - acc64 = XXH_xorshift64(acc64, 47); - acc64 ^= key64; - acc64 *= XXH_PRIME32_1; - xacc[i] = acc64; - - } - -} - -XXH_FORCE_INLINE void XXH3_initCustomSecret_scalar( - void *XXH_RESTRICT customSecret, xxh_u64 seed64) { - - /* - * We need a separate pointer for the hack below, - * which requires a non-const pointer. - * Any decent compiler will optimize this out otherwise. - */ - const xxh_u8 *kSecretPtr = XXH3_kSecret; - XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); - -#if defined(__clang__) && defined(__aarch64__) - /* - * UGLY HACK: - * Clang generates a bunch of MOV/MOVK pairs for aarch64, and they are - * placed sequentially, in order, at the top of the unrolled loop. - * - * While MOVK is great for generating constants (2 cycles for a 64-bit - * constant compared to 4 cycles for LDR), long MOVK chains stall the - * integer pipelines: - * I L S - * MOVK - * MOVK - * MOVK - * MOVK - * ADD - * SUB STR - * STR - * By forcing loads from memory (as the asm line causes Clang to assume - * that XXH3_kSecretPtr has been changed), the pipelines are used more - * efficiently: - * I L S - * LDR - * ADD LDR - * SUB STR - * STR - * XXH3_64bits_withSeed, len == 256, Snapdragon 835 - * without hack: 2654.4 MB/s - * with hack: 3202.9 MB/s - */ - __asm__("" : "+r"(kSecretPtr)); -#endif - /* - * Note: in debug mode, this overrides the asm optimization - * and Clang will emit MOVK chains again. - */ - XXH_ASSERT(kSecretPtr == XXH3_kSecret); - - { - - int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16; - int i; - for (i = 0; i < nbRounds; i++) { - - /* - * The asm hack causes Clang to assume that kSecretPtr aliases with - * customSecret, and on aarch64, this prevented LDP from merging two - * loads together for free. Putting the loads together before the stores - * properly generates LDP. - */ - xxh_u64 lo = XXH_readLE64(kSecretPtr + 16 * i) + seed64; - xxh_u64 hi = XXH_readLE64(kSecretPtr + 16 * i + 8) - seed64; - XXH_writeLE64((xxh_u8 *)customSecret + 16 * i, lo); - XXH_writeLE64((xxh_u8 *)customSecret + 16 * i + 8, hi); - - } - - } - -} - -typedef void (*XXH3_f_accumulate_512)(void *XXH_RESTRICT, const void *, - const void *, XXH3_accWidth_e); -typedef void (*XXH3_f_scrambleAcc)(void *XXH_RESTRICT, const void *); -typedef void (*XXH3_f_initCustomSecret)(void *XXH_RESTRICT, xxh_u64); - -#if (XXH_VECTOR == XXH_AVX512) - - #define XXH3_accumulate_512 XXH3_accumulate_512_avx512 - #define XXH3_scrambleAcc XXH3_scrambleAcc_avx512 - #define XXH3_initCustomSecret XXH3_initCustomSecret_avx512 - -#elif (XXH_VECTOR == XXH_AVX2) - - #define XXH3_accumulate_512 XXH3_accumulate_512_avx2 - #define XXH3_scrambleAcc XXH3_scrambleAcc_avx2 - #define XXH3_initCustomSecret XXH3_initCustomSecret_avx2 - -#elif (XXH_VECTOR == XXH_SSE2) - - #define XXH3_accumulate_512 XXH3_accumulate_512_sse2 - #define XXH3_scrambleAcc XXH3_scrambleAcc_sse2 - #define XXH3_initCustomSecret XXH3_initCustomSecret_sse2 - -#elif (XXH_VECTOR == XXH_NEON) - - #define XXH3_accumulate_512 XXH3_accumulate_512_neon - #define XXH3_scrambleAcc XXH3_scrambleAcc_neon - #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar - -#elif (XXH_VECTOR == XXH_VSX) - - #define XXH3_accumulate_512 XXH3_accumulate_512_vsx - #define XXH3_scrambleAcc XXH3_scrambleAcc_vsx - #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar - -#else /* scalar */ - - #define XXH3_accumulate_512 XXH3_accumulate_512_scalar - #define XXH3_scrambleAcc XXH3_scrambleAcc_scalar - #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar - -#endif - -#ifndef XXH_PREFETCH_DIST - #ifdef __clang__ - #define XXH_PREFETCH_DIST 320 - #else - #if (XXH_VECTOR == XXH_AVX512) - #define XXH_PREFETCH_DIST 512 - #else - #define XXH_PREFETCH_DIST 384 - #endif - #endif /* __clang__ */ -#endif /* XXH_PREFETCH_DIST */ - -/* - * XXH3_accumulate() - * Loops over XXH3_accumulate_512(). - * Assumption: nbStripes will not overflow the secret size - */ -XXH_FORCE_INLINE void XXH3_accumulate(xxh_u64 *XXH_RESTRICT acc, - const xxh_u8 *XXH_RESTRICT input, - const xxh_u8 *XXH_RESTRICT secret, - size_t nbStripes, - XXH3_accWidth_e accWidth, - XXH3_f_accumulate_512 f_acc512) { - - size_t n; - for (n = 0; n < nbStripes; n++) { - - const xxh_u8 *const in = input + n * XXH_STRIPE_LEN; - XXH_PREFETCH(in + XXH_PREFETCH_DIST); - f_acc512(acc, in, secret + n * XXH_SECRET_CONSUME_RATE, accWidth); - - } - -} - -XXH_FORCE_INLINE void XXH3_hashLong_internal_loop( - xxh_u64 *XXH_RESTRICT acc, const xxh_u8 *XXH_RESTRICT input, size_t len, - const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, - XXH3_accWidth_e accWidth, XXH3_f_accumulate_512 f_acc512, - XXH3_f_scrambleAcc f_scramble) { - - size_t const nb_rounds = - (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE; - size_t const block_len = XXH_STRIPE_LEN * nb_rounds; - size_t const nb_blocks = len / block_len; - - size_t n; - - XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); - - for (n = 0; n < nb_blocks; n++) { - - XXH3_accumulate(acc, input + n * block_len, secret, nb_rounds, accWidth, - f_acc512); - f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN); - - } - - /* last partial block */ - XXH_ASSERT(len > XXH_STRIPE_LEN); - { - - size_t const nbStripes = (len - (block_len * nb_blocks)) / XXH_STRIPE_LEN; - XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE)); - XXH3_accumulate(acc, input + nb_blocks * block_len, secret, nbStripes, - accWidth, f_acc512); - - /* last stripe */ - if (len & (XXH_STRIPE_LEN - 1)) { - - const xxh_u8 *const p = input + len - XXH_STRIPE_LEN; - /* Do not align on 8, so that the secret is different from the scrambler - */ -#define XXH_SECRET_LASTACC_START 7 - f_acc512(acc, p, - secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START, - accWidth); - - } - - } - -} - -XXH_FORCE_INLINE xxh_u64 XXH3_mix2Accs(const xxh_u64 *XXH_RESTRICT acc, - const xxh_u8 *XXH_RESTRICT secret) { - - return XXH3_mul128_fold64(acc[0] ^ XXH_readLE64(secret), - acc[1] ^ XXH_readLE64(secret + 8)); - -} - -static XXH64_hash_t XXH3_mergeAccs(const xxh_u64 *XXH_RESTRICT acc, - const xxh_u8 *XXH_RESTRICT secret, - xxh_u64 start) { - - xxh_u64 result64 = start; - size_t i = 0; - - for (i = 0; i < 4; i++) { - - result64 += XXH3_mix2Accs(acc + 2 * i, secret + 16 * i); -#if defined(__clang__) /* Clang */ \ - && (defined(__arm__) || defined(__thumb__)) /* ARMv7 */ \ - && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ - && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ - /* - * UGLY HACK: - * Prevent autovectorization on Clang ARMv7-a. Exact same problem as - * the one in XXH3_len_129to240_64b. Speeds up shorter keys > 240b. - * XXH3_64bits, len == 256, Snapdragon 835: - * without hack: 2063.7 MB/s - * with hack: 2560.7 MB/s - */ - __asm__("" : "+r"(result64)); -#endif - - } - - return XXH3_avalanche(result64); - -} - -#define XXH3_INIT_ACC \ - { \ - \ - XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, XXH_PRIME64_4, \ - XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 \ - \ - } - -XXH_FORCE_INLINE XXH64_hash_t XXH3_hashLong_64b_internal( - const xxh_u8 *XXH_RESTRICT input, size_t len, - const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, - XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble) { - - XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; - - XXH3_hashLong_internal_loop(acc, input, len, secret, secretSize, - XXH3_acc_64bits, f_acc512, f_scramble); - - /* converge into final hash */ - XXH_STATIC_ASSERT(sizeof(acc) == 64); - /* do not align on 8, so that the secret is different from the accumulator */ -#define XXH_SECRET_MERGEACCS_START 11 - XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); - return XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, - (xxh_u64)len * XXH_PRIME64_1); - -} - -/* - * It's important for performance that XXH3_hashLong is not inlined. - */ -XXH_NO_INLINE XXH64_hash_t XXH3_hashLong_64b_withSecret( - const xxh_u8 *XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, - const xxh_u8 *XXH_RESTRICT secret, size_t secretLen) { - - (void)seed64; - return XXH3_hashLong_64b_internal(input, len, secret, secretLen, - XXH3_accumulate_512, XXH3_scrambleAcc); - -} - -/* - * XXH3_hashLong_64b_withSeed(): - * Generate a custom key based on alteration of default XXH3_kSecret with the - * seed, and then use this key for long mode hashing. - * - * This operation is decently fast but nonetheless costs a little bit of time. - * Try to avoid it whenever possible (typically when seed==0). - * - * It's important for performance that XXH3_hashLong is not inlined. Not sure - * why (uop cache maybe?), but the difference is large and easily measurable. - */ -XXH_FORCE_INLINE XXH64_hash_t XXH3_hashLong_64b_withSeed_internal( - const xxh_u8 *input, size_t len, XXH64_hash_t seed, - XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble, - XXH3_f_initCustomSecret f_initSec) { - - if (seed == 0) - return XXH3_hashLong_64b_internal( - input, len, XXH3_kSecret, sizeof(XXH3_kSecret), f_acc512, f_scramble); - { - - XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; - f_initSec(secret, seed); - return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret), - f_acc512, f_scramble); - - } - -} - -/* - * It's important for performance that XXH3_hashLong is not inlined. - */ -XXH_NO_INLINE XXH64_hash_t XXH3_hashLong_64b_withSeed(const xxh_u8 *input, - size_t len, - XXH64_hash_t seed, - const xxh_u8 *secret, - size_t secretLen) { - - (void)secret; - (void)secretLen; - return XXH3_hashLong_64b_withSeed_internal( - input, len, seed, XXH3_accumulate_512, XXH3_scrambleAcc, - XXH3_initCustomSecret); - -} - -typedef XXH64_hash_t (*XXH3_hashLong64_f)(const xxh_u8 *XXH_RESTRICT, size_t, - XXH64_hash_t, - const xxh_u8 *XXH_RESTRICT, size_t); - -XXH_FORCE_INLINE XXH64_hash_t -XXH3_64bits_internal(const void *XXH_RESTRICT input, size_t len, - XXH64_hash_t seed64, const void *XXH_RESTRICT secret, - size_t secretLen, XXH3_hashLong64_f f_hashLong) { - - XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); - /* - * If an action is to be taken if `secretLen` condition is not respected, - * it should be done here. - * For now, it's a contract pre-condition. - * Adding a check and a branch here would cost performance at every hash. - * Also, note that function signature doesn't offer room to return an error. - */ - if (len <= 16) - return XXH3_len_0to16_64b((const xxh_u8 *)input, len, - (const xxh_u8 *)secret, seed64); - if (len <= 128) - return XXH3_len_17to128_64b((const xxh_u8 *)input, len, - (const xxh_u8 *)secret, secretLen, seed64); - if (len <= XXH3_MIDSIZE_MAX) - return XXH3_len_129to240_64b((const xxh_u8 *)input, len, - (const xxh_u8 *)secret, secretLen, seed64); - return f_hashLong((const xxh_u8 *)input, len, seed64, (const xxh_u8 *)secret, - secretLen); - -} - -/* === Public entry point === */ - -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void *input, size_t len) { - - return XXH3_64bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), - XXH3_hashLong_64b_withSecret); - -} - -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void *input, - size_t len, - const void *secret, - size_t secretSize) { - - return XXH3_64bits_internal(input, len, 0, secret, secretSize, - XXH3_hashLong_64b_withSecret); - -} - -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void *input, size_t len, - XXH64_hash_t seed) { - - return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, - sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed); - -} - -/* === XXH3 streaming === */ - -/* - * Malloc's a pointer that is always aligned to align. - * - * This must be freed with `XXH_alignedFree()`. - * - * malloc typically guarantees 16 byte alignment on 64-bit systems and 8 byte - * alignment on 32-bit. This isn't enough for the 32 byte aligned loads in AVX2 - * or on 32-bit, the 16 byte aligned loads in SSE2 and NEON. - * - * This underalignment previously caused a rather obvious crash which went - * completely unnoticed due to XXH3_createState() not actually being tested. - * Credit to RedSpah for noticing this bug. - * - * The alignment is done manually: Functions like posix_memalign or _mm_malloc - * are avoided: To maintain portability, we would have to write a fallback - * like this anyways, and besides, testing for the existence of library - * functions without relying on external build tools is impossible. - * - * The method is simple: Overallocate, manually align, and store the offset - * to the original behind the returned pointer. - * - * Align must be a power of 2 and 8 <= align <= 128. - */ -static void *XXH_alignedMalloc(size_t s, size_t align) { - - XXH_ASSERT(align <= 128 && align >= 8); /* range check */ - XXH_ASSERT((align & (align - 1)) == 0); /* power of 2 */ - XXH_ASSERT(s != 0 && s < (s + align)); /* empty/overflow */ - { /* Overallocate to make room for manual realignment and an offset byte */ - xxh_u8 *base = (xxh_u8 *)XXH_malloc(s + align); - if (base != NULL) { - - /* - * Get the offset needed to align this pointer. - * - * Even if the returned pointer is aligned, there will always be - * at least one byte to store the offset to the original pointer. - */ - size_t offset = align - ((size_t)base & (align - 1)); /* base % align */ - /* Add the offset for the now-aligned pointer */ - xxh_u8 *ptr = base + offset; - - XXH_ASSERT((size_t)ptr % align == 0); - - /* Store the offset immediately before the returned pointer. */ - ptr[-1] = (xxh_u8)offset; - return ptr; - - } - - return NULL; - - } - -} - -/* - * Frees an aligned pointer allocated by XXH_alignedMalloc(). Don't pass - * normal malloc'd pointers, XXH_alignedMalloc has a specific data layout. - */ -static void XXH_alignedFree(void *p) { - - if (p != NULL) { - - xxh_u8 *ptr = (xxh_u8 *)p; - /* Get the offset byte we added in XXH_malloc. */ - xxh_u8 offset = ptr[-1]; - /* Free the original malloc'd pointer */ - xxh_u8 *base = ptr - offset; - XXH_free(base); - - } - -} - -XXH_PUBLIC_API XXH3_state_t *XXH3_createState(void) { - - return (XXH3_state_t *)XXH_alignedMalloc(sizeof(XXH3_state_t), 64); - -} - -XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t *statePtr) { - - XXH_alignedFree(statePtr); - return XXH_OK; - -} - -XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t * dst_state, - const XXH3_state_t *src_state) { - - memcpy(dst_state, src_state, sizeof(*dst_state)); - -} - -static void XXH3_64bits_reset_internal(XXH3_state_t *statePtr, - XXH64_hash_t seed, const xxh_u8 *secret, - size_t secretSize) { - - XXH_ASSERT(statePtr != NULL); - memset(statePtr, 0, sizeof(*statePtr)); - statePtr->acc[0] = XXH_PRIME32_3; - statePtr->acc[1] = XXH_PRIME64_1; - statePtr->acc[2] = XXH_PRIME64_2; - statePtr->acc[3] = XXH_PRIME64_3; - statePtr->acc[4] = XXH_PRIME64_4; - statePtr->acc[5] = XXH_PRIME32_2; - statePtr->acc[6] = XXH_PRIME64_5; - statePtr->acc[7] = XXH_PRIME32_1; - statePtr->seed = seed; - XXH_ASSERT(secret != NULL); - statePtr->extSecret = secret; - XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); - statePtr->secretLimit = secretSize - XXH_STRIPE_LEN; - statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE; - -} - -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t *statePtr) { - - if (statePtr == NULL) return XXH_ERROR; - XXH3_64bits_reset_internal(statePtr, 0, XXH3_kSecret, - XXH_SECRET_DEFAULT_SIZE); - return XXH_OK; - -} - -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret( - XXH3_state_t *statePtr, const void *secret, size_t secretSize) { - - if (statePtr == NULL) return XXH_ERROR; - XXH3_64bits_reset_internal(statePtr, 0, (const xxh_u8 *)secret, secretSize); - if (secret == NULL) return XXH_ERROR; - if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; - return XXH_OK; - -} - -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t *statePtr, - XXH64_hash_t seed) { - - if (statePtr == NULL) return XXH_ERROR; - XXH3_64bits_reset_internal(statePtr, seed, XXH3_kSecret, - XXH_SECRET_DEFAULT_SIZE); - XXH3_initCustomSecret(statePtr->customSecret, seed); - statePtr->extSecret = NULL; - return XXH_OK; - -} - -XXH_FORCE_INLINE void XXH3_consumeStripes( - xxh_u64 *XXH_RESTRICT acc, size_t *XXH_RESTRICT nbStripesSoFarPtr, - size_t nbStripesPerBlock, const xxh_u8 *XXH_RESTRICT input, - size_t totalStripes, const xxh_u8 *XXH_RESTRICT secret, size_t secretLimit, - XXH3_accWidth_e accWidth, XXH3_f_accumulate_512 f_acc512, - XXH3_f_scrambleAcc f_scramble) { - - XXH_ASSERT(*nbStripesSoFarPtr < nbStripesPerBlock); - if (nbStripesPerBlock - *nbStripesSoFarPtr <= totalStripes) { - - /* need a scrambling operation */ - size_t const nbStripes = nbStripesPerBlock - *nbStripesSoFarPtr; - XXH3_accumulate(acc, input, - secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, - nbStripes, accWidth, f_acc512); - f_scramble(acc, secret + secretLimit); - XXH3_accumulate(acc, input + nbStripes * XXH_STRIPE_LEN, secret, - totalStripes - nbStripes, accWidth, f_acc512); - *nbStripesSoFarPtr = totalStripes - nbStripes; - - } else { - - XXH3_accumulate(acc, input, - secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, - totalStripes, accWidth, f_acc512); - *nbStripesSoFarPtr += totalStripes; - - } - -} - -/* - * Both XXH3_64bits_update and XXH3_128bits_update use this routine. - */ -XXH_FORCE_INLINE XXH_errorcode XXH3_update(XXH3_state_t *state, - const xxh_u8 *input, size_t len, - XXH3_accWidth_e accWidth, - XXH3_f_accumulate_512 f_acc512, - XXH3_f_scrambleAcc f_scramble) { - - if (input == NULL) -#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && \ - (XXH_ACCEPT_NULL_INPUT_POINTER >= 1) - return XXH_OK; -#else - return XXH_ERROR; -#endif - - { - - const xxh_u8 *const bEnd = input + len; - const unsigned char *const secret = - (state->extSecret == NULL) ? state->customSecret : state->extSecret; - - state->totalLen += len; - - if (state->bufferedSize + len <= - XXH3_INTERNALBUFFER_SIZE) { /* fill in tmp buffer */ - XXH_memcpy(state->buffer + state->bufferedSize, input, len); - state->bufferedSize += (XXH32_hash_t)len; - return XXH_OK; - - } - - /* input is now > XXH3_INTERNALBUFFER_SIZE */ - -#define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN) - XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == - 0); /* clean multiple */ - - /* - * There is some input left inside the internal buffer. - * Fill it, then consume it. - */ - if (state->bufferedSize) { - - size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize; - XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize); - input += loadSize; - XXH3_consumeStripes(state->acc, &state->nbStripesSoFar, - state->nbStripesPerBlock, state->buffer, - XXH3_INTERNALBUFFER_STRIPES, secret, - state->secretLimit, accWidth, f_acc512, f_scramble); - state->bufferedSize = 0; - - } - - /* Consume input by full buffer quantities */ - if (input + XXH3_INTERNALBUFFER_SIZE <= bEnd) { - - const xxh_u8 *const limit = bEnd - XXH3_INTERNALBUFFER_SIZE; - do { - - XXH3_consumeStripes(state->acc, &state->nbStripesSoFar, - state->nbStripesPerBlock, input, - XXH3_INTERNALBUFFER_STRIPES, secret, - state->secretLimit, accWidth, f_acc512, f_scramble); - input += XXH3_INTERNALBUFFER_SIZE; - - } while (input <= limit); - - /* for last partial stripe */ - memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, - input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); - - } - - if (input < bEnd) { /* Some remaining input: buffer it */ - XXH_memcpy(state->buffer, input, (size_t)(bEnd - input)); - state->bufferedSize = (XXH32_hash_t)(bEnd - input); - - } - - } - - return XXH_OK; - -} - -XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update(XXH3_state_t *state, - const void *input, size_t len) { - - return XXH3_update(state, (const xxh_u8 *)input, len, XXH3_acc_64bits, - XXH3_accumulate_512, XXH3_scrambleAcc); - -} - -XXH_FORCE_INLINE void XXH3_digest_long(XXH64_hash_t * acc, - const XXH3_state_t * state, - const unsigned char *secret, - XXH3_accWidth_e accWidth) { - - /* - * Digest on a local copy. This way, the state remains unaltered, and it can - * continue ingesting more input afterwards. - */ - memcpy(acc, state->acc, sizeof(state->acc)); - if (state->bufferedSize >= XXH_STRIPE_LEN) { - - size_t const nbStripes = state->bufferedSize / XXH_STRIPE_LEN; - size_t nbStripesSoFar = state->nbStripesSoFar; - XXH3_consumeStripes(acc, &nbStripesSoFar, state->nbStripesPerBlock, - state->buffer, nbStripes, secret, state->secretLimit, - accWidth, XXH3_accumulate_512, XXH3_scrambleAcc); - if (state->bufferedSize % XXH_STRIPE_LEN) { /* one last partial stripe */ - XXH3_accumulate_512( - acc, state->buffer + state->bufferedSize - XXH_STRIPE_LEN, - secret + state->secretLimit - XXH_SECRET_LASTACC_START, accWidth); - - } - - } else { /* bufferedSize < XXH_STRIPE_LEN */ - - if (state->bufferedSize) { /* one last stripe */ - xxh_u8 lastStripe[XXH_STRIPE_LEN]; - size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize; - memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, - catchupSize); - memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); - XXH3_accumulate_512( - acc, lastStripe, - secret + state->secretLimit - XXH_SECRET_LASTACC_START, accWidth); - - } - - } - -} - -XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest(const XXH3_state_t *state) { - - const unsigned char *const secret = - (state->extSecret == NULL) ? state->customSecret : state->extSecret; - if (state->totalLen > XXH3_MIDSIZE_MAX) { - - XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; - XXH3_digest_long(acc, state, secret, XXH3_acc_64bits); - return XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, - (xxh_u64)state->totalLen * XXH_PRIME64_1); - - } - - /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */ - if (state->seed) - return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, - state->seed); - return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen), - secret, state->secretLimit + XXH_STRIPE_LEN); - -} - -#define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x)) - -XXH_PUBLIC_API void XXH3_generateSecret(void * secretBuffer, - const void *customSeed, - size_t customSeedSize) { - - XXH_ASSERT(secretBuffer != NULL); - if (customSeedSize == 0) { - - memcpy(secretBuffer, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); - return; - - } - - XXH_ASSERT(customSeed != NULL); - - { - - size_t const segmentSize = sizeof(XXH128_hash_t); - size_t const nbSegments = XXH_SECRET_DEFAULT_SIZE / segmentSize; - XXH128_canonical_t scrambler; - XXH64_hash_t seeds[12]; - size_t segnb; - XXH_ASSERT(nbSegments == 12); - XXH_ASSERT(segmentSize * nbSegments == - XXH_SECRET_DEFAULT_SIZE); /* exact multiple */ - XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0)); - - /* - * Copy customSeed to seeds[], truncating or repeating as necessary. - */ - { - - size_t toFill = XXH_MIN(customSeedSize, sizeof(seeds)); - size_t filled = toFill; - memcpy(seeds, customSeed, toFill); - while (filled < sizeof(seeds)) { - - toFill = XXH_MIN(filled, sizeof(seeds) - filled); - memcpy((char *)seeds + filled, seeds, toFill); - filled += toFill; - - } - - } - - /* generate secret */ - memcpy(secretBuffer, &scrambler, sizeof(scrambler)); - for (segnb = 1; segnb < nbSegments; segnb++) { - - size_t const segmentStart = segnb * segmentSize; - XXH128_canonical_t segment; - XXH128_canonicalFromHash(&segment, - XXH128(&scrambler, sizeof(scrambler), - XXH_readLE64(seeds + segnb) + segnb)); - memcpy((char *)secretBuffer + segmentStart, &segment, sizeof(segment)); - - } - - } - -} - -/* ========================================== - * XXH3 128 bits (a.k.a XXH128) - * ========================================== - * XXH3's 128-bit variant has better mixing and strength than the 64-bit - * variant, even without counting the significantly larger output size. - * - * For example, extra steps are taken to avoid the seed-dependent collisions - * in 17-240 byte inputs (See XXH3_mix16B and XXH128_mix32B). - * - * This strength naturally comes at the cost of some speed, especially on short - * lengths. Note that longer hashes are about as fast as the 64-bit version - * due to it using only a slight modification of the 64-bit loop. - * - * XXH128 is also more oriented towards 64-bit machines. It is still extremely - * fast for a _128-bit_ hash on 32-bit (it usually clears XXH64). - */ - -XXH_FORCE_INLINE XXH128_hash_t XXH3_len_1to3_128b(const xxh_u8 *input, - size_t len, - const xxh_u8 *secret, - XXH64_hash_t seed) { - - /* A doubled version of 1to3_64b with different constants. */ - XXH_ASSERT(input != NULL); - XXH_ASSERT(1 <= len && len <= 3); - XXH_ASSERT(secret != NULL); - /* - * len = 1: combinedl = { input[0], 0x01, input[0], input[0] } - * len = 2: combinedl = { input[1], 0x02, input[0], input[1] } - * len = 3: combinedl = { input[2], 0x03, input[0], input[1] } - */ - { - - xxh_u8 const c1 = input[0]; - xxh_u8 const c2 = input[len >> 1]; - xxh_u8 const c3 = input[len - 1]; - xxh_u32 const combinedl = ((xxh_u32)c1 << 16) | ((xxh_u32)c2 << 24) | - ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); - xxh_u32 const combinedh = XXH_rotl32(XXH_swap32(combinedl), 13); - xxh_u64 const bitflipl = - (XXH_readLE32(secret) ^ XXH_readLE32(secret + 4)) + seed; - xxh_u64 const bitfliph = - (XXH_readLE32(secret + 8) ^ XXH_readLE32(secret + 12)) - seed; - xxh_u64 const keyed_lo = (xxh_u64)combinedl ^ bitflipl; - xxh_u64 const keyed_hi = (xxh_u64)combinedh ^ bitfliph; - xxh_u64 const mixedl = keyed_lo * XXH_PRIME64_1; - xxh_u64 const mixedh = keyed_hi * XXH_PRIME64_5; - XXH128_hash_t h128; - h128.low64 = XXH3_avalanche(mixedl); - h128.high64 = XXH3_avalanche(mixedh); - return h128; - - } - -} - -XXH_FORCE_INLINE XXH128_hash_t XXH3_len_4to8_128b(const xxh_u8 *input, - size_t len, - const xxh_u8 *secret, - XXH64_hash_t seed) { - - XXH_ASSERT(input != NULL); - XXH_ASSERT(secret != NULL); - XXH_ASSERT(4 <= len && len <= 8); - seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; - { - - xxh_u32 const input_lo = XXH_readLE32(input); - xxh_u32 const input_hi = XXH_readLE32(input + len - 4); - xxh_u64 const input_64 = input_lo + ((xxh_u64)input_hi << 32); - xxh_u64 const bitflip = - (XXH_readLE64(secret + 16) ^ XXH_readLE64(secret + 24)) + seed; - xxh_u64 const keyed = input_64 ^ bitflip; - - /* Shift len to the left to ensure it is even, this avoids even multiplies. - */ - XXH128_hash_t m128 = XXH_mult64to128(keyed, XXH_PRIME64_1 + (len << 2)); - - m128.high64 += (m128.low64 << 1); - m128.low64 ^= (m128.high64 >> 3); - - m128.low64 = XXH_xorshift64(m128.low64, 35); - m128.low64 *= 0x9FB21C651E98DF25ULL; - m128.low64 = XXH_xorshift64(m128.low64, 28); - m128.high64 = XXH3_avalanche(m128.high64); - return m128; - - } - -} - -XXH_FORCE_INLINE XXH128_hash_t XXH3_len_9to16_128b(const xxh_u8 *input, - size_t len, - const xxh_u8 *secret, - XXH64_hash_t seed) { - - XXH_ASSERT(input != NULL); - XXH_ASSERT(secret != NULL); - XXH_ASSERT(9 <= len && len <= 16); - { - - xxh_u64 const bitflipl = - (XXH_readLE64(secret + 32) ^ XXH_readLE64(secret + 40)) - seed; - xxh_u64 const bitfliph = - (XXH_readLE64(secret + 48) ^ XXH_readLE64(secret + 56)) + seed; - xxh_u64 const input_lo = XXH_readLE64(input); - xxh_u64 input_hi = XXH_readLE64(input + len - 8); - XXH128_hash_t m128 = - XXH_mult64to128(input_lo ^ input_hi ^ bitflipl, XXH_PRIME64_1); - /* - * Put len in the middle of m128 to ensure that the length gets mixed to - * both the low and high bits in the 128x64 multiply below. - */ - m128.low64 += (xxh_u64)(len - 1) << 54; - input_hi ^= bitfliph; - /* - * Add the high 32 bits of input_hi to the high 32 bits of m128, then - * add the long product of the low 32 bits of input_hi and XXH_PRIME32_2 to - * the high 64 bits of m128. - * - * The best approach to this operation is different on 32-bit and 64-bit. - */ - if (sizeof(void *) < sizeof(xxh_u64)) { /* 32-bit */ - /* - * 32-bit optimized version, which is more readable. - * - * On 32-bit, it removes an ADC and delays a dependency between the two - * halves of m128.high64, but it generates an extra mask on 64-bit. - */ - m128.high64 += (input_hi & 0xFFFFFFFF00000000) + - XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2); - - } else { - - /* - * 64-bit optimized (albeit more confusing) version. - * - * Uses some properties of addition and multiplication to remove the mask: - * - * Let: - * a = input_hi.lo = (input_hi & 0x00000000FFFFFFFF) - * b = input_hi.hi = (input_hi & 0xFFFFFFFF00000000) - * c = XXH_PRIME32_2 - * - * a + (b * c) - * Inverse Property: x + y - x == y - * a + (b * (1 + c - 1)) - * Distributive Property: x * (y + z) == (x * y) + (x * z) - * a + (b * 1) + (b * (c - 1)) - * Identity Property: x * 1 == x - * a + b + (b * (c - 1)) - * - * Substitute a, b, and c: - * input_hi.hi + input_hi.lo + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - - * 1)) - * - * Since input_hi.hi + input_hi.lo == input_hi, we get this: - * input_hi + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) - */ - m128.high64 += - input_hi + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2 - 1); - - } - - /* m128 ^= XXH_swap64(m128 >> 64); */ - m128.low64 ^= XXH_swap64(m128.high64); - - { /* 128x64 multiply: h128 = m128 * XXH_PRIME64_2; */ - XXH128_hash_t h128 = XXH_mult64to128(m128.low64, XXH_PRIME64_2); - h128.high64 += m128.high64 * XXH_PRIME64_2; - - h128.low64 = XXH3_avalanche(h128.low64); - h128.high64 = XXH3_avalanche(h128.high64); - return h128; - - } - - } - -} - -/* - * Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN - */ -XXH_FORCE_INLINE XXH128_hash_t XXH3_len_0to16_128b(const xxh_u8 *input, - size_t len, - const xxh_u8 *secret, - XXH64_hash_t seed) { - - XXH_ASSERT(len <= 16); - { - - if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed); - if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed); - if (len) return XXH3_len_1to3_128b(input, len, secret, seed); - { - - XXH128_hash_t h128; - xxh_u64 const bitflipl = - XXH_readLE64(secret + 64) ^ XXH_readLE64(secret + 72); - xxh_u64 const bitfliph = - XXH_readLE64(secret + 80) ^ XXH_readLE64(secret + 88); - h128.low64 = XXH3_avalanche((XXH_PRIME64_1 + seed) ^ bitflipl); - h128.high64 = XXH3_avalanche((XXH_PRIME64_2 - seed) ^ bitfliph); - return h128; - - } - - } - -} - -/* - * A bit slower than XXH3_mix16B, but handles multiply by zero better. - */ -XXH_FORCE_INLINE XXH128_hash_t XXH128_mix32B(XXH128_hash_t acc, - const xxh_u8 *input_1, - const xxh_u8 *input_2, - const xxh_u8 *secret, - XXH64_hash_t seed) { - - acc.low64 += XXH3_mix16B(input_1, secret + 0, seed); - acc.low64 ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8); - acc.high64 += XXH3_mix16B(input_2, secret + 16, seed); - acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8); - return acc; - -} - -XXH_FORCE_INLINE XXH128_hash_t XXH3_len_17to128_128b( - const xxh_u8 *XXH_RESTRICT input, size_t len, - const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { - - XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); - (void)secretSize; - XXH_ASSERT(16 < len && len <= 128); - - { - - XXH128_hash_t acc; - acc.low64 = len * XXH_PRIME64_1; - acc.high64 = 0; - if (len > 32) { - - if (len > 64) { - - if (len > 96) { - - acc = XXH128_mix32B(acc, input + 48, input + len - 64, secret + 96, - seed); - - } - - acc = - XXH128_mix32B(acc, input + 32, input + len - 48, secret + 64, seed); - - } - - acc = XXH128_mix32B(acc, input + 16, input + len - 32, secret + 32, seed); - - } - - acc = XXH128_mix32B(acc, input, input + len - 16, secret, seed); - { - - XXH128_hash_t h128; - h128.low64 = acc.low64 + acc.high64; - h128.high64 = (acc.low64 * XXH_PRIME64_1) + (acc.high64 * XXH_PRIME64_4) + - ((len - seed) * XXH_PRIME64_2); - h128.low64 = XXH3_avalanche(h128.low64); - h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); - return h128; - - } - - } - -} - -XXH_NO_INLINE XXH128_hash_t XXH3_len_129to240_128b( - const xxh_u8 *XXH_RESTRICT input, size_t len, - const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { - - XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); - (void)secretSize; - XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); - - { - - XXH128_hash_t acc; - int const nbRounds = (int)len / 32; - int i; - acc.low64 = len * XXH_PRIME64_1; - acc.high64 = 0; - for (i = 0; i < 4; i++) { - - acc = XXH128_mix32B(acc, input + (32 * i), input + (32 * i) + 16, - secret + (32 * i), seed); - - } - - acc.low64 = XXH3_avalanche(acc.low64); - acc.high64 = XXH3_avalanche(acc.high64); - XXH_ASSERT(nbRounds >= 4); - for (i = 4; i < nbRounds; i++) { - - acc = XXH128_mix32B(acc, input + (32 * i), input + (32 * i) + 16, - secret + XXH3_MIDSIZE_STARTOFFSET + (32 * (i - 4)), - seed); - - } - - /* last bytes */ - acc = XXH128_mix32B( - acc, input + len - 16, input + len - 32, - secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16, - 0ULL - seed); - - { - - XXH128_hash_t h128; - h128.low64 = acc.low64 + acc.high64; - h128.high64 = (acc.low64 * XXH_PRIME64_1) + (acc.high64 * XXH_PRIME64_4) + - ((len - seed) * XXH_PRIME64_2); - h128.low64 = XXH3_avalanche(h128.low64); - h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); - return h128; - - } - - } - -} - -XXH_FORCE_INLINE XXH128_hash_t XXH3_hashLong_128b_internal( - const xxh_u8 *XXH_RESTRICT input, size_t len, - const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, - XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble) { - - XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; - - XXH3_hashLong_internal_loop(acc, input, len, secret, secretSize, - XXH3_acc_128bits, f_acc512, f_scramble); - - /* converge into final hash */ - XXH_STATIC_ASSERT(sizeof(acc) == 64); - XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); - { - - XXH128_hash_t h128; - h128.low64 = XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, - (xxh_u64)len * XXH_PRIME64_1); - h128.high64 = XXH3_mergeAccs( - acc, secret + secretSize - sizeof(acc) - XXH_SECRET_MERGEACCS_START, - ~((xxh_u64)len * XXH_PRIME64_2)); - return h128; - - } - -} - -/* - * It's important for performance that XXH3_hashLong is not inlined. - */ -XXH_NO_INLINE XXH128_hash_t XXH3_hashLong_128b_defaultSecret( - const xxh_u8 *XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, - const xxh_u8 *XXH_RESTRICT secret, size_t secretLen) { - - (void)seed64; - (void)secret; - (void)secretLen; - return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, - sizeof(XXH3_kSecret), XXH3_accumulate_512, - XXH3_scrambleAcc); - -} - -/* - * It's important for performance that XXH3_hashLong is not inlined. - */ -XXH_NO_INLINE XXH128_hash_t XXH3_hashLong_128b_withSecret( - const xxh_u8 *XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, - const xxh_u8 *XXH_RESTRICT secret, size_t secretLen) { - - (void)seed64; - return XXH3_hashLong_128b_internal(input, len, secret, secretLen, - XXH3_accumulate_512, XXH3_scrambleAcc); - -} - -XXH_FORCE_INLINE XXH128_hash_t XXH3_hashLong_128b_withSeed_internal( - const xxh_u8 *XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, - XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble, - XXH3_f_initCustomSecret f_initSec) { - - if (seed64 == 0) - return XXH3_hashLong_128b_internal( - input, len, XXH3_kSecret, sizeof(XXH3_kSecret), f_acc512, f_scramble); - { - - XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; - f_initSec(secret, seed64); - return XXH3_hashLong_128b_internal(input, len, secret, sizeof(secret), - f_acc512, f_scramble); - - } - -} - -/* - * It's important for performance that XXH3_hashLong is not inlined. - */ -XXH_NO_INLINE XXH128_hash_t XXH3_hashLong_128b_withSeed( - const xxh_u8 *input, size_t len, XXH64_hash_t seed64, - const xxh_u8 *XXH_RESTRICT secret, size_t secretLen) { - - (void)secret; - (void)secretLen; - return XXH3_hashLong_128b_withSeed_internal( - input, len, seed64, XXH3_accumulate_512, XXH3_scrambleAcc, - XXH3_initCustomSecret); - -} - -typedef XXH128_hash_t (*XXH3_hashLong128_f)(const xxh_u8 *XXH_RESTRICT, size_t, - XXH64_hash_t, - const xxh_u8 *XXH_RESTRICT, size_t); - -XXH_FORCE_INLINE XXH128_hash_t -XXH3_128bits_internal(const void *input, size_t len, XXH64_hash_t seed64, - const xxh_u8 *XXH_RESTRICT secret, size_t secretLen, - XXH3_hashLong128_f f_hl128) { - - XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); - /* - * If an action is to be taken if `secret` conditions are not respected, - * it should be done here. - * For now, it's a contract pre-condition. - * Adding a check and a branch here would cost performance at every hash. - */ - if (len <= 16) - return XXH3_len_0to16_128b((const xxh_u8 *)input, len, secret, seed64); - if (len <= 128) - return XXH3_len_17to128_128b((const xxh_u8 *)input, len, secret, secretLen, - seed64); - if (len <= XXH3_MIDSIZE_MAX) - return XXH3_len_129to240_128b((const xxh_u8 *)input, len, secret, secretLen, - seed64); - return f_hl128((const xxh_u8 *)input, len, seed64, secret, secretLen); - -} - -/* === Public XXH128 API === */ - -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void *input, size_t len) { - - return XXH3_128bits_internal(input, len, 0, XXH3_kSecret, - sizeof(XXH3_kSecret), - XXH3_hashLong_128b_withSecret); - -} - -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void *input, - size_t len, - const void *secret, - size_t secretSize) { - - return XXH3_128bits_internal(input, len, 0, (const xxh_u8 *)secret, - secretSize, XXH3_hashLong_128b_defaultSecret); - -} - -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void * input, - size_t len, - XXH64_hash_t seed) { - - return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, - sizeof(XXH3_kSecret), - XXH3_hashLong_128b_withSeed); - -} - -XXH_PUBLIC_API XXH128_hash_t XXH128(const void *input, size_t len, - XXH64_hash_t seed) { - - return XXH3_128bits_withSeed(input, len, seed); - -} - -/* === XXH3 128-bit streaming === */ - -/* - * All the functions are actually the same as for 64-bit streaming variant. - * The only difference is the finalizatiom routine. - */ - -static void XXH3_128bits_reset_internal(XXH3_state_t *statePtr, - XXH64_hash_t seed, const xxh_u8 *secret, - size_t secretSize) { - - XXH3_64bits_reset_internal(statePtr, seed, secret, secretSize); - -} - -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t *statePtr) { - - if (statePtr == NULL) return XXH_ERROR; - XXH3_128bits_reset_internal(statePtr, 0, XXH3_kSecret, - XXH_SECRET_DEFAULT_SIZE); - return XXH_OK; - -} - -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret( - XXH3_state_t *statePtr, const void *secret, size_t secretSize) { - - if (statePtr == NULL) return XXH_ERROR; - XXH3_128bits_reset_internal(statePtr, 0, (const xxh_u8 *)secret, secretSize); - if (secret == NULL) return XXH_ERROR; - if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; - return XXH_OK; - -} - -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t *statePtr, - XXH64_hash_t seed) { - - if (statePtr == NULL) return XXH_ERROR; - XXH3_128bits_reset_internal(statePtr, seed, XXH3_kSecret, - XXH_SECRET_DEFAULT_SIZE); - XXH3_initCustomSecret(statePtr->customSecret, seed); - statePtr->extSecret = NULL; - return XXH_OK; - -} - -XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update(XXH3_state_t *state, - const void * input, - size_t len) { - - return XXH3_update(state, (const xxh_u8 *)input, len, XXH3_acc_128bits, - XXH3_accumulate_512, XXH3_scrambleAcc); - -} - -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest(const XXH3_state_t *state) { - - const unsigned char *const secret = - (state->extSecret == NULL) ? state->customSecret : state->extSecret; - if (state->totalLen > XXH3_MIDSIZE_MAX) { - - XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; - XXH3_digest_long(acc, state, secret, XXH3_acc_128bits); - XXH_ASSERT(state->secretLimit + XXH_STRIPE_LEN >= - sizeof(acc) + XXH_SECRET_MERGEACCS_START); - { - - XXH128_hash_t h128; - h128.low64 = XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, - (xxh_u64)state->totalLen * XXH_PRIME64_1); - h128.high64 = - XXH3_mergeAccs(acc, - secret + state->secretLimit + XXH_STRIPE_LEN - - sizeof(acc) - XXH_SECRET_MERGEACCS_START, - ~((xxh_u64)state->totalLen * XXH_PRIME64_2)); - return h128; - - } - - } - - /* len <= XXH3_MIDSIZE_MAX : short code */ - if (state->seed) - return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, - state->seed); - return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen), - secret, state->secretLimit + XXH_STRIPE_LEN); - -} - -/* 128-bit utility functions */ - -#include /* memcmp, memcpy */ - -/* return : 1 is equal, 0 if different */ -XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) { - - /* note : XXH128_hash_t is compact, it has no padding byte */ - return !(memcmp(&h1, &h2, sizeof(h1))); - -} - -/* This prototype is compatible with stdlib's qsort(). - * return : >0 if *h128_1 > *h128_2 - * <0 if *h128_1 < *h128_2 - * =0 if *h128_1 == *h128_2 */ -XXH_PUBLIC_API int XXH128_cmp(const void *h128_1, const void *h128_2) { - - XXH128_hash_t const h1 = *(const XXH128_hash_t *)h128_1; - XXH128_hash_t const h2 = *(const XXH128_hash_t *)h128_2; - int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64); - /* note : bets that, in most cases, hash values are different */ - if (hcmp) return hcmp; - return (h1.low64 > h2.low64) - (h2.low64 > h1.low64); - -} - -/*====== Canonical representation ======*/ -XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t *dst, - XXH128_hash_t hash) { - - XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); - if (XXH_CPU_LITTLE_ENDIAN) { - - hash.high64 = XXH_swap64(hash.high64); - hash.low64 = XXH_swap64(hash.low64); - - } - - memcpy(dst, &hash.high64, sizeof(hash.high64)); - memcpy((char *)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64)); - -} - -XXH_PUBLIC_API XXH128_hash_t -XXH128_hashFromCanonical(const XXH128_canonical_t *src) { - - XXH128_hash_t h; - h.high64 = XXH_readBE64(src); - h.low64 = XXH_readBE64(src->digest + 8); - return h; - -} - -/* Pop our optimization override from above */ -#if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \ - && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ - && defined(__OPTIMIZE__) && \ - !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */ - #pragma GCC pop_options -#endif - -#endif /* XXH3_H_1397135465 */ - diff --git a/include/xxhash.h b/include/xxhash.h index 826f39bd..0472f881 100644 --- a/include/xxhash.h +++ b/include/xxhash.h @@ -197,6 +197,7 @@ extern "C" { #define XXH_CAT(A, B) A##B #define XXH_NAME2(A, B) XXH_CAT(A, B) #define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) + /* XXH32 */ #define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) #define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) #define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) @@ -208,6 +209,7 @@ extern "C" { XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) #define XXH32_hashFromCanonical \ XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) + /* XXH64 */ #define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) #define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) #define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) @@ -219,14 +221,50 @@ extern "C" { XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) #define XXH64_hashFromCanonical \ XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) + /* XXH3_64bits */ + #define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits) + #define XXH3_64bits_withSecret \ + XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret) + #define XXH3_64bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed) + #define XXH3_createState XXH_NAME2(XXH_NAMESPACE, XXH3_createState) + #define XXH3_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_freeState) + #define XXH3_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_copyState) + #define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset) + #define XXH3_64bits_reset_withSeed \ + XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed) + #define XXH3_64bits_reset_withSecret \ + XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret) + #define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update) + #define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest) + #define XXH3_generateSecret XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret) + /* XXH3_128bits */ + #define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128) + #define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits) + #define XXH3_128bits_withSeed \ + XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed) + #define XXH3_128bits_withSecret \ + XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecret) + #define XXH3_128bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset) + #define XXH3_128bits_reset_withSeed \ + XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSeed) + #define XXH3_128bits_reset_withSecret \ + XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecret) + #define XXH3_128bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_update) + #define XXH3_128bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_digest) + #define XXH128_isEqual XXH_NAME2(XXH_NAMESPACE, XXH128_isEqual) + #define XXH128_cmp XXH_NAME2(XXH_NAMESPACE, XXH128_cmp) + #define XXH128_canonicalFromHash \ + XXH_NAME2(XXH_NAMESPACE, XXH128_canonicalFromHash) + #define XXH128_hashFromCanonical \ + XXH_NAME2(XXH_NAMESPACE, XXH128_hashFromCanonical) #endif /* ************************************* * Version ***************************************/ #define XXH_VERSION_MAJOR 0 - #define XXH_VERSION_MINOR 7 - #define XXH_VERSION_RELEASE 4 + #define XXH_VERSION_MINOR 8 + #define XXH_VERSION_RELEASE 0 #define XXH_VERSION_NUMBER \ (XXH_VERSION_MAJOR * 100 * 100 + XXH_VERSION_MINOR * 100 + \ XXH_VERSION_RELEASE) @@ -401,145 +439,56 @@ XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t *dst, XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t *src); - #endif /* XXH_NO_LONG_LONG */ - -#endif /* XXHASH_H_5627135585666179 */ - -#if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) - #define XXHASH_H_STATIC_13879238742 -/* **************************************************************************** - * This section contains declarations which are not guaranteed to remain stable. - * They may change in future versions, becoming incompatible with a different - * version of the library. - * These declarations should only be used with static linking. - * Never use them in association with dynamic linking! - ***************************************************************************** - */ +/*-********************************************************************** + * XXH3 64-bit variant + ************************************************************************/ -/* - * These definitions are only present to allow static allocation of an XXH - * state, for example, on the stack or in a struct. - * Never **ever** access members directly. +/* ************************************************************************ + * XXH3 is a new hash algorithm featuring: + * - Improved speed for both small and large inputs + * - True 64-bit and 128-bit outputs + * - SIMD acceleration + * - Improved 32-bit viability + * + * Speed analysis methodology is explained here: + * + * https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html + * + * In general, expect XXH3 to run about ~2x faster on large inputs and >3x + * faster on small ones compared to XXH64, though exact differences depend on + * the platform. + * + * The algorithm is portable: Like XXH32 and XXH64, it generates the same hash + * on all platforms. + * + * It benefits greatly from SIMD and 64-bit arithmetic, but does not require it. + * + * Almost all 32-bit and 64-bit targets that can run XXH32 smoothly can run + * XXH3 at competitive speeds, even if XXH64 runs slowly. Further details are + * explained in the implementation. + * + * Optimized implementations are provided for AVX512, AVX2, SSE2, NEON, POWER8, + * ZVector and scalar targets. This can be controlled with the XXH_VECTOR macro. + * + * XXH3 offers 2 variants, _64bits and _128bits. + * When only 64 bits are needed, prefer calling the _64bits variant, as it + * reduces the amount of mixing, resulting in faster speed on small inputs. + * + * It's also generally simpler to manipulate a scalar return type than a struct. + * + * The 128-bit version adds additional strength, but it is slightly slower. + * + * Return values of XXH3 and XXH128 are officially finalized starting + * with v0.8.0 and will no longer change in future versions. + * Avoid storing values from before that release in long-term storage. + * + * Results produced by v0.7.x are not comparable with results from v0.7.y. + * However, the API is completely stable, and it can safely be used for + * ephemeral data (local sessions). + * + * The API supports one-shot hashing, streaming mode, and custom secrets. */ -struct XXH32_state_s { - - XXH32_hash_t total_len_32; - XXH32_hash_t large_len; - XXH32_hash_t v1; - XXH32_hash_t v2; - XXH32_hash_t v3; - XXH32_hash_t v4; - XXH32_hash_t mem32[4]; - XXH32_hash_t memsize; - XXH32_hash_t - reserved; /* never read nor write, might be removed in a future version */ - -}; /* typedef'd to XXH32_state_t */ - - #ifndef XXH_NO_LONG_LONG /* defined when there is no 64-bit support */ - -struct XXH64_state_s { - - XXH64_hash_t total_len; - XXH64_hash_t v1; - XXH64_hash_t v2; - XXH64_hash_t v3; - XXH64_hash_t v4; - XXH64_hash_t mem64[4]; - XXH32_hash_t memsize; - XXH32_hash_t reserved32; /* required for padding anyway */ - XXH64_hash_t reserved64; /* never read nor write, might be removed in a future - version */ - -}; /* typedef'd to XXH64_state_t */ - - /*-********************************************************************** - * XXH3 - * New experimental hash - ************************************************************************/ - - /* ************************************************************************ - * XXH3 is a new hash algorithm featuring: - * - Improved speed for both small and large inputs - * - True 64-bit and 128-bit outputs - * - SIMD acceleration - * - Improved 32-bit viability - * - * Speed analysis methodology is explained here: - * - * https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html - * - * In general, expect XXH3 to run about ~2x faster on large inputs and >3x - * faster on small ones compared to XXH64, though exact differences depend on - * the platform. - * - * The algorithm is portable: Like XXH32 and XXH64, it generates the same hash - * on all platforms. - * - * It benefits greatly from SIMD and 64-bit arithmetic, but does not require - * it. - * - * Almost all 32-bit and 64-bit targets that can run XXH32 smoothly can run - * XXH3 at competitive speeds, even if XXH64 runs slowly. Further details are - * explained in the implementation. - * - * Optimized implementations are provided for AVX512, AVX2, SSE2, NEON, - * POWER8, ZVector and scalar targets. This can be controlled with the - * XXH_VECTOR macro. - * - * XXH3 offers 2 variants, _64bits and _128bits. - * When only 64 bits are needed, prefer calling the _64bits variant, as it - * reduces the amount of mixing, resulting in faster speed on small inputs. - * - * It's also generally simpler to manipulate a scalar return type than a - * struct. - * - * The 128-bit version adds additional strength, but it is slightly slower. - * - * The XXH3 algorithm is still in development. - * The results it produces may still change in future versions. - * - * Results produced by v0.7.x are not comparable with results from v0.7.y. - * However, the API is completely stable, and it can safely be used for - * ephemeral data (local sessions). - * - * Avoid storing values in long-term storage until the algorithm is finalized. - * - * Since v0.7.3, XXH3 has reached "release candidate" status, meaning that, if - * everything remains fine, its current format will be "frozen" and become the - * final one. - * - * After which, return values of XXH3 and XXH128 will no longer change in - * future versions. - * - * XXH3's return values will be officially finalized upon reaching v0.8.0. - * - * The API supports one-shot hashing, streaming mode, and custom secrets. - */ - - #ifdef XXH_NAMESPACE - #define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits) - #define XXH3_64bits_withSecret \ - XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret) - #define XXH3_64bits_withSeed \ - XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed) - - #define XXH3_createState XXH_NAME2(XXH_NAMESPACE, XXH3_createState) - #define XXH3_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_freeState) - #define XXH3_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_copyState) - - #define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset) - #define XXH3_64bits_reset_withSeed \ - XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed) - #define XXH3_64bits_reset_withSecret \ - XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret) - #define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update) - #define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest) - - #define XXH3_generateSecret XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret) - #endif - /* XXH3_64bits(): * default 64-bit variant, using default secret and default seed of 0. * It's the fastest variant. */ @@ -547,8 +496,8 @@ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void *data, size_t len); /* * XXH3_64bits_withSeed(): - * This variant generates a custom secret on the fly based on the default - * secret, altered using the `seed` value. + * This variant generates a custom secret on the fly + * based on default secret altered using the `seed` value. * While this operation is decently fast, note that it's not completely free. * Note: seed==0 produces the same results as XXH3_64bits(). */ @@ -559,74 +508,28 @@ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void *data, size_t len, * XXH3_64bits_withSecret(): * It's possible to provide any blob of bytes as a "secret" to generate the * hash. This makes it more difficult for an external actor to prepare an - * intentional collision. secretSize *must* be large enough (>= - * XXH3_SECRET_SIZE_MIN). The hash quality depends on the secret's high - * entropy, meaning that the secret should look like a bunch of random - * bytes. Avoid "trivial" sequences such as text or a bunch of repeated - * characters. If you are unsure of the "randonmess" of the blob of bytes, - * consider making it a "custom seed" instead, - * and use "XXH_generateSecret()" to generate a high quality secret. + * intentional collision. The main condition is that secretSize *must* be + * large enough (>= XXH3_SECRET_SIZE_MIN). However, the quality of produced + * hash values depends on secret's entropy. Technically, the secret must + * look like a bunch of random bytes. Avoid "trivial" or structured data + * such as repeated sequences or a text document. Whenever unsure about the + * "randomness" of the blob of bytes, consider relabelling it as a "custom + * seed" instead, and employ "XXH3_generateSecret()" (see below) to generate + * a high entropy secret derived from the custom seed. */ #define XXH3_SECRET_SIZE_MIN 136 XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void *data, size_t len, const void *secret, size_t secretSize); - /* streaming 64-bit */ - - #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11+ */ - #include - #define XXH_ALIGN(n) alignas(n) - #elif defined(__GNUC__) - #define XXH_ALIGN(n) __attribute__((aligned(n))) - #elif defined(_MSC_VER) - #define XXH_ALIGN(n) __declspec(align(n)) - #else - #define XXH_ALIGN(n) /* disabled */ - #endif - - /* Old GCC versions only accept the attribute after the type in structures. - */ - #if !(defined(__STDC_VERSION__) && \ - (__STDC_VERSION__ >= 201112L)) /* C11+ */ \ - && defined(__GNUC__) - #define XXH_ALIGN_MEMBER(align, type) type XXH_ALIGN(align) - #else - #define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type - #endif - -typedef struct XXH3_state_s XXH3_state_t; - - #define XXH3_INTERNALBUFFER_SIZE 256 - #define XXH3_SECRET_DEFAULT_SIZE 192 -struct XXH3_state_s { - - XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]); - /* used to store a custom secret generated from a seed */ - XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]); - XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]); - XXH32_hash_t bufferedSize; - XXH32_hash_t reserved32; - size_t nbStripesPerBlock; - size_t nbStripesSoFar; - size_t secretLimit; - XXH64_hash_t totalLen; - XXH64_hash_t seed; - XXH64_hash_t reserved64; - const unsigned char *extSecret; /* reference to external secret; - * if == NULL, use .customSecret instead */ - /* note: there may be some padding at the end due to alignment on 64 bytes */ - -}; /* typedef'd to XXH3_state_t */ - - #undef XXH_ALIGN_MEMBER - +/******* Streaming *******/ /* * Streaming requires state maintenance. * This operation costs memory and CPU. * As a consequence, streaming is slower than one-shot hashing. - * For better performance, prefer one-shot functions whenever possible. + * For better performance, prefer one-shot functions whenever applicable. */ +typedef struct XXH3_state_s XXH3_state_t; XXH_PUBLIC_API XXH3_state_t *XXH3_createState(void); XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t *statePtr); XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t * dst_state, @@ -634,8 +537,8 @@ XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t * dst_state, /* * XXH3_64bits_reset(): - * Initialize with the default parameters. - * The result will be equivalent to `XXH3_64bits()`. + * Initialize with default parameters. + * digest will be equivalent to `XXH3_64bits()`. */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t *statePtr); /* @@ -647,9 +550,12 @@ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t *statePtr, XXH64_hash_t seed); /* * XXH3_64bits_reset_withSecret(): - * `secret` is referenced, and must outlive the hash streaming session, so - * be careful when using stack arrays. - * `secretSize` must be >= `XXH3_SECRET_SIZE_MIN`. + * `secret` is referenced, it _must outlive_ the hash streaming session. + * Similar to one-shot API, `secretSize` must be >= `XXH3_SECRET_SIZE_MIN`, + * and the quality of produced hash values depends on secret's entropy + * (secret's content should look like a bunch of random bytes). + * When in doubt about the randomness of a candidate `secret`, + * consider employing `XXH3_generateSecret()` instead (see below). */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret( XXH3_state_t *statePtr, const void *secret, size_t secretSize); @@ -659,31 +565,12 @@ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update(XXH3_state_t *statePtr, size_t length); XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest(const XXH3_state_t *statePtr); - /* 128-bit */ - - #ifdef XXH_NAMESPACE - #define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128) - #define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits) - #define XXH3_128bits_withSeed \ - XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed) - #define XXH3_128bits_withSecret \ - XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecret) - - #define XXH3_128bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset) - #define XXH3_128bits_reset_withSeed \ - XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSeed) - #define XXH3_128bits_reset_withSecret \ - XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecret) - #define XXH3_128bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_update) - #define XXH3_128bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_digest) - - #define XXH128_isEqual XXH_NAME2(XXH_NAMESPACE, XXH128_isEqual) - #define XXH128_cmp XXH_NAME2(XXH_NAMESPACE, XXH128_cmp) - #define XXH128_canonicalFromHash \ - XXH_NAME2(XXH_NAMESPACE, XXH128_canonicalFromHash) - #define XXH128_hashFromCanonical \ - XXH_NAME2(XXH_NAMESPACE, XXH128_hashFromCanonical) - #endif +/* note : canonical representation of XXH3 is the same as XXH64 + * since they both produce XXH64_hash_t values */ + +/*-********************************************************************** + * XXH3 128-bit variant + ************************************************************************/ typedef struct { @@ -692,16 +579,28 @@ typedef struct { } XXH128_hash_t; -XXH_PUBLIC_API XXH128_hash_t XXH128(const void *data, size_t len, - XXH64_hash_t seed); XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void *data, size_t len); -XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed( - const void *data, size_t len, XXH64_hash_t seed); /* == XXH128() */ +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void *data, size_t len, + XXH64_hash_t seed); XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void *data, size_t len, const void *secret, size_t secretSize); +/******* Streaming *******/ +/* + * Streaming requires state maintenance. + * This operation costs memory and CPU. + * As a consequence, streaming is slower than one-shot hashing. + * For better performance, prefer one-shot functions whenever applicable. + * + * XXH3_128bits uses the same XXH3_state_t as XXH3_64bits(). + * Use already declared XXH3_createState() and XXH3_freeState(). + * + * All reset and streaming functions have same meaning as their 64-bit + * counterpart. + */ + XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t *statePtr); XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t *statePtr, XXH64_hash_t seed); @@ -713,7 +612,10 @@ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update(XXH3_state_t *statePtr, size_t length); XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest(const XXH3_state_t *statePtr); -/* Note: For better performance, these functions can be inlined using +/* Following helper functions make it possible to compare XXH128_hast_t values. + * Since XXH128_hash_t is a structure, this capability is not offered by the + * language. + * Note: For better performance, these functions can be inlined using * XXH_INLINE_ALL */ /*! @@ -745,6 +647,116 @@ XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t *dst, XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t *src); + #endif /* XXH_NO_LONG_LONG */ + +#endif /* XXHASH_H_5627135585666179 */ + +#if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) + #define XXHASH_H_STATIC_13879238742 +/* **************************************************************************** + * This section contains declarations which are not guaranteed to remain stable. + * They may change in future versions, becoming incompatible with a different + * version of the library. + * These declarations should only be used with static linking. + * Never use them in association with dynamic linking! + ***************************************************************************** +*/ + +/* + * These definitions are only present to allow static allocation + * of XXH states, on stack or in a struct, for example. + * Never **ever** access their members directly. + */ + +struct XXH32_state_s { + + XXH32_hash_t total_len_32; + XXH32_hash_t large_len; + XXH32_hash_t v1; + XXH32_hash_t v2; + XXH32_hash_t v3; + XXH32_hash_t v4; + XXH32_hash_t mem32[4]; + XXH32_hash_t memsize; + XXH32_hash_t + reserved; /* never read nor write, might be removed in a future version */ + +}; /* typedef'd to XXH32_state_t */ + + #ifndef XXH_NO_LONG_LONG /* defined when there is no 64-bit support */ + +struct XXH64_state_s { + + XXH64_hash_t total_len; + XXH64_hash_t v1; + XXH64_hash_t v2; + XXH64_hash_t v3; + XXH64_hash_t v4; + XXH64_hash_t mem64[4]; + XXH32_hash_t memsize; + XXH32_hash_t reserved32; /* required for padding anyway */ + XXH64_hash_t reserved64; /* never read nor write, might be removed in a future + version */ + +}; /* typedef'd to XXH64_state_t */ + + #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11+ */ + #include + #define XXH_ALIGN(n) alignas(n) + #elif defined(__GNUC__) + #define XXH_ALIGN(n) __attribute__((aligned(n))) + #elif defined(_MSC_VER) + #define XXH_ALIGN(n) __declspec(align(n)) + #else + #define XXH_ALIGN(n) /* disabled */ + #endif + + /* Old GCC versions only accept the attribute after the type in structures. + */ + #if !(defined(__STDC_VERSION__) && \ + (__STDC_VERSION__ >= 201112L)) /* C11+ */ \ + && defined(__GNUC__) + #define XXH_ALIGN_MEMBER(align, type) type XXH_ALIGN(align) + #else + #define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type + #endif + + #define XXH3_INTERNALBUFFER_SIZE 256 + #define XXH3_SECRET_DEFAULT_SIZE 192 +struct XXH3_state_s { + + XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]); + /* used to store a custom secret generated from a seed */ + XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]); + XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]); + XXH32_hash_t bufferedSize; + XXH32_hash_t reserved32; + size_t nbStripesSoFar; + XXH64_hash_t totalLen; + size_t nbStripesPerBlock; + size_t secretLimit; + XXH64_hash_t seed; + XXH64_hash_t reserved64; + const unsigned char *extSecret; /* reference to external secret; + * if == NULL, use .customSecret instead */ + /* note: there may be some padding at the end due to alignment on 64 bytes */ + +}; /* typedef'd to XXH3_state_t */ + + #undef XXH_ALIGN_MEMBER + + /* When the XXH3_state_t structure is merely emplaced on stack, + * it should be initialized with XXH3_INITSTATE() or a memset() + * in case its first reset uses XXH3_NNbits_reset_withSeed(). + * This init can be omitted if the first reset uses default or _withSecret + * mode. This operation isn't necessary when the state is created with + * XXH3_createState(). Note that this doesn't prepare the state for a + * streaming operation, it's still necessary to use XXH3_NNbits_reset*() + * afterwards. + */ + #define XXH3_INITSTATE(XXH3_state_ptr) \ + { (XXH3_state_ptr)->seed = 0; } + /* === Experimental API === */ /* Symbols defined below must be considered tied to a specific library version. */ @@ -752,17 +764,19 @@ XXH128_hashFromCanonical(const XXH128_canonical_t *src); /* * XXH3_generateSecret(): * - * Derive a secret for use with `*_withSecret()` prototypes of XXH3. - * Use this if you need a higher level of security than the one provided by - * 64bit seed. + * Derive a high-entropy secret from any user-defined content, named customSeed. + * The generated secret can be used in combination with `*_withSecret()` + * functions. The `_withSecret()` variants are useful to provide a higher level + * of protection than 64-bit seed, as it becomes much more difficult for an + * external actor to guess how to impact the calculation logic. * - * Take as input a custom seed of any length and any content, - * generate from it a high-entropy secret of length XXH3_SECRET_DEFAULT_SIZE - * into already allocated buffer secretBuffer. - * The generated secret ALWAYS is XXH_SECRET_DEFAULT_SIZE bytes long. + * The function accepts as input a custom seed of any length and any content, + * and derives from it a high-entropy secret of length XXH3_SECRET_DEFAULT_SIZE + * into an already allocated buffer secretBuffer. + * The generated secret is _always_ XXH_SECRET_DEFAULT_SIZE bytes long. * * The generated secret can then be used with any `*_withSecret()` variant. - * The functions `XXH3_128bits_withSecret()`, `XXH3_64bits_withSecret()`, + * Functions `XXH3_128bits_withSecret()`, `XXH3_64bits_withSecret()`, * `XXH3_128bits_reset_withSecret()` and `XXH3_64bits_reset_withSecret()` * are part of this list. They all accept a `secret` parameter * which must be very long for implementation reasons (>= XXH3_SECRET_SIZE_MIN) @@ -771,8 +785,8 @@ XXH128_hashFromCanonical(const XXH128_canonical_t *src); * this function can be used to generate a secret of proper quality. * * customSeed can be anything. It can have any size, even small ones, - * and its content can be anything, even some "low entropy" source such as a - * bunch of zeroes. The resulting `secret` will nonetheless respect all expected + * and its content can be anything, even stupidly "low entropy" source such as a + * bunch of zeroes. The resulting `secret` will nonetheless provide all expected * qualities. * * Supplying NULL as the customSeed copies the default secret into @@ -783,6 +797,10 @@ XXH_PUBLIC_API void XXH3_generateSecret(void * secretBuffer, const void *customSeed, size_t customSeedSize); +/* simple short-cut to pre-selected XXH3_128bits variant */ +XXH_PUBLIC_API XXH128_hash_t XXH128(const void *data, size_t len, + XXH64_hash_t seed); + #endif /* XXH_NO_LONG_LONG */ #if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) @@ -799,17 +817,23 @@ XXH_PUBLIC_API void XXH3_generateSecret(void * secretBuffer, /*-********************************************************************** * xxHash implementation *-********************************************************************** - * xxHash's implementation used to be found in xxhash.c. + * xxHash's implementation used to be hosted inside xxhash.c. * - * However, code inlining requires the implementation to be visible to the - * compiler, usually within the header. + * However, inlining requires implementation to be visible to the compiler, + * hence be included alongside the header. + * Previously, implementation was hosted inside xxhash.c, + * which was then #included when inlining was activated. + * This construction created issues with a few build and install systems, + * as it required xxhash.c to be stored in /include directory. * - * As a workaround, xxhash.c used to be included within xxhash.h. This caused - * some issues with some build systems, especially ones which treat .c files - * as source files. + * xxHash implementation is now directly integrated within xxhash.h. + * As a consequence, xxhash.c is no longer needed in /include. * - * Therefore, the implementation is now directly integrated within xxhash.h. - * Another small advantage is that xxhash.c is no longer needed in /include. + * xxhash.c is still available and is still useful. + * In a "normal" setup, when xxhash is not inlined, + * xxhash.h only exposes the prototypes and public symbols, + * while xxhash.c can be built into an object file xxhash.o + * which can then be linked into the final binary. ************************************************************************/ #if (defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) || \ @@ -828,10 +852,10 @@ XXH_PUBLIC_API void XXH3_generateSecret(void * secretBuffer, * Unfortunately, on some target/compiler combinations, the generated assembly * is sub-optimal. * - * The below switch allow to select a different access method for improved - * performance. + * The below switch allow selection of a different access method + * in the search for improved performance. * Method 0 (default): - * Use `memcpy()`. Safe and portable. + * Use `memcpy()`. Safe and portable. Default. * Method 1: * `__attribute__((packed))` statement. It depends on compiler extensions * and is therefore not portable. @@ -843,7 +867,7 @@ XXH_PUBLIC_API void XXH3_generateSecret(void * secretBuffer, * It can generate buggy code on targets which do not support unaligned * memory accesses. * But in some circumstances, it's the only known way to get the most - * performance (ie GCC + ARMv6) + * performance (example: GCC + ARMv6) * Method 3: * Byteshift. This can generate the best code on old compilers which don't * inline small `memcpy()` calls, and it might also be faster on @@ -924,7 +948,8 @@ XXH_PUBLIC_API void XXH3_generateSecret(void * secretBuffer, * -fno-inline with GCC or Clang, this will automatically be defined. */ #ifndef XXH_NO_INLINE_HINTS - #if defined(__OPTIMIZE_SIZE__) || defined(__NO_INLINE__) + #if defined(__OPTIMIZE_SIZE__) /* -Os, -Oz */ \ + || defined(__NO_INLINE__) /* -O0, -fno-inline */ #define XXH_NO_INLINE_HINTS 1 #else #define XXH_NO_INLINE_HINTS 0 @@ -950,8 +975,8 @@ XXH_PUBLIC_API void XXH3_generateSecret(void * secretBuffer, * Includes & Memory related functions ***************************************/ /*! - * Modify the local functions below should you wish to use some other memory - * routines for malloc() and free() + * Modify the local functions below should you wish to use + * different memory routines for malloc() and free() */ #include @@ -1137,7 +1162,8 @@ typedef enum { XXH_bigEndian = 0, XXH_littleEndian = 1 } XXH_endianess; * Try to detect endianness automatically, to avoid the nonstandard behavior * in `XXH_isLittleEndian()` */ - #if defined(_WIN32) || defined(__LITTLE_ENDIAN__) || \ + #if defined(_WIN32) /* Windows is always little endian */ \ + || defined(__LITTLE_ENDIAN__) || \ (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) #define XXH_CPU_LITTLE_ENDIAN 1 #elif defined(__BIG_ENDIAN__) || \ @@ -1163,7 +1189,7 @@ static int XXH_isLittleEndian(void) { return one.c[0]; } - +\ #define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian() #endif #endif @@ -1371,9 +1397,7 @@ static xxh_u32 XXH32_avalanche(xxh_u32 h32) { static xxh_u32 XXH32_finalize(xxh_u32 h32, const xxh_u8 *ptr, size_t len, XXH_alignment align) { - - /* dummy comment */ - +\ #define XXH_PROCESS1 \ do { \ \ @@ -1778,13 +1802,16 @@ typedef XXH64_hash_t xxh_u64; * rerolled. */ #ifndef XXH_REROLL_XXH64 - #if (defined(__ILP32__) || defined(_ILP32)) || \ - !(defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) || \ - defined(_M_ARM64) || defined(__aarch64__) || defined(__arm64__) || \ - defined(__PPC64__) || defined(__PPC64LE__) || \ - defined(__ppc64__) || defined(__powerpc64__) || \ - defined(__mips64__) || defined(__mips64)) || \ - (!defined(SIZE_MAX) || SIZE_MAX < ULLONG_MAX) + #if (defined(__ILP32__) || \ + defined(_ILP32)) /* ILP32 is often defined on 32-bit GCC family */ \ + || !(defined(__x86_64__) || defined(_M_X64) || \ + defined(_M_AMD64) /* x86-64 */ \ + || defined(_M_ARM64) || defined(__aarch64__) || \ + defined(__arm64__) /* aarch64 */ \ + || defined(__PPC64__) || defined(__PPC64LE__) || \ + defined(__ppc64__) || defined(__powerpc64__) /* ppc64 */ \ + || defined(__mips64__) || defined(__mips64)) /* mips64 */ \ + || (!defined(SIZE_MAX) || SIZE_MAX < ULLONG_MAX) /* check limits */ #define XXH_REROLL_XXH64 1 #else #define XXH_REROLL_XXH64 0 @@ -1923,21 +1950,16 @@ XXH_FORCE_INLINE xxh_u64 XXH_readLE64_align(const void * ptr, /******* xxh64 *******/ -static const xxh_u64 XXH_PRIME64_1 = - 0x9E3779B185EBCA87ULL; /* 0b1001111000110111011110011011000110000101111010111100101010000111 - */ -static const xxh_u64 XXH_PRIME64_2 = - 0xC2B2AE3D27D4EB4FULL; /* 0b1100001010110010101011100011110100100111110101001110101101001111 - */ -static const xxh_u64 XXH_PRIME64_3 = - 0x165667B19E3779F9ULL; /* 0b0001011001010110011001111011000110011110001101110111100111111001 - */ -static const xxh_u64 XXH_PRIME64_4 = - 0x85EBCA77C2B2AE63ULL; /* 0b1000010111101011110010100111011111000010101100101010111001100011 - */ -static const xxh_u64 XXH_PRIME64_5 = - 0x27D4EB2F165667C5ULL; /* 0b0010011111010100111010110010111100010110010101100110011111000101 - */ +static const xxh_u64 XXH_PRIME64_1 = 0x9E3779B185EBCA87ULL; /* 0b1001111000110111011110011011000110000101111010111100101010000111 + */ +static const xxh_u64 XXH_PRIME64_2 = 0xC2B2AE3D27D4EB4FULL; /* 0b1100001010110010101011100011110100100111110101001110101101001111 + */ +static const xxh_u64 XXH_PRIME64_3 = 0x165667B19E3779F9ULL; /* 0b0001011001010110011001111011000110011110001101110111100111111001 + */ +static const xxh_u64 XXH_PRIME64_4 = 0x85EBCA77C2B2AE63ULL; /* 0b1000010111101011110010100111011111000010101100101010111001100011 + */ +static const xxh_u64 XXH_PRIME64_5 = 0x27D4EB2F165667C5ULL; /* 0b0010011111010100111010110010111100010110010101100110011111000101 + */ #ifdef XXH_OLD_NAMES #define PRIME64_1 XXH_PRIME64_1 @@ -1980,9 +2002,7 @@ static xxh_u64 XXH64_avalanche(xxh_u64 h64) { static xxh_u64 XXH64_finalize(xxh_u64 h64, const xxh_u8 *ptr, size_t len, XXH_alignment align) { - - /* dummy comment */ - +\ #define XXH_PROCESS1_64 \ do { \ \ @@ -2428,7 +2448,3132 @@ XXH64_hashFromCanonical(const XXH64_canonical_t *src) { * New generation hash designed for speed on small keys and vectorization ************************************************************************ */ - #include "xxh3.h" + /* === Compiler specifics === */ + + #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */ + #define XXH_RESTRICT restrict + #else + /* Note: it might be useful to define __restrict or __restrict__ for some + * C++ compilers */ + #define XXH_RESTRICT /* disable */ + #endif + + #if (defined(__GNUC__) && (__GNUC__ >= 3)) || \ + (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || \ + defined(__clang__) + #define XXH_likely(x) __builtin_expect(x, 1) + #define XXH_unlikely(x) __builtin_expect(x, 0) + #else + #define XXH_likely(x) (x) + #define XXH_unlikely(x) (x) + #endif + + #if defined(__GNUC__) + #if defined(__AVX2__) + #include + #elif defined(__SSE2__) + #include + #elif defined(__ARM_NEON__) || defined(__ARM_NEON) + #define inline __inline__ /* circumvent a clang bug */ + #include + #undef inline + #endif + #elif defined(_MSC_VER) + #include + #endif + + /* + * One goal of XXH3 is to make it fast on both 32-bit and 64-bit, while + * remaining a true 64-bit/128-bit hash function. + * + * This is done by prioritizing a subset of 64-bit operations that can be + * emulated without too many steps on the average 32-bit machine. + * + * For example, these two lines seem similar, and run equally fast on + * 64-bit: + * + * xxh_u64 x; + * x ^= (x >> 47); // good + * x ^= (x >> 13); // bad + * + * However, to a 32-bit machine, there is a major difference. + * + * x ^= (x >> 47) looks like this: + * + * x.lo ^= (x.hi >> (47 - 32)); + * + * while x ^= (x >> 13) looks like this: + * + * // note: funnel shifts are not usually cheap. + * x.lo ^= (x.lo >> 13) | (x.hi << (32 - 13)); + * x.hi ^= (x.hi >> 13); + * + * The first one is significantly faster than the second, simply because the + * shift is larger than 32. This means: + * - All the bits we need are in the upper 32 bits, so we can ignore the + * lower 32 bits in the shift. + * - The shift result will always fit in the lower 32 bits, and therefore, + * we can ignore the upper 32 bits in the xor. + * + * Thanks to this optimization, XXH3 only requires these features to be + * efficient: + * + * - Usable unaligned access + * - A 32-bit or 64-bit ALU + * - If 32-bit, a decent ADC instruction + * - A 32 or 64-bit multiply with a 64-bit result + * - For the 128-bit variant, a decent byteswap helps short inputs. + * + * The first two are already required by XXH32, and almost all 32-bit and + * 64-bit platforms which can run XXH32 can run XXH3 efficiently. + * + * Thumb-1, the classic 16-bit only subset of ARM's instruction set, is one + * notable exception. + * + * First of all, Thumb-1 lacks support for the UMULL instruction which + * performs the important long multiply. This means numerous __aeabi_lmul + * calls. + * + * Second of all, the 8 functional registers are just not enough. + * Setup for __aeabi_lmul, byteshift loads, pointers, and all arithmetic + * need Lo registers, and this shuffling results in thousands more MOVs than + * A32. + * + * A32 and T32 don't have this limitation. They can access all 14 registers, + * do a 32->64 multiply with UMULL, and the flexible operand allowing free + * shifts is helpful, too. + * + * Therefore, we do a quick sanity check. + * + * If compiling Thumb-1 for a target which supports ARM instructions, we + * will emit a warning, as it is not a "sane" platform to compile for. + * + * Usually, if this happens, it is because of an accident and you probably + * need to specify -march, as you likely meant to compile for a newer + * architecture. + * + * Credit: large sections of the vectorial and asm source code paths + * have been contributed by @easyaspi314 + */ + #if defined(__thumb__) && !defined(__thumb2__) && \ + defined(__ARM_ARCH_ISA_ARM) + #warning "XXH3 is highly inefficient without ARM or Thumb-2." + #endif + + /* ========================================== + * Vectorization detection + * ========================================== */ + #define XXH_SCALAR 0 /* Portable scalar version */ + #define XXH_SSE2 1 /* SSE2 for Pentium 4 and all x86_64 */ + #define XXH_AVX2 2 /* AVX2 for Haswell and Bulldozer */ + #define XXH_AVX512 3 /* AVX512 for Skylake and Icelake */ + #define XXH_NEON 4 /* NEON for most ARMv7-A and all AArch64 */ + #define XXH_VSX 5 /* VSX and ZVector for POWER8/z13 */ + + #ifndef XXH_VECTOR /* can be defined on command line */ + #if defined(__AVX512F__) + #define XXH_VECTOR XXH_AVX512 + #elif defined(__AVX2__) + #define XXH_VECTOR XXH_AVX2 + #elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && (_M_IX86_FP == 2)) + #define XXH_VECTOR XXH_SSE2 + #elif defined(__GNUC__) /* msvc support maybe later */ \ + && (defined(__ARM_NEON__) || defined(__ARM_NEON)) && \ + (defined(__LITTLE_ENDIAN__) /* We only support little endian NEON */ \ + || (defined(__BYTE_ORDER__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + #define XXH_VECTOR XXH_NEON + #elif (defined(__PPC64__) && defined(__POWER8_VECTOR__)) || \ + (defined(__s390x__) && defined(__VEC__)) && \ + defined(__GNUC__) /* TODO: IBM XL */ + #define XXH_VECTOR XXH_VSX + #else + #define XXH_VECTOR XXH_SCALAR + #endif + #endif + + /* + * Controls the alignment of the accumulator, + * for compatibility with aligned vector loads, which are usually faster. + */ + #ifndef XXH_ACC_ALIGN + #if defined(XXH_X86DISPATCH) + #define XXH_ACC_ALIGN 64 /* for compatibility with avx512 */ + #elif XXH_VECTOR == XXH_SCALAR /* scalar */ + #define XXH_ACC_ALIGN 8 + #elif XXH_VECTOR == XXH_SSE2 /* sse2 */ + #define XXH_ACC_ALIGN 16 + #elif XXH_VECTOR == XXH_AVX2 /* avx2 */ + #define XXH_ACC_ALIGN 32 + #elif XXH_VECTOR == XXH_NEON /* neon */ + #define XXH_ACC_ALIGN 16 + #elif XXH_VECTOR == XXH_VSX /* vsx */ + #define XXH_ACC_ALIGN 16 + #elif XXH_VECTOR == XXH_AVX512 /* avx512 */ + #define XXH_ACC_ALIGN 64 + #endif + #endif + + #if defined(XXH_X86DISPATCH) || XXH_VECTOR == XXH_SSE2 || \ + XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512 + #define XXH_SEC_ALIGN XXH_ACC_ALIGN + #else + #define XXH_SEC_ALIGN 8 + #endif + + /* + * UGLY HACK: + * GCC usually generates the best code with -O3 for xxHash. + * + * However, when targeting AVX2, it is overzealous in its unrolling + * resulting in code roughly 3/4 the speed of Clang. + * + * There are other issues, such as GCC splitting _mm256_loadu_si256 into + * _mm_loadu_si128 + _mm256_inserti128_si256. This is an optimization which + * only applies to Sandy and Ivy Bridge... which don't even support AVX2. + * + * That is why when compiling the AVX2 version, it is recommended to use + * either -O2 -mavx2 -march=haswell or -O2 -mavx2 + * -mno-avx256-split-unaligned-load for decent performance, or to use Clang + * instead. + * + * Fortunately, we can control the first one with a pragma that forces GCC + * into -O2, but the other one we can't control without "failed to inline + * always inline function due to target mismatch" warnings. + */ + #if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \ + && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ + && defined(__OPTIMIZE__) && \ + !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */ + #pragma GCC push_options + #pragma GCC optimize("-O2") + #endif + + #if XXH_VECTOR == XXH_NEON + /* + * NEON's setup for vmlal_u32 is a little more complicated than it is on + * SSE2, AVX2, and VSX. + * + * While PMULUDQ and VMULEUW both perform a mask, VMLAL.U32 performs an + * upcast. + * + * To do the same operation, the 128-bit 'Q' register needs to be split + * into two 64-bit 'D' registers, performing this operation:: + * + * [ a | b ] | + * '---------. .--------' | | x | + * | .---------' '--------. | + * [ a & 0xFFFFFFFF | b & 0xFFFFFFFF ],[ a >> 32 | b >> 32 ] + * + * Due to significant changes in aarch64, the fastest method for aarch64 + * is completely different than the fastest method for ARMv7-A. + * + * ARMv7-A treats D registers as unions overlaying Q registers, so + * modifying D11 will modify the high half of Q5. This is similar to how + * modifying AH will only affect bits 8-15 of AX on x86. + * + * VZIP takes two registers, and puts even lanes in one register and odd + * lanes in the other. + * + * On ARMv7-A, this strangely modifies both parameters in place instead of + * taking the usual 3-operand form. + * + * Therefore, if we want to do this, we can simply use a D-form VZIP.32 on + * the lower and upper halves of the Q register to end up with the high + * and low halves where we want - all in one instruction. + * + * vzip.32 d10, d11 @ d10 = { d10[0], d11[0] }; d11 = { d10[1], + * d11[1] } + * + * Unfortunately we need inline assembly for this: Instructions modifying + * two registers at once is not possible in GCC or Clang's IR, and they + * have to create a copy. + * + * aarch64 requires a different approach. + * + * In order to make it easier to write a decent compiler for aarch64, many + * quirks were removed, such as conditional execution. + * + * NEON was also affected by this. + * + * aarch64 cannot access the high bits of a Q-form register, and writes to + * a D-form register zero the high bits, similar to how writes to W-form + * scalar registers (or DWORD registers on x86_64) work. + * + * The formerly free vget_high intrinsics now require a vext (with a few + * exceptions) + * + * Additionally, VZIP was replaced by ZIP1 and ZIP2, which are the + * equivalent of PUNPCKL* and PUNPCKH* in SSE, respectively, in order to + * only modify one operand. + * + * The equivalent of the VZIP.32 on the lower and upper halves would be + * this mess: + * + * ext v2.4s, v0.4s, v0.4s, #2 // v2 = { v0[2], v0[3], v0[0], v0[1] + * } zip1 v1.2s, v0.2s, v2.2s // v1 = { v0[0], v2[0] } zip2 v0.2s, + * v0.2s, v1.2s // v0 = { v0[1], v2[1] } + * + * Instead, we use a literal downcast, vmovn_u64 (XTN), and vshrn_n_u64 + * (SHRN): + * + * shrn v1.2s, v0.2d, #32 // v1 = (uint32x2_t)(v0 >> 32); + * xtn v0.2s, v0.2d // v0 = (uint32x2_t)(v0 & 0xFFFFFFFF); + * + * This is available on ARMv7-A, but is less efficient than a single + * VZIP.32. + */ + + /* + * Function-like macro: + * void XXH_SPLIT_IN_PLACE(uint64x2_t &in, uint32x2_t &outLo, uint32x2_t + * &outHi) + * { + + * outLo = (uint32x2_t)(in & 0xFFFFFFFF); + * outHi = (uint32x2_t)(in >> 32); + * in = UNDEFINED; + * } + */ + #if !defined(XXH_NO_VZIP_HACK) /* define to disable */ \ + && defined(__GNUC__) && !defined(__aarch64__) && !defined(__arm64__) + #define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \ + do { \ + \ + /* Undocumented GCC/Clang operand modifier: %e0 = lower D half, \ + * %f0 = upper D half */ \ + /* https://github.com/gcc-mirror/gcc/blob/38cf91e5/gcc/config/arm/arm.c#L22486 \ + */ \ + /* https://github.com/llvm-mirror/llvm/blob/2c4ca683/lib/Target/ARM/ARMAsmPrinter.cpp#L399 \ + */ \ + __asm__("vzip.32 %e0, %f0" : "+w"(in)); \ + (outLo) = vget_low_u32(vreinterpretq_u32_u64(in)); \ + (outHi) = vget_high_u32(vreinterpretq_u32_u64(in)); \ + \ + } while (0) + #else + #define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \ + do { \ + \ + (outLo) = vmovn_u64(in); \ + (outHi) = vshrn_n_u64((in), 32); \ + \ + } while (0) + #endif + #endif /* XXH_VECTOR == XXH_NEON */ + + /* + * VSX and Z Vector helpers. + * + * This is very messy, and any pull requests to clean this up are welcome. + * + * There are a lot of problems with supporting VSX and s390x, due to + * inconsistent intrinsics, spotty coverage, and multiple endiannesses. + */ + #if XXH_VECTOR == XXH_VSX + #if defined(__s390x__) + #include + #else + /* gcc's altivec.h can have the unwanted consequence to unconditionally + * #define bool, vector, and pixel keywords, + * with bad consequences for programs already using these keywords for + * other purposes. The paragraph defining these macros is skipped when + * __APPLE_ALTIVEC__ is defined. + * __APPLE_ALTIVEC__ is _generally_ defined automatically by the + * compiler, but it seems that, in some cases, it isn't. Force the build + * macro to be defined, so that keywords are not altered. + */ + #if defined(__GNUC__) && !defined(__APPLE_ALTIVEC__) + #define __APPLE_ALTIVEC__ + #endif + #include + #endif + +typedef __vector unsigned long long xxh_u64x2; +typedef __vector unsigned char xxh_u8x16; +typedef __vector unsigned xxh_u32x4; + + #ifndef XXH_VSX_BE + #if defined(__BIG_ENDIAN__) || \ + (defined(__BYTE_ORDER__) && \ + __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + #define XXH_VSX_BE 1 + #elif defined(__VEC_ELEMENT_REG_ORDER__) && \ + __VEC_ELEMENT_REG_ORDER__ == __ORDER_BIG_ENDIAN__ + #warning \ + "-maltivec=be is not recommended. Please use native endianness." + #define XXH_VSX_BE 1 + #else + #define XXH_VSX_BE 0 + #endif + #endif /* !defined(XXH_VSX_BE) */ + + #if XXH_VSX_BE + /* A wrapper for POWER9's vec_revb. */ + #if defined(__POWER9_VECTOR__) || \ + (defined(__clang__) && defined(__s390x__)) + #define XXH_vec_revb vec_revb + #else +XXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val) { + + xxh_u8x16 const vByteSwap = {0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08}; + return vec_perm(val, val, vByteSwap); + +} + + #endif + #endif /* XXH_VSX_BE */ + +/* + * Performs an unaligned load and byte swaps it on big endian. + */ +XXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr) { + + xxh_u64x2 ret; + memcpy(&ret, ptr, sizeof(xxh_u64x2)); + #if XXH_VSX_BE + ret = XXH_vec_revb(ret); + #endif + return ret; + +} + + /* + * vec_mulo and vec_mule are very problematic intrinsics on PowerPC + * + * These intrinsics weren't added until GCC 8, despite existing for a + * while, and they are endian dependent. Also, their meaning swap + * depending on version. + * */ + #if defined(__s390x__) + /* s390x is always big endian, no issue on this platform */ + #define XXH_vec_mulo vec_mulo + #define XXH_vec_mule vec_mule + #elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw) + /* Clang has a better way to control this, we can just use the builtin + * which doesn't swap. */ + #define XXH_vec_mulo __builtin_altivec_vmulouw + #define XXH_vec_mule __builtin_altivec_vmuleuw + #else +/* gcc needs inline assembly */ +/* Adapted from + * https://github.com/google/highwayhash/blob/master/highwayhash/hh_vsx.h. */ +XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mulo(xxh_u32x4 a, xxh_u32x4 b) { + + xxh_u64x2 result; + __asm__("vmulouw %0, %1, %2" : "=v"(result) : "v"(a), "v"(b)); + return result; + +} + +XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b) { + + xxh_u64x2 result; + __asm__("vmuleuw %0, %1, %2" : "=v"(result) : "v"(a), "v"(b)); + return result; + +} + + #endif /* XXH_vec_mulo, XXH_vec_mule */ + #endif /* XXH_VECTOR == XXH_VSX */ + + /* prefetch + * can be disabled, by declaring XXH_NO_PREFETCH build macro */ + #if defined(XXH_NO_PREFETCH) + #define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ + #else + #if defined(_MSC_VER) && \ + (defined(_M_X64) || \ + defined( \ + _M_I86)) /* _mm_prefetch() is not defined outside of x86/x64 */ + #include /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ + #define XXH_PREFETCH(ptr) _mm_prefetch((const char *)(ptr), _MM_HINT_T0) + #elif defined(__GNUC__) && \ + ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))) + #define XXH_PREFETCH(ptr) \ + __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */) + #else + #define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ + #endif + #endif /* XXH_NO_PREFETCH */ + + /* ========================================== + * XXH3 default settings + * ========================================== */ + + #define XXH_SECRET_DEFAULT_SIZE 192 /* minimum XXH3_SECRET_SIZE_MIN */ + + #if (XXH_SECRET_DEFAULT_SIZE < XXH3_SECRET_SIZE_MIN) + #error "default keyset is not large enough" + #endif + +/* Pseudorandom secret taken directly from FARSH */ +XXH_ALIGN(64) +static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = { + + 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, + 0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, + 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, 0xcb, 0x79, 0xe6, 0x4e, + 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, + 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, + 0x81, 0x3a, 0x26, 0x4c, 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, + 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, 0x71, 0x64, 0x48, 0x97, + 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, + 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, + 0xc7, 0x0b, 0x4f, 0x1d, 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, + 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, 0xea, 0xc5, 0xac, 0x83, + 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, + 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, + 0x29, 0xd4, 0x68, 0x9e, 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, + 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, 0x45, 0xcb, 0x3a, 0x8f, + 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, + +}; + + #ifdef XXH_OLD_NAMES + #define kSecret XXH3_kSecret + #endif + + /* + * Calculates a 32-bit to 64-bit long multiply. + * + * Wraps __emulu on MSVC x86 because it tends to call __allmul when it + * doesn't need to (but it shouldn't need to anyways, it is about 7 + * instructions to do a 64x64 multiply...). Since we know that this will + * _always_ emit MULL, we use that instead of the normal method. + * + * If you are compiling for platforms like Thumb-1 and don't have a better + * option, you may also want to write your own long multiply routine here. + * + * XXH_FORCE_INLINE xxh_u64 XXH_mult32to64(xxh_u64 x, xxh_u64 y) + * { + + * return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF); + * } + */ + #if defined(_MSC_VER) && defined(_M_IX86) + #include + #define XXH_mult32to64(x, y) __emulu((unsigned)(x), (unsigned)(y)) + #else + /* + * Downcast + upcast is usually better than masking on older compilers + * like GCC 4.2 (especially 32-bit ones), all without affecting newer + * compilers. + * + * The other method, (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF), will AND both + * operands and perform a full 64x64 multiply -- entirely redundant on + * 32-bit. + */ + #define XXH_mult32to64(x, y) \ + ((xxh_u64)(xxh_u32)(x) * (xxh_u64)(xxh_u32)(y)) + #endif + +/* + * Calculates a 64->128-bit long multiply. + * + * Uses __uint128_t and _umul128 if available, otherwise uses a scalar version. + */ +static XXH128_hash_t XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) { + + /* + * GCC/Clang __uint128_t method. + * + * On most 64-bit targets, GCC and Clang define a __uint128_t type. + * This is usually the best way as it usually uses a native long 64-bit + * multiply, such as MULQ on x86_64 or MUL + UMULH on aarch64. + * + * Usually. + * + * Despite being a 32-bit platform, Clang (and emscripten) define this type + * despite not having the arithmetic for it. This results in a laggy + * compiler builtin call which calculates a full 128-bit multiply. + * In that case it is best to use the portable one. + * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677 + */ + #if defined(__GNUC__) && !defined(__wasm__) && \ + defined(__SIZEOF_INT128__) || \ + (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128) + + __uint128_t const product = (__uint128_t)lhs * (__uint128_t)rhs; + XXH128_hash_t r128; + r128.low64 = (xxh_u64)(product); + r128.high64 = (xxh_u64)(product >> 64); + return r128; + + /* + * MSVC for x64's _umul128 method. + * + * xxh_u64 _umul128(xxh_u64 Multiplier, xxh_u64 Multiplicand, xxh_u64 + * *HighProduct); + * + * This compiles to single operand MUL on x64. + */ + #elif defined(_M_X64) || defined(_M_IA64) + + #ifndef _MSC_VER + #pragma intrinsic(_umul128) + #endif + xxh_u64 product_high; + xxh_u64 const product_low = _umul128(lhs, rhs, &product_high); + XXH128_hash_t r128; + r128.low64 = product_low; + r128.high64 = product_high; + return r128; + + #else + /* + * Portable scalar method. Optimized for 32-bit and 64-bit ALUs. + * + * This is a fast and simple grade school multiply, which is shown below + * with base 10 arithmetic instead of base 0x100000000. + * + * 9 3 // D2 lhs = 93 + * x 7 5 // D2 rhs = 75 + * ---------- + * 1 5 // D2 lo_lo = (93 % 10) * (75 % 10) = 15 + * 4 5 | // D2 hi_lo = (93 / 10) * (75 % 10) = 45 + * 2 1 | // D2 lo_hi = (93 % 10) * (75 / 10) = 21 + * + 6 3 | | // D2 hi_hi = (93 / 10) * (75 / 10) = 63 + * --------- + * 2 7 | // D2 cross = (15 / 10) + (45 % 10) + 21 = 27 + * + 6 7 | | // D2 upper = (27 / 10) + (45 / 10) + 63 = 67 + * --------- + * 6 9 7 5 // D4 res = (27 * 10) + (15 % 10) + (67 * 100) = 6975 + * + * The reasons for adding the products like this are: + * 1. It avoids manual carry tracking. Just like how + * (9 * 9) + 9 + 9 = 99, the same applies with this for UINT64_MAX. + * This avoids a lot of complexity. + * + * 2. It hints for, and on Clang, compiles to, the powerful UMAAL + * instruction available in ARM's Digital Signal Processing extension + * in 32-bit ARMv6 and later, which is shown below: + * + * void UMAAL(xxh_u32 *RdLo, xxh_u32 *RdHi, xxh_u32 Rn, xxh_u32 Rm) + * { + + * xxh_u64 product = (xxh_u64)*RdLo * (xxh_u64)*RdHi + Rn + Rm; + * *RdLo = (xxh_u32)(product & 0xFFFFFFFF); + * *RdHi = (xxh_u32)(product >> 32); + * } + * + * This instruction was designed for efficient long multiplication, and + * allows this to be calculated in only 4 instructions at speeds + * comparable to some 64-bit ALUs. + * + * 3. It isn't terrible on other platforms. Usually this will be a couple + * of 32-bit ADD/ADCs. + */ + + /* First calculate all of the cross products. */ + xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF); + xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32, rhs & 0xFFFFFFFF); + xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32); + xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32, rhs >> 32); + + /* Now add the products together. These will never overflow. */ + xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi; + xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32) + hi_hi; + xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF); + + XXH128_hash_t r128; + r128.low64 = lower; + r128.high64 = upper; + return r128; + #endif + +} + +/* + * Does a 64-bit to 128-bit multiply, then XOR folds it. + * + * The reason for the separate function is to prevent passing too many structs + * around by value. This will hopefully inline the multiply, but we don't force + * it. + */ +static xxh_u64 XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) { + + XXH128_hash_t product = XXH_mult64to128(lhs, rhs); + return product.low64 ^ product.high64; + +} + +/* Seems to produce slightly better code on GCC for some reason. */ +XXH_FORCE_INLINE xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) { + + XXH_ASSERT(0 <= shift && shift < 64); + return v64 ^ (v64 >> shift); + +} + +/* + * This is a fast avalanche stage, + * suitable when input bits are already partially mixed + */ +static XXH64_hash_t XXH3_avalanche(xxh_u64 h64) { + + h64 = XXH_xorshift64(h64, 37); + h64 *= 0x165667919E3779F9ULL; + h64 = XXH_xorshift64(h64, 32); + return h64; + +} + +/* + * This is a stronger avalanche, + * inspired by Pelle Evensen's rrmxmx + * preferable when input has not been previously mixed + */ +static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len) { + + /* this mix is inspired by Pelle Evensen's rrmxmx */ + h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24); + h64 *= 0x9FB21C651E98DF25ULL; + h64 ^= (h64 >> 35) + len; + h64 *= 0x9FB21C651E98DF25ULL; + return XXH_xorshift64(h64, 28); + +} + +/* ========================================== + * Short keys + * ========================================== + * One of the shortcomings of XXH32 and XXH64 was that their performance was + * sub-optimal on short lengths. It used an iterative algorithm which strongly + * favored lengths that were a multiple of 4 or 8. + * + * Instead of iterating over individual inputs, we use a set of single shot + * functions which piece together a range of lengths and operate in constant + * time. + * + * Additionally, the number of multiplies has been significantly reduced. This + * reduces latency, especially when emulating 64-bit multiplies on 32-bit. + * + * Depending on the platform, this may or may not be faster than XXH32, but it + * is almost guaranteed to be faster than XXH64. + */ + +/* + * At very short lengths, there isn't enough input to fully hide secrets, or use + * the entire secret. + * + * There is also only a limited amount of mixing we can do before significantly + * impacting performance. + * + * Therefore, we use different sections of the secret and always mix two secret + * samples with an XOR. This should have no effect on performance on the + * seedless or withSeed variants because everything _should_ be constant folded + * by modern compilers. + * + * The XOR mixing hides individual parts of the secret and increases entropy. + * + * This adds an extra layer of strength for custom secrets. + */ +XXH_FORCE_INLINE XXH64_hash_t XXH3_len_1to3_64b(const xxh_u8 *input, size_t len, + const xxh_u8 *secret, + XXH64_hash_t seed) { + + XXH_ASSERT(input != NULL); + XXH_ASSERT(1 <= len && len <= 3); + XXH_ASSERT(secret != NULL); + /* + * len = 1: combined = { input[0], 0x01, input[0], input[0] } + * len = 2: combined = { input[1], 0x02, input[0], input[1] } + * len = 3: combined = { input[2], 0x03, input[0], input[1] } + */ + { + + xxh_u8 const c1 = input[0]; + xxh_u8 const c2 = input[len >> 1]; + xxh_u8 const c3 = input[len - 1]; + xxh_u32 const combined = ((xxh_u32)c1 << 16) | ((xxh_u32)c2 << 24) | + ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); + xxh_u64 const bitflip = + (XXH_readLE32(secret) ^ XXH_readLE32(secret + 4)) + seed; + xxh_u64 const keyed = (xxh_u64)combined ^ bitflip; + return XXH64_avalanche(keyed); + + } + +} + +XXH_FORCE_INLINE XXH64_hash_t XXH3_len_4to8_64b(const xxh_u8 *input, size_t len, + const xxh_u8 *secret, + XXH64_hash_t seed) { + + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(4 <= len && len < 8); + seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; + { + + xxh_u32 const input1 = XXH_readLE32(input); + xxh_u32 const input2 = XXH_readLE32(input + len - 4); + xxh_u64 const bitflip = + (XXH_readLE64(secret + 8) ^ XXH_readLE64(secret + 16)) - seed; + xxh_u64 const input64 = input2 + (((xxh_u64)input1) << 32); + xxh_u64 const keyed = input64 ^ bitflip; + return XXH3_rrmxmx(keyed, len); + + } + +} + +XXH_FORCE_INLINE XXH64_hash_t XXH3_len_9to16_64b(const xxh_u8 *input, + size_t len, + const xxh_u8 *secret, + XXH64_hash_t seed) { + + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(8 <= len && len <= 16); + { + + xxh_u64 const bitflip1 = + (XXH_readLE64(secret + 24) ^ XXH_readLE64(secret + 32)) + seed; + xxh_u64 const bitflip2 = + (XXH_readLE64(secret + 40) ^ XXH_readLE64(secret + 48)) - seed; + xxh_u64 const input_lo = XXH_readLE64(input) ^ bitflip1; + xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ bitflip2; + xxh_u64 const acc = len + XXH_swap64(input_lo) + input_hi + + XXH3_mul128_fold64(input_lo, input_hi); + return XXH3_avalanche(acc); + + } + +} + +XXH_FORCE_INLINE XXH64_hash_t XXH3_len_0to16_64b(const xxh_u8 *input, + size_t len, + const xxh_u8 *secret, + XXH64_hash_t seed) { + + XXH_ASSERT(len <= 16); + { + + if (XXH_likely(len > 8)) + return XXH3_len_9to16_64b(input, len, secret, seed); + if (XXH_likely(len >= 4)) + return XXH3_len_4to8_64b(input, len, secret, seed); + if (len) return XXH3_len_1to3_64b(input, len, secret, seed); + return XXH64_avalanche( + seed ^ (XXH_readLE64(secret + 56) ^ XXH_readLE64(secret + 64))); + + } + +} + +/* + * DISCLAIMER: There are known *seed-dependent* multicollisions here due to + * multiplication by zero, affecting hashes of lengths 17 to 240. + * + * However, they are very unlikely. + * + * Keep this in mind when using the unseeded XXH3_64bits() variant: As with all + * unseeded non-cryptographic hashes, it does not attempt to defend itself + * against specially crafted inputs, only random inputs. + * + * Compared to classic UMAC where a 1 in 2^31 chance of 4 consecutive bytes + * cancelling out the secret is taken an arbitrary number of times (addressed + * in XXH3_accumulate_512), this collision is very unlikely with random inputs + * and/or proper seeding: + * + * This only has a 1 in 2^63 chance of 8 consecutive bytes cancelling out, in a + * function that is only called up to 16 times per hash with up to 240 bytes of + * input. + * + * This is not too bad for a non-cryptographic hash function, especially with + * only 64 bit outputs. + * + * The 128-bit variant (which trades some speed for strength) is NOT affected + * by this, although it is always a good idea to use a proper seed if you care + * about strength. + */ +XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8 *XXH_RESTRICT input, + const xxh_u8 *XXH_RESTRICT secret, + xxh_u64 seed64) { + + #if defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ + && defined(__i386__) && defined(__SSE2__) /* x86 + SSE2 */ \ + && \ + !defined( \ + XXH_ENABLE_AUTOVECTORIZE) /* Define to disable like XXH32 hack */ + /* + * UGLY HACK: + * GCC for x86 tends to autovectorize the 128-bit multiply, resulting in + * slower code. + * + * By forcing seed64 into a register, we disrupt the cost model and + * cause it to scalarize. See `XXH32_round()` + * + * FIXME: Clang's output is still _much_ faster -- On an AMD Ryzen 3600, + * XXH3_64bits @ len=240 runs at 4.6 GB/s with Clang 9, but 3.3 GB/s on + * GCC 9.2, despite both emitting scalar code. + * + * GCC generates much better scalar code than Clang for the rest of XXH3, + * which is why finding a more optimal codepath is an interest. + */ + __asm__("" : "+r"(seed64)); + #endif + { + + xxh_u64 const input_lo = XXH_readLE64(input); + xxh_u64 const input_hi = XXH_readLE64(input + 8); + return XXH3_mul128_fold64(input_lo ^ (XXH_readLE64(secret) + seed64), + input_hi ^ (XXH_readLE64(secret + 8) - seed64)); + + } + +} + +/* For mid range keys, XXH3 uses a Mum-hash variant. */ +XXH_FORCE_INLINE XXH64_hash_t XXH3_len_17to128_64b( + const xxh_u8 *XXH_RESTRICT input, size_t len, + const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { + + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + (void)secretSize; + XXH_ASSERT(16 < len && len <= 128); + + { + + xxh_u64 acc = len * XXH_PRIME64_1; + if (len > 32) { + + if (len > 64) { + + if (len > 96) { + + acc += XXH3_mix16B(input + 48, secret + 96, seed); + acc += XXH3_mix16B(input + len - 64, secret + 112, seed); + + } + + acc += XXH3_mix16B(input + 32, secret + 64, seed); + acc += XXH3_mix16B(input + len - 48, secret + 80, seed); + + } + + acc += XXH3_mix16B(input + 16, secret + 32, seed); + acc += XXH3_mix16B(input + len - 32, secret + 48, seed); + + } + + acc += XXH3_mix16B(input + 0, secret + 0, seed); + acc += XXH3_mix16B(input + len - 16, secret + 16, seed); + + return XXH3_avalanche(acc); + + } + +} + + #define XXH3_MIDSIZE_MAX 240 + +XXH_NO_INLINE XXH64_hash_t XXH3_len_129to240_64b( + const xxh_u8 *XXH_RESTRICT input, size_t len, + const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { + + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + (void)secretSize; + XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); + + #define XXH3_MIDSIZE_STARTOFFSET 3 + #define XXH3_MIDSIZE_LASTOFFSET 17 + + { + + xxh_u64 acc = len * XXH_PRIME64_1; + int const nbRounds = (int)len / 16; + int i; + for (i = 0; i < 8; i++) { + + acc += XXH3_mix16B(input + (16 * i), secret + (16 * i), seed); + + } + + acc = XXH3_avalanche(acc); + XXH_ASSERT(nbRounds >= 8); + #if defined(__clang__) /* Clang */ \ + && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ + && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ + /* + * UGLY HACK: + * Clang for ARMv7-A tries to vectorize this loop, similar to GCC x86. + * In everywhere else, it uses scalar code. + * + * For 64->128-bit multiplies, even if the NEON was 100% optimal, it + * would still be slower than UMAAL (see XXH_mult64to128). + * + * Unfortunately, Clang doesn't handle the long multiplies properly and + * converts them to the nonexistent "vmulq_u64" intrinsic, which is then + * scalarized into an ugly mess of VMOV.32 instructions. + * + * This mess is difficult to avoid without turning autovectorization + * off completely, but they are usually relatively minor and/or not + * worth it to fix. + * + * This loop is the easiest to fix, as unlike XXH32, this pragma + * _actually works_ because it is a loop vectorization instead of an + * SLP vectorization. + */ + #pragma clang loop vectorize(disable) + #endif + for (i = 8; i < nbRounds; i++) { + + acc += + XXH3_mix16B(input + (16 * i), + secret + (16 * (i - 8)) + XXH3_MIDSIZE_STARTOFFSET, seed); + + } + + /* last bytes */ + acc += XXH3_mix16B(input + len - 16, + secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, + seed); + return XXH3_avalanche(acc); + + } + +} + + /* ======= Long Keys ======= */ + + #define XXH_STRIPE_LEN 64 + #define XXH_SECRET_CONSUME_RATE \ + 8 /* nb of secret bytes consumed at each accumulation */ + #define XXH_ACC_NB (XXH_STRIPE_LEN / sizeof(xxh_u64)) + + #ifdef XXH_OLD_NAMES + #define STRIPE_LEN XXH_STRIPE_LEN + #define ACC_NB XXH_ACC_NB + #endif + +XXH_FORCE_INLINE void XXH_writeLE64(void *dst, xxh_u64 v64) { + + if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64); + memcpy(dst, &v64, sizeof(v64)); + +} + + /* Several intrinsic functions below are supposed to accept __int64 as + * argument, as documented in + * https://software.intel.com/sites/landingpage/IntrinsicsGuide/ . However, + * several environments do not define __int64 type, requiring a workaround. + */ + #if !defined(__VMS) && \ + (defined(__cplusplus) || (defined(__STDC_VERSION__) && \ + (__STDC_VERSION__ >= 199901L) /* C99 */)) +typedef int64_t xxh_i64; + #else +/* the following type must have a width of 64-bit */ +typedef long long xxh_i64; + #endif + + /* + * XXH3_accumulate_512 is the tightest loop for long inputs, and it is the + * most optimized. + * + * It is a hardened version of UMAC, based off of FARSH's implementation. + * + * This was chosen because it adapts quite well to 32-bit, 64-bit, and SIMD + * implementations, and it is ridiculously fast. + * + * We harden it by mixing the original input to the accumulators as well as + * the product. + * + * This means that in the (relatively likely) case of a multiply by zero, the + * original input is preserved. + * + * On 128-bit inputs, we swap 64-bit pairs when we add the input to improve + * cross-pollination, as otherwise the upper and lower halves would be + * essentially independent. + * + * This doesn't matter on 64-bit hashes since they all get merged together in + * the end, so we skip the extra step. + * + * Both XXH3_64bits and XXH3_128bits use this subroutine. + */ + + #if (XXH_VECTOR == XXH_AVX512) || defined(XXH_X86DISPATCH) + + #ifndef XXH_TARGET_AVX512 + #define XXH_TARGET_AVX512 /* disable attribute target */ + #endif + +XXH_FORCE_INLINE XXH_TARGET_AVX512 void XXH3_accumulate_512_avx512( + void *XXH_RESTRICT acc, const void *XXH_RESTRICT input, + const void *XXH_RESTRICT secret) { + + XXH_ALIGN(64) __m512i *const xacc = (__m512i *)acc; + XXH_ASSERT((((size_t)acc) & 63) == 0); + XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); + + { + + /* data_vec = input[0]; */ + __m512i const data_vec = _mm512_loadu_si512(input); + /* key_vec = secret[0]; */ + __m512i const key_vec = _mm512_loadu_si512(secret); + /* data_key = data_vec ^ key_vec; */ + __m512i const data_key = _mm512_xor_si512(data_vec, key_vec); + /* data_key_lo = data_key >> 32; */ + __m512i const data_key_lo = + _mm512_shuffle_epi32(data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1)); + /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ + __m512i const product = _mm512_mul_epu32(data_key, data_key_lo); + /* xacc[0] += swap(data_vec); */ + __m512i const data_swap = + _mm512_shuffle_epi32(data_vec, (_MM_PERM_ENUM)_MM_SHUFFLE(1, 0, 3, 2)); + __m512i const sum = _mm512_add_epi64(*xacc, data_swap); + /* xacc[0] += product; */ + *xacc = _mm512_add_epi64(product, sum); + + } + +} + +/* + * XXH3_scrambleAcc: Scrambles the accumulators to improve mixing. + * + * Multiplication isn't perfect, as explained by Google in HighwayHash: + * + * // Multiplication mixes/scrambles bytes 0-7 of the 64-bit result to + * // varying degrees. In descending order of goodness, bytes + * // 3 4 2 5 1 6 0 7 have quality 228 224 164 160 100 96 36 32. + * // As expected, the upper and lower bytes are much worse. + * + * Source: + * https://github.com/google/highwayhash/blob/0aaf66b/highwayhash/hh_avx2.h#L291 + * + * Since our algorithm uses a pseudorandom secret to add some variance into the + * mix, we don't need to (or want to) mix as often or as much as HighwayHash + * does. + * + * This isn't as tight as XXH3_accumulate, but still written in SIMD to avoid + * extraction. + * + * Both XXH3_64bits and XXH3_128bits use this subroutine. + */ + +XXH_FORCE_INLINE XXH_TARGET_AVX512 void XXH3_scrambleAcc_avx512( + void *XXH_RESTRICT acc, const void *XXH_RESTRICT secret) { + + XXH_ASSERT((((size_t)acc) & 63) == 0); + XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); + { + + XXH_ALIGN(64) __m512i *const xacc = (__m512i *)acc; + const __m512i prime32 = _mm512_set1_epi32((int)XXH_PRIME32_1); + + /* xacc[0] ^= (xacc[0] >> 47) */ + __m512i const acc_vec = *xacc; + __m512i const shifted = _mm512_srli_epi64(acc_vec, 47); + __m512i const data_vec = _mm512_xor_si512(acc_vec, shifted); + /* xacc[0] ^= secret; */ + __m512i const key_vec = _mm512_loadu_si512(secret); + __m512i const data_key = _mm512_xor_si512(data_vec, key_vec); + + /* xacc[0] *= XXH_PRIME32_1; */ + __m512i const data_key_hi = + _mm512_shuffle_epi32(data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1)); + __m512i const prod_lo = _mm512_mul_epu32(data_key, prime32); + __m512i const prod_hi = _mm512_mul_epu32(data_key_hi, prime32); + *xacc = _mm512_add_epi64(prod_lo, _mm512_slli_epi64(prod_hi, 32)); + + } + +} + +XXH_FORCE_INLINE XXH_TARGET_AVX512 void XXH3_initCustomSecret_avx512( + void *XXH_RESTRICT customSecret, xxh_u64 seed64) { + + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 63) == 0); + XXH_STATIC_ASSERT(XXH_SEC_ALIGN == 64); + XXH_ASSERT(((size_t)customSecret & 63) == 0); + (void)(&XXH_writeLE64); + { + + int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m512i); + __m512i const seed = _mm512_mask_set1_epi64( + _mm512_set1_epi64((xxh_i64)seed64), 0xAA, -(xxh_i64)seed64); + + XXH_ALIGN(64) const __m512i *const src = (const __m512i *)XXH3_kSecret; + XXH_ALIGN(64) __m512i *const dest = (__m512i *)customSecret; + int i; + for (i = 0; i < nbRounds; ++i) { + + /* GCC has a bug, _mm512_stream_load_si512 accepts 'void*', not 'void + * const*', this will warn "discards ‘const’ qualifier". */ + union { + + XXH_ALIGN(64) const __m512i *cp; + XXH_ALIGN(64) void *p; + + } remote_const_void; + + remote_const_void.cp = src + i; + dest[i] = + _mm512_add_epi64(_mm512_stream_load_si512(remote_const_void.p), seed); + + } + + } + +} + + #endif + + #if (XXH_VECTOR == XXH_AVX2) || defined(XXH_X86DISPATCH) + + #ifndef XXH_TARGET_AVX2 + #define XXH_TARGET_AVX2 /* disable attribute target */ + #endif + +XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_accumulate_512_avx2( + void *XXH_RESTRICT acc, const void *XXH_RESTRICT input, + const void *XXH_RESTRICT secret) { + + XXH_ASSERT((((size_t)acc) & 31) == 0); + { + + XXH_ALIGN(32) __m256i *const xacc = (__m256i *)acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. + */ + const __m256i *const xinput = (const __m256i *)input; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ + const __m256i *const xsecret = (const __m256i *)secret; + + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m256i); i++) { + + /* data_vec = xinput[i]; */ + __m256i const data_vec = _mm256_loadu_si256(xinput + i); + /* key_vec = xsecret[i]; */ + __m256i const key_vec = _mm256_loadu_si256(xsecret + i); + /* data_key = data_vec ^ key_vec; */ + __m256i const data_key = _mm256_xor_si256(data_vec, key_vec); + /* data_key_lo = data_key >> 32; */ + __m256i const data_key_lo = + _mm256_shuffle_epi32(data_key, _MM_SHUFFLE(0, 3, 0, 1)); + /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ + __m256i const product = _mm256_mul_epu32(data_key, data_key_lo); + /* xacc[i] += swap(data_vec); */ + __m256i const data_swap = + _mm256_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2)); + __m256i const sum = _mm256_add_epi64(xacc[i], data_swap); + /* xacc[i] += product; */ + xacc[i] = _mm256_add_epi64(product, sum); + + } + + } + +} + +XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_scrambleAcc_avx2( + void *XXH_RESTRICT acc, const void *XXH_RESTRICT secret) { + + XXH_ASSERT((((size_t)acc) & 31) == 0); + { + + XXH_ALIGN(32) __m256i *const xacc = (__m256i *)acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ + const __m256i *const xsecret = (const __m256i *)secret; + const __m256i prime32 = _mm256_set1_epi32((int)XXH_PRIME32_1); + + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m256i); i++) { + + /* xacc[i] ^= (xacc[i] >> 47) */ + __m256i const acc_vec = xacc[i]; + __m256i const shifted = _mm256_srli_epi64(acc_vec, 47); + __m256i const data_vec = _mm256_xor_si256(acc_vec, shifted); + /* xacc[i] ^= xsecret; */ + __m256i const key_vec = _mm256_loadu_si256(xsecret + i); + __m256i const data_key = _mm256_xor_si256(data_vec, key_vec); + + /* xacc[i] *= XXH_PRIME32_1; */ + __m256i const data_key_hi = + _mm256_shuffle_epi32(data_key, _MM_SHUFFLE(0, 3, 0, 1)); + __m256i const prod_lo = _mm256_mul_epu32(data_key, prime32); + __m256i const prod_hi = _mm256_mul_epu32(data_key_hi, prime32); + xacc[i] = _mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32)); + + } + + } + +} + +XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_initCustomSecret_avx2( + void *XXH_RESTRICT customSecret, xxh_u64 seed64) { + + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 31) == 0); + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE / sizeof(__m256i)) == 6); + XXH_STATIC_ASSERT(XXH_SEC_ALIGN <= 64); + (void)(&XXH_writeLE64); + XXH_PREFETCH(customSecret); + { + + __m256i const seed = _mm256_set_epi64x(-(xxh_i64)seed64, (xxh_i64)seed64, + -(xxh_i64)seed64, (xxh_i64)seed64); + + XXH_ALIGN(64) const __m256i *const src = (const __m256i *)XXH3_kSecret; + XXH_ALIGN(64) __m256i * dest = (__m256i *)customSecret; + + #if defined(__GNUC__) || defined(__clang__) + /* + * On GCC & Clang, marking 'dest' as modified will cause the compiler: + * - do not extract the secret from sse registers in the internal loop + * - use less common registers, and avoid pushing these reg into stack + * The asm hack causes Clang to assume that XXH3_kSecretPtr aliases with + * customSecret, and on aarch64, this prevented LDP from merging two + * loads together for free. Putting the loads together before the stores + * properly generates LDP. + */ + __asm__("" : "+r"(dest)); + #endif + + /* GCC -O2 need unroll loop manually */ + dest[0] = _mm256_add_epi64(_mm256_stream_load_si256(src + 0), seed); + dest[1] = _mm256_add_epi64(_mm256_stream_load_si256(src + 1), seed); + dest[2] = _mm256_add_epi64(_mm256_stream_load_si256(src + 2), seed); + dest[3] = _mm256_add_epi64(_mm256_stream_load_si256(src + 3), seed); + dest[4] = _mm256_add_epi64(_mm256_stream_load_si256(src + 4), seed); + dest[5] = _mm256_add_epi64(_mm256_stream_load_si256(src + 5), seed); + + } + +} + + #endif + + #if (XXH_VECTOR == XXH_SSE2) || defined(XXH_X86DISPATCH) + + #ifndef XXH_TARGET_SSE2 + #define XXH_TARGET_SSE2 /* disable attribute target */ + #endif + +XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_accumulate_512_sse2( + void *XXH_RESTRICT acc, const void *XXH_RESTRICT input, + const void *XXH_RESTRICT secret) { + + /* SSE2 is just a half-scale version of the AVX2 version. */ + XXH_ASSERT((((size_t)acc) & 15) == 0); + { + + XXH_ALIGN(16) __m128i *const xacc = (__m128i *)acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ + const __m128i *const xinput = (const __m128i *)input; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ + const __m128i *const xsecret = (const __m128i *)secret; + + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m128i); i++) { + + /* data_vec = xinput[i]; */ + __m128i const data_vec = _mm_loadu_si128(xinput + i); + /* key_vec = xsecret[i]; */ + __m128i const key_vec = _mm_loadu_si128(xsecret + i); + /* data_key = data_vec ^ key_vec; */ + __m128i const data_key = _mm_xor_si128(data_vec, key_vec); + /* data_key_lo = data_key >> 32; */ + __m128i const data_key_lo = + _mm_shuffle_epi32(data_key, _MM_SHUFFLE(0, 3, 0, 1)); + /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ + __m128i const product = _mm_mul_epu32(data_key, data_key_lo); + /* xacc[i] += swap(data_vec); */ + __m128i const data_swap = + _mm_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2)); + __m128i const sum = _mm_add_epi64(xacc[i], data_swap); + /* xacc[i] += product; */ + xacc[i] = _mm_add_epi64(product, sum); + + } + + } + +} + +XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_scrambleAcc_sse2( + void *XXH_RESTRICT acc, const void *XXH_RESTRICT secret) { + + XXH_ASSERT((((size_t)acc) & 15) == 0); + { + + XXH_ALIGN(16) __m128i *const xacc = (__m128i *)acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ + const __m128i *const xsecret = (const __m128i *)secret; + const __m128i prime32 = _mm_set1_epi32((int)XXH_PRIME32_1); + + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m128i); i++) { + + /* xacc[i] ^= (xacc[i] >> 47) */ + __m128i const acc_vec = xacc[i]; + __m128i const shifted = _mm_srli_epi64(acc_vec, 47); + __m128i const data_vec = _mm_xor_si128(acc_vec, shifted); + /* xacc[i] ^= xsecret[i]; */ + __m128i const key_vec = _mm_loadu_si128(xsecret + i); + __m128i const data_key = _mm_xor_si128(data_vec, key_vec); + + /* xacc[i] *= XXH_PRIME32_1; */ + __m128i const data_key_hi = + _mm_shuffle_epi32(data_key, _MM_SHUFFLE(0, 3, 0, 1)); + __m128i const prod_lo = _mm_mul_epu32(data_key, prime32); + __m128i const prod_hi = _mm_mul_epu32(data_key_hi, prime32); + xacc[i] = _mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32)); + + } + + } + +} + +XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2( + void *XXH_RESTRICT customSecret, xxh_u64 seed64) { + + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); + (void)(&XXH_writeLE64); + { + + int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i); + + #if defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER < 1900 + // MSVC 32bit mode does not support _mm_set_epi64x before 2015 + XXH_ALIGN(16) + const xxh_i64 seed64x2[2] = {(xxh_i64)seed64, -(xxh_i64)seed64}; + __m128i const seed = _mm_load_si128((__m128i const *)seed64x2); + #else + __m128i const seed = _mm_set_epi64x(-(xxh_i64)seed64, (xxh_i64)seed64); + #endif + int i; + + XXH_ALIGN(64) const float *const src = (float const *)XXH3_kSecret; + XXH_ALIGN(XXH_SEC_ALIGN) __m128i *dest = (__m128i *)customSecret; + #if defined(__GNUC__) || defined(__clang__) + /* + * On GCC & Clang, marking 'dest' as modified will cause the compiler: + * - do not extract the secret from sse registers in the internal loop + * - use less common registers, and avoid pushing these reg into stack + */ + __asm__("" : "+r"(dest)); + #endif + + for (i = 0; i < nbRounds; ++i) { + + dest[i] = _mm_add_epi64(_mm_castps_si128(_mm_load_ps(src + i * 4)), seed); + + } + + } + +} + + #endif + + #if (XXH_VECTOR == XXH_NEON) + +XXH_FORCE_INLINE void XXH3_accumulate_512_neon( + void *XXH_RESTRICT acc, const void *XXH_RESTRICT input, + const void *XXH_RESTRICT secret) { + + XXH_ASSERT((((size_t)acc) & 15) == 0); + { + + XXH_ALIGN(16) uint64x2_t *const xacc = (uint64x2_t *)acc; + /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. + */ + uint8_t const *const xinput = (const uint8_t *)input; + uint8_t const *const xsecret = (const uint8_t *)secret; + + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(uint64x2_t); i++) { + + /* data_vec = xinput[i]; */ + uint8x16_t data_vec = vld1q_u8(xinput + (i * 16)); + /* key_vec = xsecret[i]; */ + uint8x16_t key_vec = vld1q_u8(xsecret + (i * 16)); + uint64x2_t data_key; + uint32x2_t data_key_lo, data_key_hi; + /* xacc[i] += swap(data_vec); */ + uint64x2_t const data64 = vreinterpretq_u64_u8(data_vec); + uint64x2_t const swapped = vextq_u64(data64, data64, 1); + xacc[i] = vaddq_u64(xacc[i], swapped); + /* data_key = data_vec ^ key_vec; */ + data_key = vreinterpretq_u64_u8(veorq_u8(data_vec, key_vec)); + /* data_key_lo = (uint32x2_t) (data_key & 0xFFFFFFFF); + * data_key_hi = (uint32x2_t) (data_key >> 32); + * data_key = UNDEFINED; */ + XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi); + /* xacc[i] += (uint64x2_t) data_key_lo * (uint64x2_t) data_key_hi; */ + xacc[i] = vmlal_u32(xacc[i], data_key_lo, data_key_hi); + + } + + } + +} + +XXH_FORCE_INLINE void XXH3_scrambleAcc_neon(void *XXH_RESTRICT acc, + const void *XXH_RESTRICT secret) { + + XXH_ASSERT((((size_t)acc) & 15) == 0); + + { + + uint64x2_t * xacc = (uint64x2_t *)acc; + uint8_t const *xsecret = (uint8_t const *)secret; + uint32x2_t prime = vdup_n_u32(XXH_PRIME32_1); + + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(uint64x2_t); i++) { + + /* xacc[i] ^= (xacc[i] >> 47); */ + uint64x2_t acc_vec = xacc[i]; + uint64x2_t shifted = vshrq_n_u64(acc_vec, 47); + uint64x2_t data_vec = veorq_u64(acc_vec, shifted); + + /* xacc[i] ^= xsecret[i]; */ + uint8x16_t key_vec = vld1q_u8(xsecret + (i * 16)); + uint64x2_t data_key = veorq_u64(data_vec, vreinterpretq_u64_u8(key_vec)); + + /* xacc[i] *= XXH_PRIME32_1 */ + uint32x2_t data_key_lo, data_key_hi; + /* data_key_lo = (uint32x2_t) (xacc[i] & 0xFFFFFFFF); + * data_key_hi = (uint32x2_t) (xacc[i] >> 32); + * xacc[i] = UNDEFINED; */ + XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi); + { /* + * prod_hi = (data_key >> 32) * XXH_PRIME32_1; + * + * Avoid vmul_u32 + vshll_n_u32 since Clang 6 and 7 will + * incorrectly "optimize" this: + * tmp = vmul_u32(vmovn_u64(a), vmovn_u64(b)); + * shifted = vshll_n_u32(tmp, 32); + * to this: + * tmp = "vmulq_u64"(a, b); // no such thing! + * shifted = vshlq_n_u64(tmp, 32); + * + * However, unlike SSE, Clang lacks a 64-bit multiply routine + * for NEON, and it scalarizes two 64-bit multiplies instead. + * + * vmull_u32 has the same timing as vmul_u32, and it avoids + * this bug completely. + * See https://bugs.llvm.org/show_bug.cgi?id=39967 + */ + uint64x2_t prod_hi = vmull_u32(data_key_hi, prime); + /* xacc[i] = prod_hi << 32; */ + xacc[i] = vshlq_n_u64(prod_hi, 32); + /* xacc[i] += (prod_hi & 0xFFFFFFFF) * XXH_PRIME32_1; */ + xacc[i] = vmlal_u32(xacc[i], data_key_lo, prime); + + } + + } + + } + +} + + #endif + + #if (XXH_VECTOR == XXH_VSX) + +XXH_FORCE_INLINE void XXH3_accumulate_512_vsx(void *XXH_RESTRICT acc, + const void *XXH_RESTRICT input, + const void *XXH_RESTRICT secret) { + + xxh_u64x2 *const xacc = (xxh_u64x2 *)acc; /* presumed aligned */ + xxh_u64x2 const *const xinput = + (xxh_u64x2 const *)input; /* no alignment restriction */ + xxh_u64x2 const *const xsecret = + (xxh_u64x2 const *)secret; /* no alignment restriction */ + xxh_u64x2 const v32 = {32, 32}; + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { + + /* data_vec = xinput[i]; */ + xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + i); + /* key_vec = xsecret[i]; */ + xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i); + xxh_u64x2 const data_key = data_vec ^ key_vec; + /* shuffled = (data_key << 32) | (data_key >> 32); */ + xxh_u32x4 const shuffled = (xxh_u32x4)vec_rl(data_key, v32); + /* product = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)shuffled & + * 0xFFFFFFFF); */ + xxh_u64x2 const product = XXH_vec_mulo((xxh_u32x4)data_key, shuffled); + xacc[i] += product; + + /* swap high and low halves */ + #ifdef __s390x__ + xacc[i] += vec_permi(data_vec, data_vec, 2); + #else + xacc[i] += vec_xxpermdi(data_vec, data_vec, 2); + #endif + + } + +} + +XXH_FORCE_INLINE void XXH3_scrambleAcc_vsx(void *XXH_RESTRICT acc, + const void *XXH_RESTRICT secret) { + + XXH_ASSERT((((size_t)acc) & 15) == 0); + + { + + xxh_u64x2 *const xacc = (xxh_u64x2 *)acc; + const xxh_u64x2 *const xsecret = (const xxh_u64x2 *)secret; + /* constants */ + xxh_u64x2 const v32 = {32, 32}; + xxh_u64x2 const v47 = {47, 47}; + xxh_u32x4 const prime = {XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1, + XXH_PRIME32_1}; + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { + + /* xacc[i] ^= (xacc[i] >> 47); */ + xxh_u64x2 const acc_vec = xacc[i]; + xxh_u64x2 const data_vec = acc_vec ^ (acc_vec >> v47); + + /* xacc[i] ^= xsecret[i]; */ + xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i); + xxh_u64x2 const data_key = data_vec ^ key_vec; + + /* xacc[i] *= XXH_PRIME32_1 */ + /* prod_lo = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)prime & + * 0xFFFFFFFF); */ + xxh_u64x2 const prod_even = XXH_vec_mule((xxh_u32x4)data_key, prime); + /* prod_hi = ((xxh_u64x2)data_key >> 32) * ((xxh_u64x2)prime >> 32); */ + xxh_u64x2 const prod_odd = XXH_vec_mulo((xxh_u32x4)data_key, prime); + xacc[i] = prod_odd + (prod_even << v32); + + } + + } + +} + + #endif + +/* scalar variants - universal */ + +XXH_FORCE_INLINE void XXH3_accumulate_512_scalar( + void *XXH_RESTRICT acc, const void *XXH_RESTRICT input, + const void *XXH_RESTRICT secret) { + + XXH_ALIGN(XXH_ACC_ALIGN) + xxh_u64 *const xacc = (xxh_u64 *)acc; /* presumed aligned */ + const xxh_u8 *const xinput = + (const xxh_u8 *)input; /* no alignment restriction */ + const xxh_u8 *const xsecret = + (const xxh_u8 *)secret; /* no alignment restriction */ + size_t i; + XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN - 1)) == 0); + for (i = 0; i < XXH_ACC_NB; i++) { + + xxh_u64 const data_val = XXH_readLE64(xinput + 8 * i); + xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + i * 8); + xacc[i ^ 1] += data_val; /* swap adjacent lanes */ + xacc[i] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32); + + } + +} + +XXH_FORCE_INLINE void XXH3_scrambleAcc_scalar(void *XXH_RESTRICT acc, + const void *XXH_RESTRICT secret) { + + XXH_ALIGN(XXH_ACC_ALIGN) + xxh_u64 *const xacc = (xxh_u64 *)acc; /* presumed aligned */ + const xxh_u8 *const xsecret = + (const xxh_u8 *)secret; /* no alignment restriction */ + size_t i; + XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN - 1)) == 0); + for (i = 0; i < XXH_ACC_NB; i++) { + + xxh_u64 const key64 = XXH_readLE64(xsecret + 8 * i); + xxh_u64 acc64 = xacc[i]; + acc64 = XXH_xorshift64(acc64, 47); + acc64 ^= key64; + acc64 *= XXH_PRIME32_1; + xacc[i] = acc64; + + } + +} + +XXH_FORCE_INLINE void XXH3_initCustomSecret_scalar( + void *XXH_RESTRICT customSecret, xxh_u64 seed64) { + + /* + * We need a separate pointer for the hack below, + * which requires a non-const pointer. + * Any decent compiler will optimize this out otherwise. + */ + const xxh_u8 *kSecretPtr = XXH3_kSecret; + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); + + #if defined(__clang__) && defined(__aarch64__) + /* + * UGLY HACK: + * Clang generates a bunch of MOV/MOVK pairs for aarch64, and they are + * placed sequentially, in order, at the top of the unrolled loop. + * + * While MOVK is great for generating constants (2 cycles for a 64-bit + * constant compared to 4 cycles for LDR), long MOVK chains stall the + * integer pipelines: + * I L S + * MOVK + * MOVK + * MOVK + * MOVK + * ADD + * SUB STR + * STR + * By forcing loads from memory (as the asm line causes Clang to assume + * that XXH3_kSecretPtr has been changed), the pipelines are used more + * efficiently: + * I L S + * LDR + * ADD LDR + * SUB STR + * STR + * XXH3_64bits_withSeed, len == 256, Snapdragon 835 + * without hack: 2654.4 MB/s + * with hack: 3202.9 MB/s + */ + __asm__("" : "+r"(kSecretPtr)); + #endif + /* + * Note: in debug mode, this overrides the asm optimization + * and Clang will emit MOVK chains again. + */ + XXH_ASSERT(kSecretPtr == XXH3_kSecret); + + { + + int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16; + int i; + for (i = 0; i < nbRounds; i++) { + + /* + * The asm hack causes Clang to assume that kSecretPtr aliases with + * customSecret, and on aarch64, this prevented LDP from merging two + * loads together for free. Putting the loads together before the stores + * properly generates LDP. + */ + xxh_u64 lo = XXH_readLE64(kSecretPtr + 16 * i) + seed64; + xxh_u64 hi = XXH_readLE64(kSecretPtr + 16 * i + 8) - seed64; + XXH_writeLE64((xxh_u8 *)customSecret + 16 * i, lo); + XXH_writeLE64((xxh_u8 *)customSecret + 16 * i + 8, hi); + + } + + } + +} + +typedef void (*XXH3_f_accumulate_512)(void *XXH_RESTRICT, const void *, + const void *); +typedef void (*XXH3_f_scrambleAcc)(void *XXH_RESTRICT, const void *); +typedef void (*XXH3_f_initCustomSecret)(void *XXH_RESTRICT, xxh_u64); + + #if (XXH_VECTOR == XXH_AVX512) + + #define XXH3_accumulate_512 XXH3_accumulate_512_avx512 + #define XXH3_scrambleAcc XXH3_scrambleAcc_avx512 + #define XXH3_initCustomSecret XXH3_initCustomSecret_avx512 + + #elif (XXH_VECTOR == XXH_AVX2) + + #define XXH3_accumulate_512 XXH3_accumulate_512_avx2 + #define XXH3_scrambleAcc XXH3_scrambleAcc_avx2 + #define XXH3_initCustomSecret XXH3_initCustomSecret_avx2 + + #elif (XXH_VECTOR == XXH_SSE2) + + #define XXH3_accumulate_512 XXH3_accumulate_512_sse2 + #define XXH3_scrambleAcc XXH3_scrambleAcc_sse2 + #define XXH3_initCustomSecret XXH3_initCustomSecret_sse2 + + #elif (XXH_VECTOR == XXH_NEON) + + #define XXH3_accumulate_512 XXH3_accumulate_512_neon + #define XXH3_scrambleAcc XXH3_scrambleAcc_neon + #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + + #elif (XXH_VECTOR == XXH_VSX) + + #define XXH3_accumulate_512 XXH3_accumulate_512_vsx + #define XXH3_scrambleAcc XXH3_scrambleAcc_vsx + #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + + #else /* scalar */ + + #define XXH3_accumulate_512 XXH3_accumulate_512_scalar + #define XXH3_scrambleAcc XXH3_scrambleAcc_scalar + #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + + #endif + + #ifndef XXH_PREFETCH_DIST + #ifdef __clang__ + #define XXH_PREFETCH_DIST 320 + #else + #if (XXH_VECTOR == XXH_AVX512) + #define XXH_PREFETCH_DIST 512 + #else + #define XXH_PREFETCH_DIST 384 + #endif + #endif /* __clang__ */ + #endif /* XXH_PREFETCH_DIST */ + +/* + * XXH3_accumulate() + * Loops over XXH3_accumulate_512(). + * Assumption: nbStripes will not overflow the secret size + */ +XXH_FORCE_INLINE void XXH3_accumulate(xxh_u64 *XXH_RESTRICT acc, + const xxh_u8 *XXH_RESTRICT input, + const xxh_u8 *XXH_RESTRICT secret, + size_t nbStripes, + XXH3_f_accumulate_512 f_acc512) { + + size_t n; + for (n = 0; n < nbStripes; n++) { + + const xxh_u8 *const in = input + n * XXH_STRIPE_LEN; + XXH_PREFETCH(in + XXH_PREFETCH_DIST); + f_acc512(acc, in, secret + n * XXH_SECRET_CONSUME_RATE); + + } + +} + +XXH_FORCE_INLINE void XXH3_hashLong_internal_loop( + xxh_u64 *XXH_RESTRICT acc, const xxh_u8 *XXH_RESTRICT input, size_t len, + const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, + XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble) { + + size_t const nbStripesPerBlock = + (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE; + size_t const block_len = XXH_STRIPE_LEN * nbStripesPerBlock; + size_t const nb_blocks = (len - 1) / block_len; + + size_t n; + + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + + for (n = 0; n < nb_blocks; n++) { + + XXH3_accumulate(acc, input + n * block_len, secret, nbStripesPerBlock, + f_acc512); + f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN); + + } + + /* last partial block */ + XXH_ASSERT(len > XXH_STRIPE_LEN); + { + + size_t const nbStripes = + ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN; + XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE)); + XXH3_accumulate(acc, input + nb_blocks * block_len, secret, nbStripes, + f_acc512); + + /* last stripe */ + { + + const xxh_u8 *const p = input + len - XXH_STRIPE_LEN; + #define XXH_SECRET_LASTACC_START \ + 7 /* not aligned on 8, last secret is different from acc & scrambler */ + f_acc512(acc, p, + secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START); + + } + + } + +} + +XXH_FORCE_INLINE xxh_u64 XXH3_mix2Accs(const xxh_u64 *XXH_RESTRICT acc, + const xxh_u8 *XXH_RESTRICT secret) { + + return XXH3_mul128_fold64(acc[0] ^ XXH_readLE64(secret), + acc[1] ^ XXH_readLE64(secret + 8)); + +} + +static XXH64_hash_t XXH3_mergeAccs(const xxh_u64 *XXH_RESTRICT acc, + const xxh_u8 *XXH_RESTRICT secret, + xxh_u64 start) { + + xxh_u64 result64 = start; + size_t i = 0; + + for (i = 0; i < 4; i++) { + + result64 += XXH3_mix2Accs(acc + 2 * i, secret + 16 * i); + #if defined(__clang__) /* Clang */ \ + && (defined(__arm__) || defined(__thumb__)) /* ARMv7 */ \ + && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ + && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ + /* + * UGLY HACK: + * Prevent autovectorization on Clang ARMv7-a. Exact same problem as + * the one in XXH3_len_129to240_64b. Speeds up shorter keys > 240b. + * XXH3_64bits, len == 256, Snapdragon 835: + * without hack: 2063.7 MB/s + * with hack: 2560.7 MB/s + */ + __asm__("" : "+r"(result64)); + #endif + + } + + return XXH3_avalanche(result64); + +} + + #define XXH3_INIT_ACC \ + { \ + \ + XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, \ + XXH_PRIME64_4, XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 \ + \ + } + +XXH_FORCE_INLINE XXH64_hash_t XXH3_hashLong_64b_internal( + const void *XXH_RESTRICT input, size_t len, const void *XXH_RESTRICT secret, + size_t secretSize, XXH3_f_accumulate_512 f_acc512, + XXH3_f_scrambleAcc f_scramble) { + + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; + + XXH3_hashLong_internal_loop(acc, (const xxh_u8 *)input, len, + (const xxh_u8 *)secret, secretSize, f_acc512, + f_scramble); + + /* converge into final hash */ + XXH_STATIC_ASSERT(sizeof(acc) == 64); + /* do not align on 8, so that the secret is different from the accumulator + */ + #define XXH_SECRET_MERGEACCS_START 11 + XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + return XXH3_mergeAccs(acc, + (const xxh_u8 *)secret + XXH_SECRET_MERGEACCS_START, + (xxh_u64)len * XXH_PRIME64_1); + +} + +/* + * It's important for performance that XXH3_hashLong is not inlined. + */ +XXH_NO_INLINE XXH64_hash_t XXH3_hashLong_64b_withSecret( + const void *XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, + const xxh_u8 *XXH_RESTRICT secret, size_t secretLen) { + + (void)seed64; + return XXH3_hashLong_64b_internal(input, len, secret, secretLen, + XXH3_accumulate_512, XXH3_scrambleAcc); + +} + +/* + * It's important for performance that XXH3_hashLong is not inlined. + * Since the function is not inlined, the compiler may not be able to understand + * that, in some scenarios, its `secret` argument is actually a compile time + * constant. This variant enforces that the compiler can detect that, and uses + * this opportunity to streamline the generated code for better performance. + */ +XXH_NO_INLINE XXH64_hash_t XXH3_hashLong_64b_default( + const void *XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, + const xxh_u8 *XXH_RESTRICT secret, size_t secretLen) { + + (void)seed64; + (void)secret; + (void)secretLen; + return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, + sizeof(XXH3_kSecret), XXH3_accumulate_512, + XXH3_scrambleAcc); + +} + +/* + * XXH3_hashLong_64b_withSeed(): + * Generate a custom key based on alteration of default XXH3_kSecret with the + * seed, and then use this key for long mode hashing. + * + * This operation is decently fast but nonetheless costs a little bit of time. + * Try to avoid it whenever possible (typically when seed==0). + * + * It's important for performance that XXH3_hashLong is not inlined. Not sure + * why (uop cache maybe?), but the difference is large and easily measurable. + */ +XXH_FORCE_INLINE XXH64_hash_t XXH3_hashLong_64b_withSeed_internal( + const void *input, size_t len, XXH64_hash_t seed, + XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble, + XXH3_f_initCustomSecret f_initSec) { + + if (seed == 0) + return XXH3_hashLong_64b_internal( + input, len, XXH3_kSecret, sizeof(XXH3_kSecret), f_acc512, f_scramble); + { + + XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; + f_initSec(secret, seed); + return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret), + f_acc512, f_scramble); + + } + +} + +/* + * It's important for performance that XXH3_hashLong is not inlined. + */ +XXH_NO_INLINE XXH64_hash_t XXH3_hashLong_64b_withSeed(const void * input, + size_t len, + XXH64_hash_t seed, + const xxh_u8 *secret, + size_t secretLen) { + + (void)secret; + (void)secretLen; + return XXH3_hashLong_64b_withSeed_internal( + input, len, seed, XXH3_accumulate_512, XXH3_scrambleAcc, + XXH3_initCustomSecret); + +} + +typedef XXH64_hash_t (*XXH3_hashLong64_f)(const void *XXH_RESTRICT, size_t, + XXH64_hash_t, + const xxh_u8 *XXH_RESTRICT, size_t); + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_64bits_internal(const void *XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, const void *XXH_RESTRICT secret, + size_t secretLen, XXH3_hashLong64_f f_hashLong) { + + XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); + /* + * If an action is to be taken if `secretLen` condition is not respected, + * it should be done here. + * For now, it's a contract pre-condition. + * Adding a check and a branch here would cost performance at every hash. + * Also, note that function signature doesn't offer room to return an error. + */ + if (len <= 16) + return XXH3_len_0to16_64b((const xxh_u8 *)input, len, + (const xxh_u8 *)secret, seed64); + if (len <= 128) + return XXH3_len_17to128_64b((const xxh_u8 *)input, len, + (const xxh_u8 *)secret, secretLen, seed64); + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_len_129to240_64b((const xxh_u8 *)input, len, + (const xxh_u8 *)secret, secretLen, seed64); + return f_hashLong(input, len, seed64, (const xxh_u8 *)secret, secretLen); + +} + +/* === Public entry point === */ + +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void *input, size_t len) { + + return XXH3_64bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), + XXH3_hashLong_64b_default); + +} + +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void *input, + size_t len, + const void *secret, + size_t secretSize) { + + return XXH3_64bits_internal(input, len, 0, secret, secretSize, + XXH3_hashLong_64b_withSecret); + +} + +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void *input, size_t len, + XXH64_hash_t seed) { + + return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, + sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed); + +} + +/* === XXH3 streaming === */ + +/* + * Malloc's a pointer that is always aligned to align. + * + * This must be freed with `XXH_alignedFree()`. + * + * malloc typically guarantees 16 byte alignment on 64-bit systems and 8 byte + * alignment on 32-bit. This isn't enough for the 32 byte aligned loads in AVX2 + * or on 32-bit, the 16 byte aligned loads in SSE2 and NEON. + * + * This underalignment previously caused a rather obvious crash which went + * completely unnoticed due to XXH3_createState() not actually being tested. + * Credit to RedSpah for noticing this bug. + * + * The alignment is done manually: Functions like posix_memalign or _mm_malloc + * are avoided: To maintain portability, we would have to write a fallback + * like this anyways, and besides, testing for the existence of library + * functions without relying on external build tools is impossible. + * + * The method is simple: Overallocate, manually align, and store the offset + * to the original behind the returned pointer. + * + * Align must be a power of 2 and 8 <= align <= 128. + */ +static void *XXH_alignedMalloc(size_t s, size_t align) { + + XXH_ASSERT(align <= 128 && align >= 8); /* range check */ + XXH_ASSERT((align & (align - 1)) == 0); /* power of 2 */ + XXH_ASSERT(s != 0 && s < (s + align)); /* empty/overflow */ + { /* Overallocate to make room for manual realignment and an offset byte */ + xxh_u8 *base = (xxh_u8 *)XXH_malloc(s + align); + if (base != NULL) { + + /* + * Get the offset needed to align this pointer. + * + * Even if the returned pointer is aligned, there will always be + * at least one byte to store the offset to the original pointer. + */ + size_t offset = align - ((size_t)base & (align - 1)); /* base % align */ + /* Add the offset for the now-aligned pointer */ + xxh_u8 *ptr = base + offset; + + XXH_ASSERT((size_t)ptr % align == 0); + + /* Store the offset immediately before the returned pointer. */ + ptr[-1] = (xxh_u8)offset; + return ptr; + + } + + return NULL; + + } + +} + +/* + * Frees an aligned pointer allocated by XXH_alignedMalloc(). Don't pass + * normal malloc'd pointers, XXH_alignedMalloc has a specific data layout. + */ +static void XXH_alignedFree(void *p) { + + if (p != NULL) { + + xxh_u8 *ptr = (xxh_u8 *)p; + /* Get the offset byte we added in XXH_malloc. */ + xxh_u8 offset = ptr[-1]; + /* Free the original malloc'd pointer */ + xxh_u8 *base = ptr - offset; + XXH_free(base); + + } + +} + +XXH_PUBLIC_API XXH3_state_t *XXH3_createState(void) { + + XXH3_state_t *const state = + (XXH3_state_t *)XXH_alignedMalloc(sizeof(XXH3_state_t), 64); + if (state == NULL) return NULL; + XXH3_INITSTATE(state); + return state; + +} + +XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t *statePtr) { + + XXH_alignedFree(statePtr); + return XXH_OK; + +} + +XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t * dst_state, + const XXH3_state_t *src_state) { + + memcpy(dst_state, src_state, sizeof(*dst_state)); + +} + +static void XXH3_64bits_reset_internal(XXH3_state_t *statePtr, + XXH64_hash_t seed, const void *secret, + size_t secretSize) { + + size_t const initStart = offsetof(XXH3_state_t, bufferedSize); + size_t const initLength = + offsetof(XXH3_state_t, nbStripesPerBlock) - initStart; + XXH_ASSERT(offsetof(XXH3_state_t, nbStripesPerBlock) > initStart); + XXH_ASSERT(statePtr != NULL); + /* set members from bufferedSize to nbStripesPerBlock (excluded) to 0 */ + memset((char *)statePtr + initStart, 0, initLength); + statePtr->acc[0] = XXH_PRIME32_3; + statePtr->acc[1] = XXH_PRIME64_1; + statePtr->acc[2] = XXH_PRIME64_2; + statePtr->acc[3] = XXH_PRIME64_3; + statePtr->acc[4] = XXH_PRIME64_4; + statePtr->acc[5] = XXH_PRIME32_2; + statePtr->acc[6] = XXH_PRIME64_5; + statePtr->acc[7] = XXH_PRIME32_1; + statePtr->seed = seed; + statePtr->extSecret = (const unsigned char *)secret; + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + statePtr->secretLimit = secretSize - XXH_STRIPE_LEN; + statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE; + +} + +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t *statePtr) { + + if (statePtr == NULL) return XXH_ERROR; + XXH3_64bits_reset_internal(statePtr, 0, XXH3_kSecret, + XXH_SECRET_DEFAULT_SIZE); + return XXH_OK; + +} + +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret( + XXH3_state_t *statePtr, const void *secret, size_t secretSize) { + + if (statePtr == NULL) return XXH_ERROR; + XXH3_64bits_reset_internal(statePtr, 0, secret, secretSize); + if (secret == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; + return XXH_OK; + +} + +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t *statePtr, + XXH64_hash_t seed) { + + if (statePtr == NULL) return XXH_ERROR; + if (seed == 0) return XXH3_64bits_reset(statePtr); + if (seed != statePtr->seed) + XXH3_initCustomSecret(statePtr->customSecret, seed); + XXH3_64bits_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); + return XXH_OK; + +} + +/* Note : when XXH3_consumeStripes() is invoked, + * there must be a guarantee that at least one more byte must be consumed from + * input + * so that the function can blindly consume all stripes using the "normal" + * secret segment */ +XXH_FORCE_INLINE void XXH3_consumeStripes( + xxh_u64 *XXH_RESTRICT acc, size_t *XXH_RESTRICT nbStripesSoFarPtr, + size_t nbStripesPerBlock, const xxh_u8 *XXH_RESTRICT input, + size_t nbStripes, const xxh_u8 *XXH_RESTRICT secret, size_t secretLimit, + XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble) { + + XXH_ASSERT(nbStripes <= + nbStripesPerBlock); /* can handle max 1 scramble per invocation */ + XXH_ASSERT(*nbStripesSoFarPtr < nbStripesPerBlock); + if (nbStripesPerBlock - *nbStripesSoFarPtr <= nbStripes) { + + /* need a scrambling operation */ + size_t const nbStripesToEndofBlock = nbStripesPerBlock - *nbStripesSoFarPtr; + size_t const nbStripesAfterBlock = nbStripes - nbStripesToEndofBlock; + XXH3_accumulate(acc, input, + secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, + nbStripesToEndofBlock, f_acc512); + f_scramble(acc, secret + secretLimit); + XXH3_accumulate(acc, input + nbStripesToEndofBlock * XXH_STRIPE_LEN, secret, + nbStripesAfterBlock, f_acc512); + *nbStripesSoFarPtr = nbStripesAfterBlock; + + } else { + + XXH3_accumulate(acc, input, + secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, + nbStripes, f_acc512); + *nbStripesSoFarPtr += nbStripes; + + } + +} + +/* + * Both XXH3_64bits_update and XXH3_128bits_update use this routine. + */ +XXH_FORCE_INLINE XXH_errorcode XXH3_update(XXH3_state_t *state, + const xxh_u8 *input, size_t len, + XXH3_f_accumulate_512 f_acc512, + XXH3_f_scrambleAcc f_scramble) { + + if (input == NULL) + #if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && \ + (XXH_ACCEPT_NULL_INPUT_POINTER >= 1) + return XXH_OK; + #else + return XXH_ERROR; + #endif + + { + + const xxh_u8 *const bEnd = input + len; + const unsigned char *const secret = + (state->extSecret == NULL) ? state->customSecret : state->extSecret; + + state->totalLen += len; + + if (state->bufferedSize + len <= + XXH3_INTERNALBUFFER_SIZE) { /* fill in tmp buffer */ + XXH_memcpy(state->buffer + state->bufferedSize, input, len); + state->bufferedSize += (XXH32_hash_t)len; + return XXH_OK; + + } + + /* total input is now > XXH3_INTERNALBUFFER_SIZE */ + + #define XXH3_INTERNALBUFFER_STRIPES \ + (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN) + XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == + 0); /* clean multiple */ + + /* + * Internal buffer is partially filled (always, except at beginning) + * Complete it, then consume it. + */ + if (state->bufferedSize) { + + size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize; + XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize); + input += loadSize; + XXH3_consumeStripes(state->acc, &state->nbStripesSoFar, + state->nbStripesPerBlock, state->buffer, + XXH3_INTERNALBUFFER_STRIPES, secret, + state->secretLimit, f_acc512, f_scramble); + state->bufferedSize = 0; + + } + + XXH_ASSERT(input < bEnd); + + /* Consume input by a multiple of internal buffer size */ + if (input + XXH3_INTERNALBUFFER_SIZE < bEnd) { + + const xxh_u8 *const limit = bEnd - XXH3_INTERNALBUFFER_SIZE; + do { + + XXH3_consumeStripes(state->acc, &state->nbStripesSoFar, + state->nbStripesPerBlock, input, + XXH3_INTERNALBUFFER_STRIPES, secret, + state->secretLimit, f_acc512, f_scramble); + input += XXH3_INTERNALBUFFER_SIZE; + + } while (input < limit); + + /* for last partial stripe */ + memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, + input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); + + } + + XXH_ASSERT(input < bEnd); + + /* Some remaining input (always) : buffer it */ + XXH_memcpy(state->buffer, input, (size_t)(bEnd - input)); + state->bufferedSize = (XXH32_hash_t)(bEnd - input); + + } + + return XXH_OK; + +} + +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update(XXH3_state_t *state, + const void *input, size_t len) { + + return XXH3_update(state, (const xxh_u8 *)input, len, XXH3_accumulate_512, + XXH3_scrambleAcc); + +} + +XXH_FORCE_INLINE void XXH3_digest_long(XXH64_hash_t * acc, + const XXH3_state_t * state, + const unsigned char *secret) { + + /* + * Digest on a local copy. This way, the state remains unaltered, and it can + * continue ingesting more input afterwards. + */ + memcpy(acc, state->acc, sizeof(state->acc)); + if (state->bufferedSize >= XXH_STRIPE_LEN) { + + size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN; + size_t nbStripesSoFar = state->nbStripesSoFar; + XXH3_consumeStripes(acc, &nbStripesSoFar, state->nbStripesPerBlock, + state->buffer, nbStripes, secret, state->secretLimit, + XXH3_accumulate_512, XXH3_scrambleAcc); + /* last stripe */ + XXH3_accumulate_512(acc, + state->buffer + state->bufferedSize - XXH_STRIPE_LEN, + secret + state->secretLimit - XXH_SECRET_LASTACC_START); + + } else { /* bufferedSize < XXH_STRIPE_LEN */ + + xxh_u8 lastStripe[XXH_STRIPE_LEN]; + size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize; + XXH_ASSERT(state->bufferedSize > + 0); /* there is always some input buffered */ + memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, + catchupSize); + memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); + XXH3_accumulate_512(acc, lastStripe, + secret + state->secretLimit - XXH_SECRET_LASTACC_START); + + } + +} + +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest(const XXH3_state_t *state) { + + const unsigned char *const secret = + (state->extSecret == NULL) ? state->customSecret : state->extSecret; + if (state->totalLen > XXH3_MIDSIZE_MAX) { + + XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; + XXH3_digest_long(acc, state, secret); + return XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, + (xxh_u64)state->totalLen * XXH_PRIME64_1); + + } + + /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */ + if (state->seed) + return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, + state->seed); + return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen), + secret, state->secretLimit + XXH_STRIPE_LEN); + +} + + #define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x)) + +XXH_PUBLIC_API void XXH3_generateSecret(void * secretBuffer, + const void *customSeed, + size_t customSeedSize) { + + XXH_ASSERT(secretBuffer != NULL); + if (customSeedSize == 0) { + + memcpy(secretBuffer, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); + return; + + } + + XXH_ASSERT(customSeed != NULL); + + { + + size_t const segmentSize = sizeof(XXH128_hash_t); + size_t const nbSegments = XXH_SECRET_DEFAULT_SIZE / segmentSize; + XXH128_canonical_t scrambler; + XXH64_hash_t seeds[12]; + size_t segnb; + XXH_ASSERT(nbSegments == 12); + XXH_ASSERT(segmentSize * nbSegments == + XXH_SECRET_DEFAULT_SIZE); /* exact multiple */ + XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0)); + + /* + * Copy customSeed to seeds[], truncating or repeating as necessary. + */ + { + + size_t toFill = XXH_MIN(customSeedSize, sizeof(seeds)); + size_t filled = toFill; + memcpy(seeds, customSeed, toFill); + while (filled < sizeof(seeds)) { + + toFill = XXH_MIN(filled, sizeof(seeds) - filled); + memcpy((char *)seeds + filled, seeds, toFill); + filled += toFill; + + } + + } + + /* generate secret */ + memcpy(secretBuffer, &scrambler, sizeof(scrambler)); + for (segnb = 1; segnb < nbSegments; segnb++) { + + size_t const segmentStart = segnb * segmentSize; + XXH128_canonical_t segment; + XXH128_canonicalFromHash(&segment, + XXH128(&scrambler, sizeof(scrambler), + XXH_readLE64(seeds + segnb) + segnb)); + memcpy((char *)secretBuffer + segmentStart, &segment, sizeof(segment)); + + } + + } + +} + +/* ========================================== + * XXH3 128 bits (a.k.a XXH128) + * ========================================== + * XXH3's 128-bit variant has better mixing and strength than the 64-bit + * variant, even without counting the significantly larger output size. + * + * For example, extra steps are taken to avoid the seed-dependent collisions + * in 17-240 byte inputs (See XXH3_mix16B and XXH128_mix32B). + * + * This strength naturally comes at the cost of some speed, especially on short + * lengths. Note that longer hashes are about as fast as the 64-bit version + * due to it using only a slight modification of the 64-bit loop. + * + * XXH128 is also more oriented towards 64-bit machines. It is still extremely + * fast for a _128-bit_ hash on 32-bit (it usually clears XXH64). + */ + +XXH_FORCE_INLINE XXH128_hash_t XXH3_len_1to3_128b(const xxh_u8 *input, + size_t len, + const xxh_u8 *secret, + XXH64_hash_t seed) { + + /* A doubled version of 1to3_64b with different constants. */ + XXH_ASSERT(input != NULL); + XXH_ASSERT(1 <= len && len <= 3); + XXH_ASSERT(secret != NULL); + /* + * len = 1: combinedl = { input[0], 0x01, input[0], input[0] } + * len = 2: combinedl = { input[1], 0x02, input[0], input[1] } + * len = 3: combinedl = { input[2], 0x03, input[0], input[1] } + */ + { + + xxh_u8 const c1 = input[0]; + xxh_u8 const c2 = input[len >> 1]; + xxh_u8 const c3 = input[len - 1]; + xxh_u32 const combinedl = ((xxh_u32)c1 << 16) | ((xxh_u32)c2 << 24) | + ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); + xxh_u32 const combinedh = XXH_rotl32(XXH_swap32(combinedl), 13); + xxh_u64 const bitflipl = + (XXH_readLE32(secret) ^ XXH_readLE32(secret + 4)) + seed; + xxh_u64 const bitfliph = + (XXH_readLE32(secret + 8) ^ XXH_readLE32(secret + 12)) - seed; + xxh_u64 const keyed_lo = (xxh_u64)combinedl ^ bitflipl; + xxh_u64 const keyed_hi = (xxh_u64)combinedh ^ bitfliph; + XXH128_hash_t h128; + h128.low64 = XXH64_avalanche(keyed_lo); + h128.high64 = XXH64_avalanche(keyed_hi); + return h128; + + } + +} + +XXH_FORCE_INLINE XXH128_hash_t XXH3_len_4to8_128b(const xxh_u8 *input, + size_t len, + const xxh_u8 *secret, + XXH64_hash_t seed) { + + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(4 <= len && len <= 8); + seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; + { + + xxh_u32 const input_lo = XXH_readLE32(input); + xxh_u32 const input_hi = XXH_readLE32(input + len - 4); + xxh_u64 const input_64 = input_lo + ((xxh_u64)input_hi << 32); + xxh_u64 const bitflip = + (XXH_readLE64(secret + 16) ^ XXH_readLE64(secret + 24)) + seed; + xxh_u64 const keyed = input_64 ^ bitflip; + + /* Shift len to the left to ensure it is even, this avoids even multiplies. + */ + XXH128_hash_t m128 = XXH_mult64to128(keyed, XXH_PRIME64_1 + (len << 2)); + + m128.high64 += (m128.low64 << 1); + m128.low64 ^= (m128.high64 >> 3); + + m128.low64 = XXH_xorshift64(m128.low64, 35); + m128.low64 *= 0x9FB21C651E98DF25ULL; + m128.low64 = XXH_xorshift64(m128.low64, 28); + m128.high64 = XXH3_avalanche(m128.high64); + return m128; + + } + +} + +XXH_FORCE_INLINE XXH128_hash_t XXH3_len_9to16_128b(const xxh_u8 *input, + size_t len, + const xxh_u8 *secret, + XXH64_hash_t seed) { + + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(9 <= len && len <= 16); + { + + xxh_u64 const bitflipl = + (XXH_readLE64(secret + 32) ^ XXH_readLE64(secret + 40)) - seed; + xxh_u64 const bitfliph = + (XXH_readLE64(secret + 48) ^ XXH_readLE64(secret + 56)) + seed; + xxh_u64 const input_lo = XXH_readLE64(input); + xxh_u64 input_hi = XXH_readLE64(input + len - 8); + XXH128_hash_t m128 = + XXH_mult64to128(input_lo ^ input_hi ^ bitflipl, XXH_PRIME64_1); + /* + * Put len in the middle of m128 to ensure that the length gets mixed to + * both the low and high bits in the 128x64 multiply below. + */ + m128.low64 += (xxh_u64)(len - 1) << 54; + input_hi ^= bitfliph; + /* + * Add the high 32 bits of input_hi to the high 32 bits of m128, then + * add the long product of the low 32 bits of input_hi and XXH_PRIME32_2 to + * the high 64 bits of m128. + * + * The best approach to this operation is different on 32-bit and 64-bit. + */ + if (sizeof(void *) < sizeof(xxh_u64)) { /* 32-bit */ + /* + * 32-bit optimized version, which is more readable. + * + * On 32-bit, it removes an ADC and delays a dependency between the two + * halves of m128.high64, but it generates an extra mask on 64-bit. + */ + m128.high64 += (input_hi & 0xFFFFFFFF00000000ULL) + + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2); + + } else { + + /* + * 64-bit optimized (albeit more confusing) version. + * + * Uses some properties of addition and multiplication to remove the mask: + * + * Let: + * a = input_hi.lo = (input_hi & 0x00000000FFFFFFFF) + * b = input_hi.hi = (input_hi & 0xFFFFFFFF00000000) + * c = XXH_PRIME32_2 + * + * a + (b * c) + * Inverse Property: x + y - x == y + * a + (b * (1 + c - 1)) + * Distributive Property: x * (y + z) == (x * y) + (x * z) + * a + (b * 1) + (b * (c - 1)) + * Identity Property: x * 1 == x + * a + b + (b * (c - 1)) + * + * Substitute a, b, and c: + * input_hi.hi + input_hi.lo + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - + * 1)) + * + * Since input_hi.hi + input_hi.lo == input_hi, we get this: + * input_hi + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) + */ + m128.high64 += + input_hi + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2 - 1); + + } + + /* m128 ^= XXH_swap64(m128 >> 64); */ + m128.low64 ^= XXH_swap64(m128.high64); + + { /* 128x64 multiply: h128 = m128 * XXH_PRIME64_2; */ + XXH128_hash_t h128 = XXH_mult64to128(m128.low64, XXH_PRIME64_2); + h128.high64 += m128.high64 * XXH_PRIME64_2; + + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = XXH3_avalanche(h128.high64); + return h128; + + } + + } + +} + +/* + * Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN + */ +XXH_FORCE_INLINE XXH128_hash_t XXH3_len_0to16_128b(const xxh_u8 *input, + size_t len, + const xxh_u8 *secret, + XXH64_hash_t seed) { + + XXH_ASSERT(len <= 16); + { + + if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed); + if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed); + if (len) return XXH3_len_1to3_128b(input, len, secret, seed); + { + + XXH128_hash_t h128; + xxh_u64 const bitflipl = + XXH_readLE64(secret + 64) ^ XXH_readLE64(secret + 72); + xxh_u64 const bitfliph = + XXH_readLE64(secret + 80) ^ XXH_readLE64(secret + 88); + h128.low64 = XXH64_avalanche(seed ^ bitflipl); + h128.high64 = XXH64_avalanche(seed ^ bitfliph); + return h128; + + } + + } + +} + +/* + * A bit slower than XXH3_mix16B, but handles multiply by zero better. + */ +XXH_FORCE_INLINE XXH128_hash_t XXH128_mix32B(XXH128_hash_t acc, + const xxh_u8 *input_1, + const xxh_u8 *input_2, + const xxh_u8 *secret, + XXH64_hash_t seed) { + + acc.low64 += XXH3_mix16B(input_1, secret + 0, seed); + acc.low64 ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8); + acc.high64 += XXH3_mix16B(input_2, secret + 16, seed); + acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8); + return acc; + +} + +XXH_FORCE_INLINE XXH128_hash_t XXH3_len_17to128_128b( + const xxh_u8 *XXH_RESTRICT input, size_t len, + const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { + + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + (void)secretSize; + XXH_ASSERT(16 < len && len <= 128); + + { + + XXH128_hash_t acc; + acc.low64 = len * XXH_PRIME64_1; + acc.high64 = 0; + if (len > 32) { + + if (len > 64) { + + if (len > 96) { + + acc = XXH128_mix32B(acc, input + 48, input + len - 64, secret + 96, + seed); + + } + + acc = + XXH128_mix32B(acc, input + 32, input + len - 48, secret + 64, seed); + + } + + acc = XXH128_mix32B(acc, input + 16, input + len - 32, secret + 32, seed); + + } + + acc = XXH128_mix32B(acc, input, input + len - 16, secret, seed); + { + + XXH128_hash_t h128; + h128.low64 = acc.low64 + acc.high64; + h128.high64 = (acc.low64 * XXH_PRIME64_1) + (acc.high64 * XXH_PRIME64_4) + + ((len - seed) * XXH_PRIME64_2); + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); + return h128; + + } + + } + +} + +XXH_NO_INLINE XXH128_hash_t XXH3_len_129to240_128b( + const xxh_u8 *XXH_RESTRICT input, size_t len, + const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { + + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + (void)secretSize; + XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); + + { + + XXH128_hash_t acc; + int const nbRounds = (int)len / 32; + int i; + acc.low64 = len * XXH_PRIME64_1; + acc.high64 = 0; + for (i = 0; i < 4; i++) { + + acc = XXH128_mix32B(acc, input + (32 * i), input + (32 * i) + 16, + secret + (32 * i), seed); + + } + + acc.low64 = XXH3_avalanche(acc.low64); + acc.high64 = XXH3_avalanche(acc.high64); + XXH_ASSERT(nbRounds >= 4); + for (i = 4; i < nbRounds; i++) { + + acc = XXH128_mix32B(acc, input + (32 * i), input + (32 * i) + 16, + secret + XXH3_MIDSIZE_STARTOFFSET + (32 * (i - 4)), + seed); + + } + + /* last bytes */ + acc = XXH128_mix32B( + acc, input + len - 16, input + len - 32, + secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16, + 0ULL - seed); + + { + + XXH128_hash_t h128; + h128.low64 = acc.low64 + acc.high64; + h128.high64 = (acc.low64 * XXH_PRIME64_1) + (acc.high64 * XXH_PRIME64_4) + + ((len - seed) * XXH_PRIME64_2); + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); + return h128; + + } + + } + +} + +XXH_FORCE_INLINE XXH128_hash_t XXH3_hashLong_128b_internal( + const void *XXH_RESTRICT input, size_t len, + const xxh_u8 *XXH_RESTRICT secret, size_t secretSize, + XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble) { + + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; + + XXH3_hashLong_internal_loop(acc, (const xxh_u8 *)input, len, secret, + secretSize, f_acc512, f_scramble); + + /* converge into final hash */ + XXH_STATIC_ASSERT(sizeof(acc) == 64); + XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + { + + XXH128_hash_t h128; + h128.low64 = XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, + (xxh_u64)len * XXH_PRIME64_1); + h128.high64 = XXH3_mergeAccs( + acc, secret + secretSize - sizeof(acc) - XXH_SECRET_MERGEACCS_START, + ~((xxh_u64)len * XXH_PRIME64_2)); + return h128; + + } + +} + +/* + * It's important for performance that XXH3_hashLong is not inlined. + */ +XXH_NO_INLINE XXH128_hash_t XXH3_hashLong_128b_default( + const void *XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, + const void *XXH_RESTRICT secret, size_t secretLen) { + + (void)seed64; + (void)secret; + (void)secretLen; + return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, + sizeof(XXH3_kSecret), XXH3_accumulate_512, + XXH3_scrambleAcc); + +} + +/* + * It's important for performance that XXH3_hashLong is not inlined. + */ +XXH_NO_INLINE XXH128_hash_t XXH3_hashLong_128b_withSecret( + const void *XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, + const void *XXH_RESTRICT secret, size_t secretLen) { + + (void)seed64; + return XXH3_hashLong_128b_internal(input, len, (const xxh_u8 *)secret, + secretLen, XXH3_accumulate_512, + XXH3_scrambleAcc); + +} + +XXH_FORCE_INLINE XXH128_hash_t XXH3_hashLong_128b_withSeed_internal( + const void *XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, + XXH3_f_accumulate_512 f_acc512, XXH3_f_scrambleAcc f_scramble, + XXH3_f_initCustomSecret f_initSec) { + + if (seed64 == 0) + return XXH3_hashLong_128b_internal( + input, len, XXH3_kSecret, sizeof(XXH3_kSecret), f_acc512, f_scramble); + { + + XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; + f_initSec(secret, seed64); + return XXH3_hashLong_128b_internal(input, len, (const xxh_u8 *)secret, + sizeof(secret), f_acc512, f_scramble); + + } + +} + +/* + * It's important for performance that XXH3_hashLong is not inlined. + */ +XXH_NO_INLINE XXH128_hash_t +XXH3_hashLong_128b_withSeed(const void *input, size_t len, XXH64_hash_t seed64, + const void *XXH_RESTRICT secret, size_t secretLen) { + + (void)secret; + (void)secretLen; + return XXH3_hashLong_128b_withSeed_internal( + input, len, seed64, XXH3_accumulate_512, XXH3_scrambleAcc, + XXH3_initCustomSecret); + +} + +typedef XXH128_hash_t (*XXH3_hashLong128_f)(const void *XXH_RESTRICT, size_t, + XXH64_hash_t, + const void *XXH_RESTRICT, size_t); + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_128bits_internal(const void *input, size_t len, XXH64_hash_t seed64, + const void *XXH_RESTRICT secret, size_t secretLen, + XXH3_hashLong128_f f_hl128) { + + XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); + /* + * If an action is to be taken if `secret` conditions are not respected, + * it should be done here. + * For now, it's a contract pre-condition. + * Adding a check and a branch here would cost performance at every hash. + */ + if (len <= 16) + return XXH3_len_0to16_128b((const xxh_u8 *)input, len, + (const xxh_u8 *)secret, seed64); + if (len <= 128) + return XXH3_len_17to128_128b((const xxh_u8 *)input, len, + (const xxh_u8 *)secret, secretLen, seed64); + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_len_129to240_128b((const xxh_u8 *)input, len, + (const xxh_u8 *)secret, secretLen, seed64); + return f_hl128(input, len, seed64, secret, secretLen); + +} + +/* === Public XXH128 API === */ + +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void *input, size_t len) { + + return XXH3_128bits_internal(input, len, 0, XXH3_kSecret, + sizeof(XXH3_kSecret), + XXH3_hashLong_128b_default); + +} + +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void *input, + size_t len, + const void *secret, + size_t secretSize) { + + return XXH3_128bits_internal(input, len, 0, (const xxh_u8 *)secret, + secretSize, XXH3_hashLong_128b_withSecret); + +} + +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void * input, + size_t len, + XXH64_hash_t seed) { + + return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, + sizeof(XXH3_kSecret), + XXH3_hashLong_128b_withSeed); + +} + +XXH_PUBLIC_API XXH128_hash_t XXH128(const void *input, size_t len, + XXH64_hash_t seed) { + + return XXH3_128bits_withSeed(input, len, seed); + +} + +/* === XXH3 128-bit streaming === */ + +/* + * All the functions are actually the same as for 64-bit streaming variant. + * The only difference is the finalizatiom routine. + */ + +static void XXH3_128bits_reset_internal(XXH3_state_t *statePtr, + XXH64_hash_t seed, const void *secret, + size_t secretSize) { + + XXH3_64bits_reset_internal(statePtr, seed, secret, secretSize); + +} + +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t *statePtr) { + + if (statePtr == NULL) return XXH_ERROR; + XXH3_128bits_reset_internal(statePtr, 0, XXH3_kSecret, + XXH_SECRET_DEFAULT_SIZE); + return XXH_OK; + +} + +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret( + XXH3_state_t *statePtr, const void *secret, size_t secretSize) { + + if (statePtr == NULL) return XXH_ERROR; + XXH3_128bits_reset_internal(statePtr, 0, secret, secretSize); + if (secret == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; + return XXH_OK; + +} + +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t *statePtr, + XXH64_hash_t seed) { + + if (statePtr == NULL) return XXH_ERROR; + if (seed == 0) return XXH3_128bits_reset(statePtr); + if (seed != statePtr->seed) + XXH3_initCustomSecret(statePtr->customSecret, seed); + XXH3_128bits_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); + return XXH_OK; + +} + +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update(XXH3_state_t *state, + const void * input, + size_t len) { + + return XXH3_update(state, (const xxh_u8 *)input, len, XXH3_accumulate_512, + XXH3_scrambleAcc); + +} + +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest(const XXH3_state_t *state) { + + const unsigned char *const secret = + (state->extSecret == NULL) ? state->customSecret : state->extSecret; + if (state->totalLen > XXH3_MIDSIZE_MAX) { + + XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; + XXH3_digest_long(acc, state, secret); + XXH_ASSERT(state->secretLimit + XXH_STRIPE_LEN >= + sizeof(acc) + XXH_SECRET_MERGEACCS_START); + { + + XXH128_hash_t h128; + h128.low64 = XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, + (xxh_u64)state->totalLen * XXH_PRIME64_1); + h128.high64 = + XXH3_mergeAccs(acc, + secret + state->secretLimit + XXH_STRIPE_LEN - + sizeof(acc) - XXH_SECRET_MERGEACCS_START, + ~((xxh_u64)state->totalLen * XXH_PRIME64_2)); + return h128; + + } + + } + + /* len <= XXH3_MIDSIZE_MAX : short code */ + if (state->seed) + return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, + state->seed); + return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen), + secret, state->secretLimit + XXH_STRIPE_LEN); + +} + + /* 128-bit utility functions */ + + #include /* memcmp, memcpy */ + +/* return : 1 is equal, 0 if different */ +XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) { + + /* note : XXH128_hash_t is compact, it has no padding byte */ + return !(memcmp(&h1, &h2, sizeof(h1))); + +} + +/* This prototype is compatible with stdlib's qsort(). + * return : >0 if *h128_1 > *h128_2 + * <0 if *h128_1 < *h128_2 + * =0 if *h128_1 == *h128_2 */ +XXH_PUBLIC_API int XXH128_cmp(const void *h128_1, const void *h128_2) { + + XXH128_hash_t const h1 = *(const XXH128_hash_t *)h128_1; + XXH128_hash_t const h2 = *(const XXH128_hash_t *)h128_2; + int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64); + /* note : bets that, in most cases, hash values are different */ + if (hcmp) return hcmp; + return (h1.low64 > h2.low64) - (h2.low64 > h1.low64); + +} + +/*====== Canonical representation ======*/ +XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t *dst, + XXH128_hash_t hash) { + + XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) { + + hash.high64 = XXH_swap64(hash.high64); + hash.low64 = XXH_swap64(hash.low64); + + } + + memcpy(dst, &hash.high64, sizeof(hash.high64)); + memcpy((char *)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64)); + +} + +XXH_PUBLIC_API XXH128_hash_t +XXH128_hashFromCanonical(const XXH128_canonical_t *src) { + + XXH128_hash_t h; + h.high64 = XXH_readBE64(src); + h.low64 = XXH_readBE64(src->digest + 8); + return h; + +} + + /* Pop our optimization override from above */ + #if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \ + && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ + && defined(__OPTIMIZE__) && \ + !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */ + #pragma GCC pop_options + #endif #endif /* XXH_NO_LONG_LONG */ diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 0c472845..c6d8225f 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -138,9 +138,9 @@ static u8 check_if_text(struct queue_entry *q) { } // non-overlong 2-byte - if (((0xC2 <= buf[offset + 0] && buf[offset + 0] <= 0xDF) && - (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF)) && - len - offset > 1) { + if (len - offset > 1 && + ((0xC2 <= buf[offset + 0] && buf[offset + 0] <= 0xDF) && + (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF))) { offset += 2; utf8++; diff --git a/src/afl-performance.c b/src/afl-performance.c index a9d7cefa..7a80ac4b 100644 --- a/src/afl-performance.c +++ b/src/afl-performance.c @@ -22,7 +22,10 @@ #include #include "afl-fuzz.h" #include "types.h" -#include "xxh3.h" + +#define XXH_INLINE_ALL +#include "xxhash.h" +#undef XXH_INLINE_ALL /* we use xoshiro256** instead of rand/random because it is 10x faster and has better randomness properties. */ -- cgit 1.4.1 From e45ae8e5da9d603976a4fde1184455e5e9c49051 Mon Sep 17 00:00:00 2001 From: Thomas Rooijakkers Date: Fri, 4 Sep 2020 13:13:47 +0200 Subject: Export set afl_environment_variables to stats --- README.md | 1 + src/afl-fuzz-stats.c | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/README.md b/README.md index 6e5d9c1f..7b73a5f3 100644 --- a/README.md +++ b/README.md @@ -1035,6 +1035,7 @@ without feedback, bug reports, or patches from: Andrea Biondo Vincent Le Garrec Khaled Yakdan Kuang-che Wu Josephine Calliotte Konrad Welc + Thomas Rooijakkers ``` Thank you! diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 0ce35cb7..38c954e5 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -24,6 +24,7 @@ */ #include "afl-fuzz.h" +#include "envs.h" #include /* Update stats file for unattended monitoring. */ @@ -163,11 +164,28 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, ? "" : "default", afl->orig_cmdline); + + char * val; + uint32_t i = 0; + uint32_t s_afl_env = + sizeof(afl_environment_variables) / sizeof(afl_environment_variables[0]) - + 1; + + for (i = 0; i < s_afl_env; i++) { + + if ((val = get_afl_env(afl_environment_variables[i])) != NULL) { + + fprintf(f, "%-18.*s: %s\n", strlen(afl_environment_variables[i]), + afl_environment_variables[i], val); + + } + + } + /* ignore errors */ if (afl->debug) { - uint32_t i = 0; fprintf(f, "virgin_bytes :"); for (i = 0; i < afl->fsrv.map_size; i++) { -- cgit 1.4.1 From 6adaacbb3aed2f967b4f3aeacdc41e91502914b3 Mon Sep 17 00:00:00 2001 From: Thomas Rooijakkers Date: Fri, 4 Sep 2020 15:46:46 +0200 Subject: Seperate fuzzer_setup from fuzzer_stats, only write fuzzer_setup at the start --- include/afl-fuzz.h | 10 +++--- src/afl-fuzz-stats.c | 93 +++++++++++++++++++++++++++++++++++++--------------- src/afl-fuzz.c | 1 + 3 files changed, 73 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index f3a76492..f9858669 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -945,10 +945,12 @@ void destroy_extras(afl_state_t *); /* Stats */ -void write_stats_file(afl_state_t *, double, double, double); -void maybe_update_plot_file(afl_state_t *, double, double); -void show_stats(afl_state_t *); -void show_init_stats(afl_state_t *); +FILE *open_file(const char *); +void write_fuzzer_setup_file(afl_state_t *); +void write_stats_file(afl_state_t *, double, double, double); +void maybe_update_plot_file(afl_state_t *, double, double); +void show_stats(afl_state_t *); +void show_init_stats(afl_state_t *); /* Run */ diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 38c954e5..45b1326c 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -27,6 +27,69 @@ #include "envs.h" #include +/* Open file for writing */ + +FILE *open_file(const char *fn) { + + s32 fd; + FILE *f; + + fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + + if (fd < 0) { PFATAL("Unable to create '%s'", fn); } + + f = fdopen(fd, "w"); + + if (!f) { PFATAL("fdopen() failed"); } + + return f; + +} + +/* Write fuzzer setup file */ + +void write_fuzzer_setup_file(afl_state_t *afl) { + + u8 fn[PATH_MAX]; + FILE *f; + + snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); + f = open_file(fn); + + char * val; + uint32_t i = 0; + uint32_t s_afl_env = + sizeof(afl_environment_variables) / sizeof(afl_environment_variables[0]) - + 1; + + uint32_t max_len = 0; + uint32_t cur_len = 0; + for (i = 0; i < s_afl_env; i++) { + + if ((val = getenv(afl_environment_variables[i])) != NULL) { + + cur_len = strlen(afl_environment_variables[i]); + max_len = cur_len > max_len ? cur_len : max_len; + + } + + } + + for (i = 0; i < s_afl_env; i++) { + + if ((val = getenv(afl_environment_variables[i])) != NULL) { + + fprintf(f, "%*.*s : %s\n", -max_len, strlen(afl_environment_variables[i]), + afl_environment_variables[i], val); + + } + + } + + fclose(f); + +} + /* Update stats file for unattended monitoring. */ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, @@ -37,20 +100,12 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, #endif unsigned long long int cur_time = get_cur_time(); + u32 t_bytes = count_non_255_bytes(afl, afl->virgin_bits); u8 fn[PATH_MAX]; - s32 fd; FILE * f; - u32 t_bytes = count_non_255_bytes(afl, afl->virgin_bits); snprintf(fn, PATH_MAX, "%s/fuzzer_stats", afl->out_dir); - - fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); - - if (fd < 0) { PFATAL("Unable to create '%s'", fn); } - - f = fdopen(fd, "w"); - - if (!f) { PFATAL("fdopen() failed"); } + f = open_file(fn); /* Keep last values in case we're called from another context where exec/sec stats and such are not readily available. */ @@ -165,27 +220,11 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, : "default", afl->orig_cmdline); - char * val; - uint32_t i = 0; - uint32_t s_afl_env = - sizeof(afl_environment_variables) / sizeof(afl_environment_variables[0]) - - 1; - - for (i = 0; i < s_afl_env; i++) { - - if ((val = get_afl_env(afl_environment_variables[i])) != NULL) { - - fprintf(f, "%-18.*s: %s\n", strlen(afl_environment_variables[i]), - afl_environment_variables[i], val); - - } - - } - /* ignore errors */ if (afl->debug) { + uint32_t i = 0; fprintf(f, "virgin_bytes :"); for (i = 0; i < afl->fsrv.map_size; i++) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 0df6c15c..ae060b07 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1274,6 +1274,7 @@ int main(int argc, char **argv_orig, char **envp) { seek_to = find_start_position(afl); + write_fuzzer_setup_file(afl); write_stats_file(afl, 0, 0, 0); maybe_update_plot_file(afl, 0, 0); save_auto(afl); -- cgit 1.4.1 From 809a7cffe2de74b048143f0820fa922c9a18aff5 Mon Sep 17 00:00:00 2001 From: Thomas Rooijakkers Date: Fri, 4 Sep 2020 16:02:09 +0200 Subject: Write set environment variables in an env file style. --- include/afl-fuzz.h | 2 +- src/afl-fuzz-stats.c | 27 ++++++--------------------- src/afl-fuzz.c | 2 +- 3 files changed, 8 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index f9858669..eebd74c7 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -946,7 +946,7 @@ void destroy_extras(afl_state_t *); /* Stats */ FILE *open_file(const char *); -void write_fuzzer_setup_file(afl_state_t *); +void write_fuzzer_config_file(afl_state_t *); void write_stats_file(afl_state_t *, double, double, double); void maybe_update_plot_file(afl_state_t *, double, double); void show_stats(afl_state_t *); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 45b1326c..298ad229 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -48,39 +48,24 @@ FILE *open_file(const char *fn) { /* Write fuzzer setup file */ -void write_fuzzer_setup_file(afl_state_t *afl) { +void write_fuzzer_config_file(afl_state_t *afl) { u8 fn[PATH_MAX]; FILE *f; - snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); + snprintf(fn, PATH_MAX, "%s/fuzzer_config", afl->out_dir); f = open_file(fn); - char * val; - uint32_t i = 0; + char *val; + uint32_t s_afl_env = sizeof(afl_environment_variables) / sizeof(afl_environment_variables[0]) - 1; - - uint32_t max_len = 0; - uint32_t cur_len = 0; - for (i = 0; i < s_afl_env; i++) { - - if ((val = getenv(afl_environment_variables[i])) != NULL) { - - cur_len = strlen(afl_environment_variables[i]); - max_len = cur_len > max_len ? cur_len : max_len; - - } - - } - - for (i = 0; i < s_afl_env; i++) { + for (uint32_t i = 0; i < s_afl_env; i++) { if ((val = getenv(afl_environment_variables[i])) != NULL) { - fprintf(f, "%*.*s : %s\n", -max_len, strlen(afl_environment_variables[i]), - afl_environment_variables[i], val); + fprintf(f, "%s=%s\n", afl_environment_variables[i], val); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index ae060b07..e9ea8b62 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1274,7 +1274,7 @@ int main(int argc, char **argv_orig, char **envp) { seek_to = find_start_position(afl); - write_fuzzer_setup_file(afl); + write_fuzzer_config_file(afl); write_stats_file(afl, 0, 0, 0); maybe_update_plot_file(afl, 0, 0); save_auto(afl); -- cgit 1.4.1 From 50f61b64b1bbf2f5354bcff4f1d225965fee2d06 Mon Sep 17 00:00:00 2001 From: Thomas Rooijakkers Date: Fri, 4 Sep 2020 16:22:22 +0200 Subject: Make open_file() inline --- include/afl-fuzz.h | 11 +++++------ src/afl-fuzz-stats.c | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index eebd74c7..e3c3d5aa 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -945,12 +945,11 @@ void destroy_extras(afl_state_t *); /* Stats */ -FILE *open_file(const char *); -void write_fuzzer_config_file(afl_state_t *); -void write_stats_file(afl_state_t *, double, double, double); -void maybe_update_plot_file(afl_state_t *, double, double); -void show_stats(afl_state_t *); -void show_init_stats(afl_state_t *); +void write_fuzzer_config_file(afl_state_t *); +void write_stats_file(afl_state_t *, double, double, double); +void maybe_update_plot_file(afl_state_t *, double, double); +void show_stats(afl_state_t *); +void show_init_stats(afl_state_t *); /* Run */ diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 298ad229..b59a40e4 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -29,7 +29,7 @@ /* Open file for writing */ -FILE *open_file(const char *fn) { +inline FILE *open_file(const char *fn) { s32 fd; FILE *f; -- cgit 1.4.1 From 6c715f1a69f91d4336023a8ba10fb4a7e126f9c2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 4 Sep 2020 17:04:42 +0200 Subject: more changes to fuzzer_setup --- docs/Changelog.md | 6 +++-- include/afl-fuzz.h | 2 +- include/common.h | 6 +++++ src/afl-common.c | 33 +++++++++++++++++++++++++ src/afl-fuzz-stats.c | 70 ++++++++++++++++++++++++++++------------------------ src/afl-fuzz.c | 3 ++- 6 files changed, 84 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index d1ee9656..0d93ee1f 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -15,7 +15,7 @@ sending a mail to . https://github.com/AFLplusplus/Grammar-Mutator - a few QOL changes for Apple and its outdated gmake - afl-fuzz: - - Fix for auto dictionary entries found during fuzzing to not throw out + - fix for auto dictionary entries found during fuzzing to not throw out a -x dictionary - added total execs done to plot file - AFL_MAX_DET_EXTRAS env variable added to control the amount of @@ -25,11 +25,13 @@ sending a mail to . timeout. - bugfix for cmplog that results in a heap overflow based on target data (thanks to the magma team for reporting!) + - write fuzzing setup into out/fuzzer_setup (environment variables and + command line) - custom mutators: - added afl_custom_fuzz_count/fuzz_count function to allow specifying the number of fuzz attempts for custom_fuzz - llvm_mode: - - Ported SanCov to LTO, and made it the default for LTO. better + - ported SanCov to LTO, and made it the default for LTO. better instrumentation locations - Further llvm 12 support (fast moving target like afl++ :-) ) - deprecated LLVM SKIPSINGLEBLOCK env environment diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index e3c3d5aa..358cb5dc 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -945,7 +945,7 @@ void destroy_extras(afl_state_t *); /* Stats */ -void write_fuzzer_config_file(afl_state_t *); +void write_setup_file(afl_state_t *, int, char **); void write_stats_file(afl_state_t *, double, double, double); void maybe_update_plot_file(afl_state_t *, double, double); void show_stats(afl_state_t *); diff --git a/include/common.h b/include/common.h index 87a7425b..c364ade0 100644 --- a/include/common.h +++ b/include/common.h @@ -110,5 +110,11 @@ u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms); /* Reads the map size from ENV */ u32 get_map_size(void); +/* create a stream file */ +FILE *create_ffile(u8 *fn); + +/* create a file */ +s32 create_file(u8 *fn); + #endif diff --git a/src/afl-common.c b/src/afl-common.c index 367dec72..d66440aa 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -877,3 +877,36 @@ u32 get_map_size(void) { } +/* Create a stream file */ + +FILE *create_ffile(u8 *fn) { + + s32 fd; + FILE *f; + + fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + + if (fd < 0) { PFATAL("Unable to create '%s'", fn); } + + f = fdopen(fd, "w"); + + if (!f) { PFATAL("fdopen() failed"); } + + return f; + +} + +/* Create a file */ + +s32 create_file(u8 *fn) { + + s32 fd; + + fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + + if (fd < 0) { PFATAL("Unable to create '%s'", fn); } + + return fd; + +} + diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index b59a40e4..a84f1c7a 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -27,51 +27,57 @@ #include "envs.h" #include -/* Open file for writing */ - -inline FILE *open_file(const char *fn) { - - s32 fd; - FILE *f; - - fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); +/* Write fuzzer setup file */ - if (fd < 0) { PFATAL("Unable to create '%s'", fn); } +void write_setup_file(afl_state_t *afl, int argc, char **argv) { - f = fdopen(fd, "w"); + char *val; + u8 fn[PATH_MAX]; + snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); + FILE *f = create_ffile(fn); - if (!f) { PFATAL("fdopen() failed"); } + fprintf(f, "# environment variables:\n"); + u32 s_afl_env = + sizeof(afl_environment_variables) / sizeof(afl_environment_variables[0]) - + 1; + for (u32 i = 0; i < s_afl_env; i++) { - return f; + if ((val = getenv(afl_environment_variables[i])) != NULL) { -} + fprintf(f, "%s=%s\n", afl_environment_variables[i], val); -/* Write fuzzer setup file */ + } -void write_fuzzer_config_file(afl_state_t *afl) { + } - u8 fn[PATH_MAX]; - FILE *f; + fprintf(f, "# command line:\n"); - snprintf(fn, PATH_MAX, "%s/fuzzer_config", afl->out_dir); - f = open_file(fn); + s32 i; + size_t j; + for (i = 0; i < argc; i++) { - char *val; + if (i) fprintf(f, " "); + if (index(argv[i], '\'')) { - uint32_t s_afl_env = - sizeof(afl_environment_variables) / sizeof(afl_environment_variables[0]) - - 1; - for (uint32_t i = 0; i < s_afl_env; i++) { + fprintf(f, "'"); + for (j = 0; j < strlen(argv[i]); j++) + if (argv[i][j] == '\'') + fprintf(f, "'\"'\"'"); + else + fprintf(f, "%c", argv[i][j]); + fprintf(f, "'"); - if ((val = getenv(afl_environment_variables[i])) != NULL) { + } else { - fprintf(f, "%s=%s\n", afl_environment_variables[i], val); + fprintf(f, "'%s'", argv[i]); } } + fprintf(f, "\n"); fclose(f); + (void)(afl_environment_deprecated); } @@ -84,13 +90,13 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, struct rusage rus; #endif - unsigned long long int cur_time = get_cur_time(); - u32 t_bytes = count_non_255_bytes(afl, afl->virgin_bits); - u8 fn[PATH_MAX]; - FILE * f; + u64 cur_time = get_cur_time(); + u32 t_bytes = count_non_255_bytes(afl, afl->virgin_bits); + u8 fn[PATH_MAX]; + FILE *f; snprintf(fn, PATH_MAX, "%s/fuzzer_stats", afl->out_dir); - f = open_file(fn); + f = create_ffile(fn); /* Keep last values in case we're called from another context where exec/sec stats and such are not readily available. */ @@ -209,7 +215,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, if (afl->debug) { - uint32_t i = 0; + u32 i = 0; fprintf(f, "virgin_bytes :"); for (i = 0; i < afl->fsrv.map_size; i++) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e9ea8b62..c12d5db5 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1128,6 +1128,8 @@ int main(int argc, char **argv_orig, char **envp) { setup_custom_mutators(afl); + write_setup_file(afl, argc, argv); + setup_cmdline_file(afl, argv + optind); read_testcases(afl); @@ -1274,7 +1276,6 @@ int main(int argc, char **argv_orig, char **envp) { seek_to = find_start_position(afl); - write_fuzzer_config_file(afl); write_stats_file(afl, 0, 0, 0); maybe_update_plot_file(afl, 0, 0); save_auto(afl); -- cgit 1.4.1 From b7b38205d816e20a5e3c87adcdd1fdb6de612755 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 4 Sep 2020 17:37:11 +0200 Subject: fix travis --- src/afl-fuzz-stats.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index a84f1c7a..7e77532e 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -35,12 +35,14 @@ void write_setup_file(afl_state_t *afl, int argc, char **argv) { u8 fn[PATH_MAX]; snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); FILE *f = create_ffile(fn); + u32 i; fprintf(f, "# environment variables:\n"); u32 s_afl_env = sizeof(afl_environment_variables) / sizeof(afl_environment_variables[0]) - 1; - for (u32 i = 0; i < s_afl_env; i++) { + + for (i = 0; i < s_afl_env; i++) { if ((val = getenv(afl_environment_variables[i])) != NULL) { -- cgit 1.4.1 From 77b824d1014c3fb11804a2e91d28d155cd0f62d1 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 4 Sep 2020 17:56:17 +0200 Subject: compile fix --- src/afl-fuzz-stats.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 7e77532e..05cbafef 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -35,10 +35,10 @@ void write_setup_file(afl_state_t *afl, int argc, char **argv) { u8 fn[PATH_MAX]; snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); FILE *f = create_ffile(fn); - u32 i; + s32 i; fprintf(f, "# environment variables:\n"); - u32 s_afl_env = + s32 s_afl_env = (s32) sizeof(afl_environment_variables) / sizeof(afl_environment_variables[0]) - 1; @@ -54,7 +54,6 @@ void write_setup_file(afl_state_t *afl, int argc, char **argv) { fprintf(f, "# command line:\n"); - s32 i; size_t j; for (i = 0; i < argc; i++) { -- cgit 1.4.1 From 0625eb0a051247c7b39df987289ad9a0e089a181 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Fri, 4 Sep 2020 22:26:39 +0200 Subject: avoid signed ints for amounts (which are positive) --- include/afl-fuzz.h | 2 +- src/afl-fuzz-stats.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 358cb5dc..1a05f4f4 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -945,7 +945,7 @@ void destroy_extras(afl_state_t *); /* Stats */ -void write_setup_file(afl_state_t *, int, char **); +void write_setup_file(afl_state_t *, u32, char **); void write_stats_file(afl_state_t *, double, double, double); void maybe_update_plot_file(afl_state_t *, double, double); void show_stats(afl_state_t *); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 05cbafef..07d83f07 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -29,20 +29,20 @@ /* Write fuzzer setup file */ -void write_setup_file(afl_state_t *afl, int argc, char **argv) { +void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { char *val; u8 fn[PATH_MAX]; snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); FILE *f = create_ffile(fn); - s32 i; + u32 i; fprintf(f, "# environment variables:\n"); - s32 s_afl_env = (s32) + u32 s_afl_env = sizeof(afl_environment_variables) / sizeof(afl_environment_variables[0]) - - 1; + 1U; - for (i = 0; i < s_afl_env; i++) { + for (i = 0; i < s_afl_env; ++i) { if ((val = getenv(afl_environment_variables[i])) != NULL) { @@ -55,7 +55,7 @@ void write_setup_file(afl_state_t *afl, int argc, char **argv) { fprintf(f, "# command line:\n"); size_t j; - for (i = 0; i < argc; i++) { + for (i = 0; i < argc; ++i) { if (i) fprintf(f, " "); if (index(argv[i], '\'')) { -- cgit 1.4.1 From 976ee9022cda95e0715b82ff866098ad293117c9 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 4 Sep 2020 22:47:37 +0200 Subject: fix assignment --- src/afl-fuzz-stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 07d83f07..51eed14b 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -38,7 +38,7 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { u32 i; fprintf(f, "# environment variables:\n"); - u32 s_afl_env = + u32 s_afl_env = (u32) sizeof(afl_environment_variables) / sizeof(afl_environment_variables[0]) - 1U; -- cgit 1.4.1 From 996986bed5f2dd97a3d76f584d8eddc1203f8396 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 5 Sep 2020 12:11:48 +0200 Subject: first batch of changes --- GNUmakefile.gcc_plugin | 167 +++ GNUmakefile.llvm | 499 +++++++ gcc_plugin/GNUmakefile | 170 --- gcc_plugin/Makefile | 159 -- gcc_plugin/README.instrument_list.md | 73 - gcc_plugin/README.md | 158 -- gcc_plugin/afl-gcc-fast.c | 406 ----- gcc_plugin/afl-gcc-pass.so.cc | 601 -------- gcc_plugin/afl-gcc-rt.o.c | 315 ---- instrumentation/LLVMInsTrim.so.cc | 598 ++++++++ instrumentation/MarkNodes.cc | 481 ++++++ instrumentation/MarkNodes.h | 12 + instrumentation/README.cmplog.md | 42 + instrumentation/README.ctx.md | 22 + instrumentation/README.gcc_plugin.md | 158 ++ instrumentation/README.instrim.md | 30 + instrumentation/README.instrument_list.md | 87 ++ instrumentation/README.laf-intel.md | 56 + instrumentation/README.llvm.md | 194 +++ instrumentation/README.lto.md | 290 ++++ instrumentation/README.neverzero.md | 35 + instrumentation/README.ngram.md | 28 + instrumentation/README.persistent_mode.md | 209 +++ instrumentation/README.snapshot.md | 16 + instrumentation/SanitizerCoverageLTO.so.cc | 1503 +++++++++++++++++++ instrumentation/afl-compiler-rt.o.c | 1254 ++++++++++++++++ instrumentation/afl-gcc-pass.so.cc | 601 ++++++++ instrumentation/afl-llvm-common.cc | 575 ++++++++ instrumentation/afl-llvm-common.h | 52 + instrumentation/afl-llvm-dict2file.so.cc | 599 ++++++++ instrumentation/afl-llvm-lto-instrumentation.so.cc | 957 ++++++++++++ instrumentation/afl-llvm-lto-instrumentlist.so.cc | 147 ++ instrumentation/afl-llvm-pass.so.cc | 654 +++++++++ instrumentation/afl-llvm-rt-lto.o.c | 27 + instrumentation/cmplog-instructions-pass.cc | 292 ++++ instrumentation/cmplog-routines-pass.cc | 212 +++ instrumentation/compare-transform-pass.so.cc | 587 ++++++++ instrumentation/llvm-ngram-coverage.h | 18 + instrumentation/split-compares-pass.so.cc | 1356 +++++++++++++++++ instrumentation/split-switches-pass.so.cc | 447 ++++++ llvm_mode/GNUmakefile | 480 ------ llvm_mode/LLVMInsTrim.so.cc | 598 -------- llvm_mode/Makefile | 2 - llvm_mode/MarkNodes.cc | 481 ------ llvm_mode/MarkNodes.h | 12 - llvm_mode/README.cmplog.md | 42 - llvm_mode/README.ctx.md | 22 - llvm_mode/README.instrim.md | 25 - llvm_mode/README.instrument_list.md | 86 -- llvm_mode/README.laf-intel.md | 42 - llvm_mode/README.lto.md | 293 ---- llvm_mode/README.md | 186 --- llvm_mode/README.neverzero.md | 35 - llvm_mode/README.ngram.md | 28 - llvm_mode/README.persistent_mode.md | 209 --- llvm_mode/README.snapshot.md | 16 - llvm_mode/SanitizerCoverageLTO.so.cc | 1503 ------------------- llvm_mode/afl-clang-fast.c | 1143 --------------- llvm_mode/afl-ld-lto.c | 358 ----- llvm_mode/afl-llvm-common.cc | 575 -------- llvm_mode/afl-llvm-common.h | 52 - llvm_mode/afl-llvm-lto-instrumentation.so.cc | 957 ------------ llvm_mode/afl-llvm-lto-instrumentlist.so.cc | 147 -- llvm_mode/afl-llvm-pass.so.cc | 654 --------- llvm_mode/afl-llvm-rt-lto.o.c | 27 - llvm_mode/afl-llvm-rt.o.c | 1244 ---------------- llvm_mode/cmplog-instructions-pass.cc | 292 ---- llvm_mode/cmplog-routines-pass.cc | 212 --- llvm_mode/compare-transform-pass.so.cc | 587 -------- llvm_mode/llvm-ngram-coverage.h | 18 - llvm_mode/split-compares-pass.so.cc | 1356 ----------------- llvm_mode/split-switches-pass.so.cc | 447 ------ src/afl-cc.c | 1544 ++++++++++++++++++++ src/afl-gcc.c | 488 ------- src/afl-ld-lto.c | 358 +++++ 75 files changed, 14107 insertions(+), 14499 deletions(-) create mode 100644 GNUmakefile.gcc_plugin create mode 100644 GNUmakefile.llvm delete mode 100644 gcc_plugin/GNUmakefile delete mode 100644 gcc_plugin/Makefile delete mode 100644 gcc_plugin/README.instrument_list.md delete mode 100644 gcc_plugin/README.md delete mode 100644 gcc_plugin/afl-gcc-fast.c delete mode 100644 gcc_plugin/afl-gcc-pass.so.cc delete mode 100644 gcc_plugin/afl-gcc-rt.o.c create mode 100644 instrumentation/LLVMInsTrim.so.cc create mode 100644 instrumentation/MarkNodes.cc create mode 100644 instrumentation/MarkNodes.h create mode 100644 instrumentation/README.cmplog.md create mode 100644 instrumentation/README.ctx.md create mode 100644 instrumentation/README.gcc_plugin.md create mode 100644 instrumentation/README.instrim.md create mode 100644 instrumentation/README.instrument_list.md create mode 100644 instrumentation/README.laf-intel.md create mode 100644 instrumentation/README.llvm.md create mode 100644 instrumentation/README.lto.md create mode 100644 instrumentation/README.neverzero.md create mode 100644 instrumentation/README.ngram.md create mode 100644 instrumentation/README.persistent_mode.md create mode 100644 instrumentation/README.snapshot.md create mode 100644 instrumentation/SanitizerCoverageLTO.so.cc create mode 100644 instrumentation/afl-compiler-rt.o.c create mode 100644 instrumentation/afl-gcc-pass.so.cc create mode 100644 instrumentation/afl-llvm-common.cc create mode 100644 instrumentation/afl-llvm-common.h create mode 100644 instrumentation/afl-llvm-dict2file.so.cc create mode 100644 instrumentation/afl-llvm-lto-instrumentation.so.cc create mode 100644 instrumentation/afl-llvm-lto-instrumentlist.so.cc create mode 100644 instrumentation/afl-llvm-pass.so.cc create mode 100644 instrumentation/afl-llvm-rt-lto.o.c create mode 100644 instrumentation/cmplog-instructions-pass.cc create mode 100644 instrumentation/cmplog-routines-pass.cc create mode 100644 instrumentation/compare-transform-pass.so.cc create mode 100644 instrumentation/llvm-ngram-coverage.h create mode 100644 instrumentation/split-compares-pass.so.cc create mode 100644 instrumentation/split-switches-pass.so.cc delete mode 100644 llvm_mode/GNUmakefile delete mode 100644 llvm_mode/LLVMInsTrim.so.cc delete mode 100644 llvm_mode/Makefile delete mode 100644 llvm_mode/MarkNodes.cc delete mode 100644 llvm_mode/MarkNodes.h delete mode 100644 llvm_mode/README.cmplog.md delete mode 100644 llvm_mode/README.ctx.md delete mode 100644 llvm_mode/README.instrim.md delete mode 100644 llvm_mode/README.instrument_list.md delete mode 100644 llvm_mode/README.laf-intel.md delete mode 100644 llvm_mode/README.lto.md delete mode 100644 llvm_mode/README.md delete mode 100644 llvm_mode/README.neverzero.md delete mode 100644 llvm_mode/README.ngram.md delete mode 100644 llvm_mode/README.persistent_mode.md delete mode 100644 llvm_mode/README.snapshot.md delete mode 100644 llvm_mode/SanitizerCoverageLTO.so.cc delete mode 100644 llvm_mode/afl-clang-fast.c delete mode 100644 llvm_mode/afl-ld-lto.c delete mode 100644 llvm_mode/afl-llvm-common.cc delete mode 100644 llvm_mode/afl-llvm-common.h delete mode 100644 llvm_mode/afl-llvm-lto-instrumentation.so.cc delete mode 100644 llvm_mode/afl-llvm-lto-instrumentlist.so.cc delete mode 100644 llvm_mode/afl-llvm-pass.so.cc delete mode 100644 llvm_mode/afl-llvm-rt-lto.o.c delete mode 100644 llvm_mode/afl-llvm-rt.o.c delete mode 100644 llvm_mode/cmplog-instructions-pass.cc delete mode 100644 llvm_mode/cmplog-routines-pass.cc delete mode 100644 llvm_mode/compare-transform-pass.so.cc delete mode 100644 llvm_mode/llvm-ngram-coverage.h delete mode 100644 llvm_mode/split-compares-pass.so.cc delete mode 100644 llvm_mode/split-switches-pass.so.cc create mode 100644 src/afl-cc.c delete mode 100644 src/afl-gcc.c create mode 100644 src/afl-ld-lto.c (limited to 'src') diff --git a/GNUmakefile.gcc_plugin b/GNUmakefile.gcc_plugin new file mode 100644 index 00000000..aeb1ef16 --- /dev/null +++ b/GNUmakefile.gcc_plugin @@ -0,0 +1,167 @@ +# +# american fuzzy lop++ - GCC plugin instrumentation +# ----------------------------------------------- +# +# Written by Austin Seipp and +# Laszlo Szekeres and +# Michal Zalewski and +# Heiko Eißfeldt +# +# GCC integration design is based on the LLVM design, which comes +# from Laszlo Szekeres. +# +# Copyright 2015 Google Inc. All rights reserved. +# Copyright 2019-2020 AFLplusplus Project. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +PREFIX ?= /usr/local +HELPER_PATH ?= $(PREFIX)/lib/afl +BIN_PATH ?= $(PREFIX)/bin +DOC_PATH ?= $(PREFIX)/share/doc/afl +MAN_PATH ?= $(PREFIX)/share/man/man8 + +VERSION = $(shell grep '^$(HASH)define VERSION ' ./config.h | cut -d '"' -f2) + +CFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2 +CFLAGS_SAFE := -Wall -Iinclude -Wno-pointer-sign \ + -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ + -DGCC_VERSION=\"$(GCCVER)\" -DGCC_BINDIR=\"$(GCCBINDIR)\" \ + -Wno-unused-function +override CFLAGS += $(CFLAGS_SAFE) + +CXXFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2 +CXXEFLAGS := $(CXXFLAGS) -Wall + +CC ?= gcc +CXX ?= g++ + +ifeq "clang" "$(CC)" + CC = gcc + CXX = g++ +endif + +ifeq "clang++" "$(CXX)" + CC = gcc + CXX = g++ +endif + +PLUGIN_FLAGS = -fPIC -fno-rtti -I"$(shell $(CC) -print-file-name=plugin)/include" +HASH=\# + +GCCVER = $(shell $(CC) --version 2>/dev/null | awk 'NR == 1 {print $$NF}') +GCCBINDIR = $(shell dirname `command -v $(CC)` 2>/dev/null ) + +ifeq "$(shell echo '$(HASH)include @$(HASH)include @int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" + SHMAT_OK=1 +else + SHMAT_OK=0 + override CFLAGS += -DUSEMMAP=1 +endif + +ifeq "$(TEST_MMAP)" "1" + SHMAT_OK=0 + override CFLAGS += -DUSEMMAP=1 +endif + +ifneq "$(shell uname -s)" "Haiku" + LDFLAGS += -lrt +else + CFLAGS_SAFE += -DUSEMMAP=1 +endif + +ifeq "$(shell uname -s)" "SunOS" + PLUGIN_FLAGS += -I/usr/include/gmp +endif + + +PROGS = ./afl-gcc-pass.so + +.PHONY: all +all: test_shm test_deps $(PROGS) test_build all_done + +.PHONY: test_shm +ifeq "$(SHMAT_OK)" "1" +test_shm: + @echo "[+] shmat seems to be working." + @rm -f .test2 +else +test_shm: + @echo "[-] shmat seems not to be working, switching to mmap implementation" +endif + +.PHONY: test_deps +test_deps: + @echo "[*] Checking for working '$(CC)'..." + @type $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 ) +# @echo "[*] Checking for gcc for plugin support..." +# @$(CC) -v 2>&1 | grep -q -- --enable-plugin || ( echo "[-] Oops, this gcc has not been configured with plugin support."; exit 1 ) + @echo "[*] Checking for gcc plugin development header files..." + @test -d `$(CC) -print-file-name=plugin`/include || ( echo "[-] Oops, can't find gcc header files. Be sure to install 'gcc-X-plugin-dev'."; exit 1 ) + @echo "[*] Checking for './afl-showmap'..." + @test -f ./afl-showmap || ( echo "[-] Oops, can't find './afl-showmap'. Be sure to compile AFL first."; exit 1 ) + @echo "[+] All set and ready to build." + +afl-common.o: ./src/afl-common.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(LDFLAGS) + +./afl-gcc-pass.so: instrumentation/afl-gcc-pass.so.cc | test_deps + $(CXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared $< -o $@ + ln -sf afl-cc afl-gcc-fast + ln -sf afl-cc afl-g++-fast + ln -sf afl-cc.8 afl-gcc-fast.8 + ln -sf afl-cc.8 afl-g++-fast.8 + +.PHONY: test_build +test_build: $(PROGS) + @echo "[*] Testing the CC wrapper and instrumentation output..." + unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ./afl-gcc-fast $(CFLAGS) $(CPPFLAGS) ./test-instr.c -o test-instr $(LDFLAGS) + ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr ./$@ + @echo .SH NAME >> ./$@ + @echo .B $* >> ./$@ + @echo >> ./$@ + @echo .SH SYNOPSIS >> ./$@ + @./$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ./$@ + @echo >> ./$@ + @echo .SH OPTIONS >> ./$@ + @echo .nf >> ./$@ + @./$* -h 2>&1 | tail -n +4 >> ./$@ + @echo >> ./$@ + @echo .SH AUTHOR >> ./$@ + @echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse , Heiko \"hexcoder-\" Eissfeldt , Andrea Fioraldi and Dominik Maier " >> ./$@ + @echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@ + @echo >> ./$@ + @echo .SH LICENSE >> ./$@ + @echo Apache License Version 2.0, January 2004 >> ./$@ + ln -sf afl-cc.8 ./afl-g++-fast.8 + +.PHONY: install +install: all + ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-gcc-fast + ln -sf afl-c++ $${DESTDIR}$(BIN_PATH)/afl-g++-fast + install -m 755 ./afl-gcc-pass.so $${DESTDIR}$(HELPER_PATH) + install -m 644 -T instrumentation/README.gcc_plugin.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md + +.PHONY: clean +clean: + rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2 + rm -f $(PROGS) afl-common.o ./afl-g++-fast ./afl-g*-fast.8 instrumentation/*.o diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm new file mode 100644 index 00000000..d4502319 --- /dev/null +++ b/GNUmakefile.llvm @@ -0,0 +1,499 @@ +# american fuzzy lop++ - LLVM instrumentation +# ----------------------------------------- +# +# Written by Laszlo Szekeres and +# Michal Zalewski +# +# LLVM integration design comes from Laszlo Szekeres. +# +# Copyright 2015, 2016 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +# For Heiko: +#TEST_MMAP=1 +HASH=\# + +PREFIX ?= /usr/local +HELPER_PATH ?= $(PREFIX)/lib/afl +BIN_PATH ?= $(PREFIX)/bin +DOC_PATH ?= $(PREFIX)/share/doc/afl +MISC_PATH ?= $(PREFIX)/share/afl +MAN_PATH ?= $(PREFIX)/share/man/man8 + +BUILD_DATE ?= $(shell date -u -d "@$(SOURCE_DATE_EPOCH)" "+%Y-%m-%d" 2>/dev/null || date -u -r "$(SOURCE_DATE_EPOCH)" "+%Y-%m-%d" 2>/dev/null || date -u "+%Y-%m-%d") + +VERSION = $(shell grep '^$(HASH)define VERSION ' ./config.h | cut -d '"' -f2) + +ifeq "$(shell uname)" "OpenBSD" + LLVM_CONFIG ?= $(BIN_PATH)/llvm-config + HAS_OPT = $(shell test -x $(BIN_PATH)/opt && echo 0 || echo 1) + ifeq "$(HAS_OPT)" "1" + $(error llvm_mode needs a complete llvm installation (versions 3.4 up to 12) -> e.g. "pkg_add llvm-7.0.1p9") + endif +else + LLVM_CONFIG ?= llvm-config +endif + +LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/git//' ) +LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//' ) +LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\.[0-3]|^19' && echo 1 || echo 0 ) +LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[0-9]' && echo 1 || echo 0 ) +LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-9]' && echo 1 || echo 0 ) +LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//') +LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null) +LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null) +LLVM_STDCXX = gnu++11 +LLVM_APPLE_XCODE = $(shell clang -v 2>&1 | grep -q Apple && echo 1 || echo 0) +LLVM_LTO = 0 + +ifeq "$(LLVMVER)" "" + $(warning [!] llvm_mode needs llvm-config, which was not found) +endif + +ifeq "$(LLVM_UNSUPPORTED)" "1" + $(warning llvm_mode only supports llvm versions 3.4 up to 12) +endif + +LLVM_TOO_OLD=1 + +ifeq "$(LLVM_MAJOR)" "9" + $(info [+] llvm_mode detected llvm 9, enabling neverZero implementation) + LLVM_TOO_OLD=0 +endif + +ifeq "$(LLVM_NEW_API)" "1" + $(info [+] llvm_mode detected llvm 10+, enabling neverZero implementation and c++14) + LLVM_STDCXX = c++14 + LLVM_TOO_OLD=0 +endif + +ifeq "$(LLVM_TOO_OLD)" "1" + $(info [!] llvm_mode detected an old version of llvm, upgrade to at least 9 or preferable 11!) + $(shell sleep 1) +endif + +ifeq "$(LLVM_HAVE_LTO)" "1" + $(info [+] llvm_mode detected llvm 11+, enabling afl-lto LTO implementation) + LLVM_LTO = 1 + #TEST_MMAP = 1 +endif + +ifeq "$(LLVM_LTO)" "0" + $(info [+] llvm_mode detected llvm < 11, afl-lto LTO will not be build.) +endif + +ifeq "$(LLVM_APPLE_XCODE)" "1" + $(warning llvm_mode will not compile with Xcode clang...) +endif + +# We were using llvm-config --bindir to get the location of clang, but +# this seems to be busted on some distros, so using the one in $PATH is +# probably better. + +CC = $(LLVM_BINDIR)/clang +CXX = $(LLVM_BINDIR)/clang++ + +# llvm-config --bindir may not providing a valid path, so ... +ifeq "$(shell test -e $(CC) || echo 1 )" "1" + # however we must ensure that this is not a "CC=gcc make" + ifeq "$(shell command -v $(CC) 2> /dev/null)" "" + # we do not have a valid CC variable so we try alternatives + ifeq "$(shell test -e '$(BIN_DIR)/clang' && echo 1)" "1" + # we found one in the local install directory, lets use these + CC = $(BIN_DIR)/clang + else + # hope for the best + $(warning we have trouble finding clang - llvm-config is not helping us) + CC = clang + endif + endif +endif +# llvm-config --bindir may not providing a valid path, so ... +ifeq "$(shell test -e $(CXX) || echo 1 )" "1" + # however we must ensure that this is not a "CC=gcc make" + ifeq "$(shell command -v $(CXX) 2> /dev/null)" "" + # we do not have a valid CC variable so we try alternatives + ifeq "$(shell test -e '$(BIN_DIR)/clang++' && echo 1)" "1" + # we found one in the local install directory, lets use these + CXX = $(BIN_DIR)/clang++ + else + # hope for the best + $(warning we have trouble finding clang++ - llvm-config is not helping us) + CXX = clang++ + endif + endif +endif + +# sanity check. +# Are versions of clang --version and llvm-config --version equal? +CLANGVER = $(shell $(CC) --version | sed -E -ne '/^.*version\ (1?[0-9]\.[0-9]\.[0-9]).*/s//\1/p') + +# I disable this because it does not make sense with what we did before (marc) +# We did exactly set these 26 lines above with these values, and it would break +# "CC=gcc make" etc. usages +ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" + CC_SAVE := $(LLVM_BINDIR)/clang +else + CC_SAVE := $(CC) +endif +ifeq "$(findstring clang, $(shell $(CXX) --version 2>/dev/null))" "" + CXX_SAVE := $(LLVM_BINDIR)/clang++ +else + CXX_SAVE := $(CXX) +endif + +CLANG_BIN := $(CC_SAVE) +CLANGPP_BIN := $(CXX_SAVE) + +ifeq "$(CC_SAVE)" "$(LLVM_BINDIR)/clang" + USE_BINDIR = 1 +else + ifeq "$(CXX_SAVE)" "$(LLVM_BINDIR)/clang++" + USE_BINDIR = 1 + else + USE_BINDIR = 0 + endif +endif + +# On old platform we cannot compile with clang because std++ libraries are too +# old. For these we need to use gcc/g++, so if we find REAL_CC and REAL_CXX +# variable we override the compiler variables here +ifneq "$(REAL_CC)" "" +CC = $(REAL_CC) +endif +ifneq "$(REAL_CXX)" "" +CXX = $(REAL_CXX) +endif + +# After we set CC/CXX we can start makefile magic tests + +#ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" +# CFLAGS_OPT = -march=native +#endif + +ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + AFL_CLANG_FLTO ?= -flto=full +else + ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + AFL_CLANG_FLTO ?= -flto=thin + else + ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + AFL_CLANG_FLTO ?= -flto + endif + endif +endif + +ifeq "$(LLVM_LTO)" "1" + ifneq "$(AFL_CLANG_FLTO)" "" + ifeq "$(AFL_REAL_LD)" "" + ifneq "$(shell readlink $(LLVM_BINDIR)/ld.lld 2>&1)" "" + AFL_REAL_LD = $(LLVM_BINDIR)/ld.lld + else + $(warn ld.lld not found, cannot enable LTO mode) + LLVM_LTO = 0 + endif + endif + else + $(warn clang option -flto is not working - maybe LLVMgold.so not found - cannot enable LTO mode) + LLVM_LTO = 0 + endif +endif + +AFL_CLANG_FUSELD= +ifeq "$(LLVM_LTO)" "1" + ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=`command -v ld` -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + AFL_CLANG_FUSELD=1 + ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=ld.lld --ld-path=$(LLVM_BINDIR)/ld.lld -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + AFL_CLANG_LDPATH=1 + endif + else + $(warn -fuse-ld is not working, cannot enable LTO mode) + LLVM_LTO = 0 + endif +endif + +ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fdebug-prefix-map=$(CURDIR)=llvm_mode -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + AFL_CLANG_DEBUG_PREFIX = -fdebug-prefix-map="$(CURDIR)=llvm_mode" +else + AFL_CLANG_DEBUG_PREFIX = "" +endif + +CFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2 +CFLAGS_SAFE := -Wall -g -Wno-pointer-sign -I ./include/ -I ./instrumentation/ \ + -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ + -DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \ + -DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \ + -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \ + -DAFL_REAL_LD=\"$(AFL_REAL_LD)\" \ + -DAFL_CLANG_LDPATH=\"$(AFL_CLANG_LDPATH)\" \ + -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \ + -DCLANG_BIN=\"$(CLANG_BIN)\" -DCLANGPP_BIN=\"$(CLANGPP_BIN)\" -DUSE_BINDIR=$(USE_BINDIR) -Wno-unused-function \ + $(AFL_CLANG_DEBUG_PREFIX) +override CFLAGS += $(CFLAGS_SAFE) + +ifdef AFL_TRACE_PC + $(info Compile option AFL_TRACE_PC is deprecated, just set AFL_LLVM_INSTRUMENT=PCGUARD to activate when compiling targets ) +endif + +CXXFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2 +override CXXFLAGS += -Wall -g -I ./include/ \ + -DVERSION=\"$(VERSION)\" -Wno-variadic-macros + +ifneq "$(shell $(LLVM_CONFIG) --includedir) 2> /dev/null" "" + CLANG_CFL = -I$(shell $(LLVM_CONFIG) --includedir) +endif +ifneq "$(LLVM_CONFIG)" "" + CLANG_CFL += -I$(shell dirname $(LLVM_CONFIG))/../include +endif +CLANG_CPPFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC $(CXXFLAGS) -Wno-deprecated-declarations +CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS) + + +# User teor2345 reports that this is required to make things work on MacOS X. +ifeq "$(shell uname)" "Darwin" + CLANG_LFL += -Wl,-flat_namespace -Wl,-undefined,suppress +else + CLANG_CPPFL += -Wl,-znodelete +endif + +ifeq "$(shell uname)" "OpenBSD" + CLANG_LFL += `$(LLVM_CONFIG) --libdir`/libLLVM.so + CLANG_CPPFL += -mno-retpoline + CFLAGS += -mno-retpoline + # Needed for unwind symbols + LDFLAGS += -lc++abi +endif + +ifeq "$(shell echo '$(HASH)include @$(HASH)include @int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" + SHMAT_OK=1 +else + SHMAT_OK=0 + #CFLAGS+=-DUSEMMAP=1 + LDFLAGS += -Wno-deprecated-declarations +endif + +ifeq "$(TEST_MMAP)" "1" + SHMAT_OK=0 + CFLAGS+=-DUSEMMAP=1 + LDFLAGS += -Wno-deprecated-declarations +endif + +PROGS_ALWAYS = ./afl-cc ./afl-compiler-rt.o ./afl-compiler-rt-32.o ./afl-compiler-rt-64.o +PROGS = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./libLLVMInsTrim.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./afl-llvm-lto-instrumentation.so ./SanitizerCoverageLTO.so + +# If prerequisites are not given, warn, do not build anything, and exit with code 0 +ifeq "$(LLVMVER)" "" + NO_BUILD = 1 +endif + +ifneq "$(LLVM_UNSUPPORTED)$(LLVM_APPLE_XCODE)" "00" + NO_BUILD = 1 +endif + +ifeq "$(NO_BUILD)" "1" + TARGETS = test_shm $(PROGS_ALWAYS) afl-cc.8 +else + TARGETS = test_shm test_deps $(PROGS) afl-cc.8 test_build all_done +endif + +LLVM_MIN_4_0_1 = $(shell awk 'function tonum(ver, a) {split(ver,a,"."); return a[1]*1000000+a[2]*1000+a[3]} BEGIN { exit tonum(ARGV[1]) >= tonum(ARGV[2]) }' $(LLVMVER) 4.0.1; echo $$?) + +.PHONY: all +all: $(TARGETS) + +.PHONY: test_shm +ifeq "$(SHMAT_OK)" "1" +test_shm: + @echo "[+] shmat seems to be working." + @rm -f .test2 +else +test_shm: + @echo "[-] shmat seems not to be working, switching to mmap implementation" +endif + +.PHONY: no_build +no_build: + @printf "%b\\n" "\\033[0;31mPrerequisites are not met, skipping build llvm_mode\\033[0m" + +.PHONY: test_deps +test_deps: + @echo "[*] Checking for working 'llvm-config'..." + ifneq "$(LLVM_APPLE_XCODE)" "1" + @type $(LLVM_CONFIG) >/dev/null 2>&1 || ( echo "[-] Oops, can't find 'llvm-config'. Install clang or set \$$LLVM_CONFIG or \$$PATH beforehand."; echo " (Sometimes, the binary will be named llvm-config-3.5 or something like that.)"; exit 1 ) + endif + @echo "[*] Checking for working '$(CC)'..." + @type $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 ) + @echo "[*] Checking for matching versions of '$(CC)' and '$(LLVM_CONFIG)'" +ifneq "$(CLANGVER)" "$(LLVMVER)" + @echo "[!] WARNING: we have llvm-config version $(LLVMVER) and a clang version $(CLANGVER)" +else + @echo "[*] We have llvm-config version $(LLVMVER) with a clang version $(CLANGVER), good." +endif + @echo "[*] Checking for './afl-showmap'..." + @test -f ./afl-showmap || ( echo "[-] Oops, can't find './afl-showmap'. Be sure to compile AFL first."; exit 1 ) + @echo "[+] All set and ready to build." + +instrumentation/afl-common.o: ./src/afl-common.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(LDFLAGS) + +./afl-cc: src/afl-cc.c instrumentation/afl-common.o | test_deps + $(CC) $(CLANG_CFL) $(CFLAGS) $(CPPFLAGS) $< instrumentation/afl-common.o -o $@ -DLLVM_MAJOR=$(LLVM_MAJOR) $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\" + @ln -sf afl-cc ./afl-c++ + @ln -sf afl-cc ./afl-gcc + @ln -sf afl-cc ./afl-g++ + @ln -sf afl-cc ./afl-clang-fast + @ln -sf afl-cc ./afl-clang-fast++ +ifneq "$(AFL_CLANG_FLTO)" "" +ifeq "$(LLVM_LTO)" "1" + @ln -sf afl-cc ./afl-clang-lto + @ln -sf afl-cc ./afl-clang-lto++ + @ln -sf afl-cc ./afl-lto + @ln -sf afl-cc ./afl-lto++ +endif +endif + +instrumentation/afl-llvm-common.o: instrumentation/afl-llvm-common.cc instrumentation/afl-llvm-common.h + $(CXX) $(CFLAGS) $(CPPFLAGS) `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC -std=$(LLVM_STDCXX) -c $< -o $@ + +./libLLVMInsTrim.so: instrumentation/LLVMInsTrim.so.cc instrumentation/MarkNodes.cc instrumentation/afl-llvm-common.o | test_deps + -$(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< instrumentation/MarkNodes.cc -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o + +./afl-llvm-pass.so: instrumentation/afl-llvm-pass.so.cc instrumentation/afl-llvm-common.o | test_deps +ifeq "$(LLVM_MIN_4_0_1)" "0" + $(info [!] N-gram branch coverage instrumentation is not available for llvm version $(LLVMVER)) +endif + $(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o + +./afl-llvm-lto-instrumentlist.so: instrumentation/afl-llvm-lto-instrumentlist.so.cc instrumentation/afl-llvm-common.o +ifeq "$(LLVM_LTO)" "1" + $(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o +endif + +./afl-ld-lto: src/afl-ld-lto.c +ifeq "$(LLVM_LTO)" "1" + $(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@ +endif + +./SanitizerCoverageLTO.so: instrumentation/SanitizerCoverageLTO.so.cc +ifeq "$(LLVM_LTO)" "1" + $(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o +endif + +./afl-llvm-lto-instrumentation.so: instrumentation/afl-llvm-lto-instrumentation.so.cc instrumentation/afl-llvm-common.o +ifeq "$(LLVM_LTO)" "1" + $(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o + $(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -fPIC -c instrumentation/afl-llvm-rt-lto.o.c -o ./afl-llvm-rt-lto.o + @$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m64 -fPIC -c instrumentation/afl-llvm-rt-lto.o.c -o ./afl-llvm-rt-lto-64.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi + @$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m32 -fPIC -c instrumentation/afl-llvm-rt-lto.o.c -o ./afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi +endif + +# laf +./split-switches-pass.so: instrumentation/split-switches-pass.so.cc instrumentation/afl-llvm-common.o | test_deps + $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o +./compare-transform-pass.so: instrumentation/compare-transform-pass.so.cc instrumentation/afl-llvm-common.o | test_deps + $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o +./split-compares-pass.so: instrumentation/split-compares-pass.so.cc instrumentation/afl-llvm-common.o | test_deps + $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o +# /laf + +./cmplog-routines-pass.so: instrumentation/cmplog-routines-pass.cc instrumentation/afl-llvm-common.o | test_deps + $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o + +./cmplog-instructions-pass.so: instrumentation/cmplog-instructions-pass.cc instrumentation/afl-llvm-common.o | test_deps + $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o + +afl-llvm-dict2file.so: instrumentation/afl-llvm-dict2file.so.cc instrumentation/afl-llvm-common.o | test_deps + $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o + +.PHONY: document +document: + $(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -fPIC -c instrumentation/afl-compiler-rt.o.c -o ./afl-compiler-rt.o + @$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -m32 -fPIC -c instrumentation/afl-compiler-rt.o.c -o ./afl-compiler-rt-32.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + @$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -m64 -fPIC -c instrumentation/afl-compiler-rt.o.c -o ./afl-compiler-rt-64.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + +./afl-compiler-rt.o: instrumentation/afl-compiler-rt.o.c | test_deps + $(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -fPIC -c $< -o $@ + ln -sf ./afl-compiler-rt.o ./afl-llvm-rt.o + +./afl-compiler-rt-32.o: instrumentation/afl-compiler-rt.o.c | test_deps + @printf "[*] Building 32-bit variant of the runtime (-m32)... " + @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + @test -e ./afl-compiler-rt-32.o && ln -sf ./afl-compiler-rt-32.o ./afl-llvm-rt-32.o + +./afl-compiler-rt-64.o: instrumentation/afl-compiler-rt.o.c | test_deps + @printf "[*] Building 64-bit variant of the runtime (-m64)... " + @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + @test -e ./afl-compiler-rt-64.o && ln -sf ./afl-compiler-rt-64.o ./afl-llvm-rt-64.o + +.PHONY: test_build +test_build: $(PROGS) + @echo "[*] Testing the CC wrapper and instrumentation output..." + unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_LLVM_LAF_ALL=1 ./afl-cc $(CFLAGS) $(CPPFLAGS) ./test-instr.c -o test-instr $(LDFLAGS) + ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null + echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr + @rm -f test-instr + @cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi + @echo "[+] All right, the instrumentation seems to be working!" + +.PHONY: all_done +all_done: test_build + @echo "[+] All done! You can now use './afl-cc' to compile programs." + +.NOTPARALLEL: clean + +.PHONY: install +install: all + @install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH) + @if [ -f ./afl-cc ]; then set -e; install -m 755 ./afl-cc $${DESTDIR}$(BIN_PATH); ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-c++; fi + @if [ -f ./afl-compiler-rt.o ]; then set -e; install -m 755 ./afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH); fi + @if [ -f ./afl-lto ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto++; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ./afl-llvm-lto-instrumentation.so ./afl-llvm-rt-lto*.o ./afl-llvm-lto-instrumentlist.so $${DESTDIR}$(HELPER_PATH); fi + @if [ -f ./afl-ld-lto ]; then set -e; install -m 755 ./afl-ld-lto $${DESTDIR}$(BIN_PATH); fi + @if [ -f ./afl-compiler-rt-32.o ]; then set -e; install -m 755 ./afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-32.o; fi + @if [ -f ./afl-compiler-rt-64.o ]; then set -e; install -m 755 ./afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-64.o; fi + @if [ -f ./compare-transform-pass.so ]; then set -e; install -m 755 ./*.so $${DESTDIR}$(HELPER_PATH); fi + @if [ -f ./compare-transform-pass.so ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-fast ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang-fast++ ; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang++ ; fi + @if [ -f ./SanitizerCoverageLTO.so ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang-lto++ ; fi + set -e; install -m 644 ./dynamic_list.txt $${DESTDIR}$(HELPER_PATH) + install -m 644 instrumentation/README.*.md $${DESTDIR}$(DOC_PATH)/ + +vpath % .. +%.8: % + @echo .TH $* 8 $(BUILD_DATE) "afl++" > ./$@ + @echo .SH NAME >> ./$@ + @printf ".B $* \- " >> ../$@ + @./$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> ../$@ + @echo .B $* >> ./$@ + @echo >> ./$@ + @echo .SH SYNOPSIS >> ./$@ + @./$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ./$@ + @echo >> ./$@ + @echo .SH OPTIONS >> ./$@ + @echo .nf >> ./$@ + @./$* -h 2>&1 | tail -n +4 >> ./$@ + @echo >> ./$@ + @echo .SH AUTHOR >> ./$@ + @echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse , Heiko \"hexcoder-\" Eissfeldt , Andrea Fioraldi and Dominik Maier " >> ./$@ + @echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@ + @echo >> ./$@ + @echo .SH LICENSE >> ./$@ + @echo Apache License Version 2.0, January 2004 >> ./$@ + @ln -sf afl-cc.8 ./afl-c++.8 +ifneq "$(AFL_CLANG_FLTO)" "" +ifeq "$(LLVM_LTO)" "1" + @ln -sf afl-cc.8 ./afl-clang-lto.8 + @ln -sf afl-cc.8 ./afl-clang-lto++.8 + @ln -sf afl-cc.8 ./afl-lto.8 + @ln -sf afl-cc.8 ./afl-lto++.8 +endif +endif + +.PHONY: clean +clean: + rm -f *.o *.so *~ a.out core core.[1-9][0-9]* .test2 test-instr .test-instr0 .test-instr1 *.dwo + rm -f $(PROGS) afl-common.o ./afl-c++ ./afl-lto ./afl-lto++ ./afl-clang-lto* ./afl-clang-fast* ./afl-clang*.8 ./ld ./afl-ld ./afl-llvm-rt*.o instrumentation/*.o diff --git a/gcc_plugin/GNUmakefile b/gcc_plugin/GNUmakefile deleted file mode 100644 index 625b55fb..00000000 --- a/gcc_plugin/GNUmakefile +++ /dev/null @@ -1,170 +0,0 @@ -# -# american fuzzy lop++ - GCC plugin instrumentation -# ----------------------------------------------- -# -# Written by Austin Seipp and -# Laszlo Szekeres and -# Michal Zalewski and -# Heiko Eißfeldt -# -# GCC integration design is based on the LLVM design, which comes -# from Laszlo Szekeres. -# -# Copyright 2015 Google Inc. All rights reserved. -# Copyright 2019-2020 AFLplusplus Project. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -PREFIX ?= /usr/local -HELPER_PATH ?= $(PREFIX)/lib/afl -BIN_PATH ?= $(PREFIX)/bin -DOC_PATH ?= $(PREFIX)/share/doc/afl -MAN_PATH ?= $(PREFIX)/share/man/man8 - -VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2) - -CFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2 -CFLAGS_SAFE := -Wall -I../include -Wno-pointer-sign \ - -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ - -DGCC_VERSION=\"$(GCCVER)\" -DGCC_BINDIR=\"$(GCCBINDIR)\" \ - -Wno-unused-function -override CFLAGS += $(CFLAGS_SAFE) - -CXXFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2 -CXXEFLAGS := $(CXXFLAGS) -Wall - -CC ?= gcc -CXX ?= g++ - -ifeq "clang" "$(CC)" - CC = gcc - CXX = g++ -endif - -ifeq "clang++" "$(CXX)" - CC = gcc - CXX = g++ -endif - -PLUGIN_FLAGS = -fPIC -fno-rtti -I"$(shell $(CC) -print-file-name=plugin)/include" -HASH=\# - -GCCVER = $(shell $(CC) --version 2>/dev/null | awk 'NR == 1 {print $$NF}') -GCCBINDIR = $(shell dirname `command -v $(CC)` 2>/dev/null ) - -ifeq "$(shell echo '$(HASH)include @$(HASH)include @int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" - SHMAT_OK=1 -else - SHMAT_OK=0 - override CFLAGS += -DUSEMMAP=1 -endif - -ifeq "$(TEST_MMAP)" "1" - SHMAT_OK=0 - override CFLAGS += -DUSEMMAP=1 -endif - -ifneq "$(shell uname -s)" "Haiku" - LDFLAGS += -lrt -else - CFLAGS_SAFE += -DUSEMMAP=1 -endif - -ifeq "$(shell uname -s)" "SunOS" - PLUGIN_FLAGS += -I/usr/include/gmp -endif - - -PROGS = ../afl-gcc-fast ../afl-gcc-pass.so ../afl-gcc-rt.o - - -all: test_shm test_deps $(PROGS) afl-gcc-fast.8 test_build all_done - -ifeq "$(SHMAT_OK)" "1" - -test_shm: - @echo "[+] shmat seems to be working." - @rm -f .test2 - -else - -test_shm: - @echo "[-] shmat seems not to be working, switching to mmap implementation" - -endif - - -test_deps: - @echo "[*] Checking for working '$(CC)'..." - @type $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 ) -# @echo "[*] Checking for gcc for plugin support..." -# @$(CC) -v 2>&1 | grep -q -- --enable-plugin || ( echo "[-] Oops, this gcc has not been configured with plugin support."; exit 1 ) - @echo "[*] Checking for gcc plugin development header files..." - @test -d `$(CC) -print-file-name=plugin`/include || ( echo "[-] Oops, can't find gcc header files. Be sure to install 'gcc-X-plugin-dev'."; exit 1 ) - @echo "[*] Checking for '../afl-showmap'..." - @test -f ../afl-showmap || ( echo "[-] Oops, can't find '../afl-showmap'. Be sure to compile AFL first."; exit 1 ) - @echo "[+] All set and ready to build." - -afl-common.o: ../src/afl-common.c - $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(LDFLAGS) - -../afl-gcc-fast: afl-gcc-fast.c afl-common.o | test_deps - $(CC) -DAFL_GCC_CC=\"$(CC)\" -DAFL_GCC_CXX=\"$(CXX)\" $(CFLAGS) $(CPPFLAGS) $< afl-common.o -o $@ $(LDFLAGS) - ln -sf afl-gcc-fast ../afl-g++-fast - -../afl-gcc-pass.so: afl-gcc-pass.so.cc | test_deps - $(CXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared $< -o $@ - -../afl-gcc-rt.o: afl-gcc-rt.o.c | test_deps - $(CC) $(CFLAGS_SAFE) $(CPPFLAGS) -fPIC -c $< -o $@ - -test_build: $(PROGS) - @echo "[*] Testing the CC wrapper and instrumentation output..." - unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-gcc-fast $(CFLAGS) $(CPPFLAGS) ../test-instr.c -o test-instr $(LDFLAGS) -# unset AFL_USE_ASAN AFL_USE_MSAN; AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-gcc-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS) - ASAN_OPTIONS=detect_leaks=0 ../afl-showmap -m none -q -o .test-instr0 ./test-instr ../$@ - @echo .SH NAME >> ../$@ - @echo .B $* >> ../$@ - @echo >> ../$@ - @echo .SH SYNOPSIS >> ../$@ - @../$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ../$@ - @echo >> ../$@ - @echo .SH OPTIONS >> ../$@ - @echo .nf >> ../$@ - @../$* -h 2>&1 | tail -n +4 >> ../$@ - @echo >> ../$@ - @echo .SH AUTHOR >> ../$@ - @echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse , Heiko \"hexcoder-\" Eissfeldt , Andrea Fioraldi and Dominik Maier " >> ../$@ - @echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ../$@ - @echo >> ../$@ - @echo .SH LICENSE >> ../$@ - @echo Apache License Version 2.0, January 2004 >> ../$@ - ln -sf afl-gcc-fast.8 ../afl-g++-fast.8 - -install: all - install -m 755 ../afl-gcc-fast $${DESTDIR}$(BIN_PATH) - install -m 755 ../afl-gcc-pass.so ../afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH) - install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md - install -m 644 -T README.instrument_list.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md - -clean: - rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2 - rm -f $(PROGS) afl-common.o ../afl-g++-fast ../afl-g*-fast.8 diff --git a/gcc_plugin/Makefile b/gcc_plugin/Makefile deleted file mode 100644 index 23477e22..00000000 --- a/gcc_plugin/Makefile +++ /dev/null @@ -1,159 +0,0 @@ -# -# american fuzzy lop++ - GCC plugin instrumentation -# ----------------------------------------------- -# -# Written by Austin Seipp and -# Laszlo Szekeres and -# Michal Zalewski and -# Heiko Eißfeldt -# -# GCC integration design is based on the LLVM design, which comes -# from Laszlo Szekeres. -# -# Copyright 2015 Google Inc. All rights reserved. -# Copyright 2019-2020 AFLplusplus Project. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -PREFIX ?= /usr/local -HELPER_PATH ?= $(PREFIX)/lib/afl -BIN_PATH ?= $(PREFIX)/bin -DOC_PATH ?= $(PREFIX)/share/doc/afl -MAN_PATH ?= $(PREFIX)/share/man/man8 - -VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2) -VERSION:sh= grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2 - -CFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2 -CFLAGS = -Wall -I../include -Wno-pointer-sign \ - -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ - -DGCC_VERSION=\"$(GCCVER)\" -DGCC_BINDIR=\"$(GCCBINDIR)\" \ - -Wno-unused-function - -CXXFLAGS = -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2 -CXXEFLAGS = $(CXXFLAGS) -Wall - -CC = gcc -CXX = g++ - -MYCC=$(CC:clang=gcc) -MYCXX=$(CXX:clang++=g++) - -PLUGIN_PATH = $(shell $(MYCC) -print-file-name=plugin) -PLUGIN_PATH:sh= $(MYCC) -print-file-name=plugin -PLUGIN_FLAGS = -fPIC -fno-rtti -I"$(PLUGIN_PATH)/include" -HASH=\# - -GCCVER = $(shell $(MYCC) --version 2>/dev/null | awk 'NR == 1 {print $$NF}') -GCCVER:sh= gcc --version 2>/dev/null | awk 'NR == 1 {print $$NF}' -GCCBINDIR = $(shell dirname `command -v $(MYCC)` 2>/dev/null ) -GCCBINDIR:sh= dirname `command -v $(MYCC)` 2>/dev/null - -_SHMAT_OK= $(shell echo '$(HASH)include @$(HASH)include @int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(MYCC) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 ) -_SHMAT_OK:sh= echo '$(HASH)include @$(HASH)include @int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(MYCC) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 - -IGNORE_MMAP=$(TEST_MMAP:1=0) -__SHMAT_OK=$(_SHMAT_OK)$(IGNORE_MMAP) -___SHMAT_OK=$(__SHMAT_OK:10=0) -SHMAT_OK=$(___SHMAT_OK:1=1) -_CFLAGS_ADD=$(SHMAT_OK:1=) -CFLAGS_ADD=$(_CFLAGS_ADD:0=-DUSEMMAP=1) - -_LDFLAGS_ADD=$(SHMAT_OK:1=) -LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-lrt) - -CFLAGS += $(CFLAGS_ADD) -LDFLAGS += $(LDFLAGS_ADD) - -PROGS = ../afl-gcc-pass.so ../afl-gcc-fast ../afl-gcc-rt.o - -all: test_shm test_deps $(PROGS) ../afl-gcc-fast.8 test_build all_done - -debug: - @echo _SHMAT_OK = $(_SHMAT_OK) - @echo IGNORE_MMAP = $(IGNORE_MMAP) - @echo __SHMAT_OK = $(__SHMAT_OK) - @echo ___SHMAT_OK = $(___SHMAT_OK) - @echo SHMAT_OK = $(SHMAT_OK) - -test_shm: - @if [ "$(SHMAT_OK)" == "1" ]; then \ - echo "[+] shmat seems to be working."; \ - rm -f .test2; \ - else \ - echo "[-] shmat seems not to be working, switching to mmap implementation"; \ - fi - -test_deps: - @echo "[*] Checking for working '$(MYCC)'..." - @type $(MYCC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(MYCC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 ) -# @echo "[*] Checking for gcc for plugin support..." -# @$(MYCC) -v 2>&1 | grep -q -- --enable-plugin || ( echo "[-] Oops, this gcc has not been configured with plugin support."; exit 1 ) - @echo "[*] Checking for gcc plugin development header files..." - @test -d `$(MYCC) -print-file-name=plugin`/include || ( echo "[-] Oops, can't find gcc header files. Be sure to install 'gcc-X-plugin-dev'."; exit 1 ) - @echo "[*] Checking for '../afl-showmap'..." - @test -f ../afl-showmap || ( echo "[-] Oops, can't find '../afl-showmap'. Be sure to compile AFL first."; exit 1 ) - @echo "[+] All set and ready to build." - -afl-common.o: ../src/afl-common.c - $(MYCC) $(CFLAGS) -c $< -o $@ $(LDFLAGS) - -../afl-gcc-fast: afl-gcc-fast.c afl-common.o - $(MYCC) -DAFL_GCC_CC=\"$(MYCC)\" -DAFL_GCC_CXX=\"$(MYCXX)\" $(CFLAGS) afl-gcc-fast.c afl-common.o -o $@ $(LDFLAGS) - ln -sf afl-gcc-fast ../afl-g++-fast - -../afl-gcc-pass.so: afl-gcc-pass.so.cc - $(MYCXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared afl-gcc-pass.so.cc -o $@ - -../afl-gcc-rt.o: afl-gcc-rt.o.c - $(MYCC) $(CFLAGS) -fPIC -c afl-gcc-rt.o.c -o $@ - -test_build: $(PROGS) - @echo "[*] Testing the CC wrapper and instrumentation output..." - @unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-gcc-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS) -# unset AFL_USE_ASAN AFL_USE_MSAN; AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-gcc-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS) - @ASAN_OPTIONS=detect_leaks=0 ../afl-showmap -m none -q -o .test-instr0 ./test-instr ../$@ - @echo .SH NAME >> ../$@ - @echo .B $* >> ../$@ - @echo >> ../$@ - @echo .SH SYNOPSIS >> ../$@ - @../$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ../$@ - @echo >> ../$@ - @echo .SH OPTIONS >> ../$@ - @echo .nf >> ../$@ - @../$* -h 2>&1 | tail -n +4 >> ../$@ - @echo >> ../$@ - @echo .SH AUTHOR >> ../$@ - @echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse , Heiko \"hexcoder-\" Eissfeldt , Andrea Fioraldi and Dominik Maier " >> ../$@ - @echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ../$@ - @echo >> ../$@ - @echo .SH LICENSE >> ../$@ - @echo Apache License Version 2.0, January 2004 >> ../$@ - ln -sf afl-gcc-fast.8 ../afl-g++-fast.8 - -install: all - install -m 755 ../afl-gcc-fast $${DESTDIR}$(BIN_PATH) - install -m 755 ../afl-gcc-pass.so ../afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH) - install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md - install -m 644 -T README.instrument_list.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md - -clean: - rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2 - rm -f $(PROGS) afl-common.o ../afl-g++-fast ../afl-g*-fast.8 diff --git a/gcc_plugin/README.instrument_list.md b/gcc_plugin/README.instrument_list.md deleted file mode 100644 index d0eaf6ff..00000000 --- a/gcc_plugin/README.instrument_list.md +++ /dev/null @@ -1,73 +0,0 @@ -======================================== -Using afl++ with partial instrumentation -======================================== - - This file describes how you can selectively instrument only the source files - that are interesting to you using the gcc instrumentation provided by - afl++. - - Plugin by hexcoder-. - - -## 1) Description and purpose - -When building and testing complex programs where only a part of the program is -the fuzzing target, it often helps to only instrument the necessary parts of -the program, leaving the rest uninstrumented. This helps to focus the fuzzer -on the important parts of the program, avoiding undesired noise and -disturbance by uninteresting code being exercised. - -For this purpose, I have added a "partial instrumentation" support to the gcc -plugin of AFLFuzz that allows you to specify on a source file level which files -should be compiled with or without instrumentation. - - -## 2) Building the gcc plugin - -The new code is part of the existing afl++ gcc plugin in the gcc_plugin/ -subdirectory. There is nothing specifically to do :) - - -## 3) How to use the partial instrumentation mode - -In order to build with partial instrumentation, you need to build with -afl-gcc-fast and afl-g++-fast respectively. The only required change is -that you need to set the environment variable AFL_GCC_INSTRUMENT_FILE when calling -the compiler. - -The environment variable must point to a file containing all the filenames -that should be instrumented. For matching, the filename that is being compiled -must end in the filename entry contained in this instrument list (to avoid breaking -the matching when absolute paths are used during compilation). - -For example if your source tree looks like this: - -``` -project/ -project/feature_a/a1.cpp -project/feature_a/a2.cpp -project/feature_b/b1.cpp -project/feature_b/b2.cpp -``` - -and you only want to test feature_a, then create a instrument list file containing: - -``` -feature_a/a1.cpp -feature_a/a2.cpp -``` - -However if the instrument list file contains only this, it works as well: - -``` -a1.cpp -a2.cpp -``` - -but it might lead to files being unwantedly instrumented if the same filename -exists somewhere else in the project directories. - -The created instrument list file is then set to AFL_GCC_INSTRUMENT_FILE when you compile -your program. For each file that didn't match the instrument list, the compiler will -issue a warning at the end stating that no blocks were instrumented. If you -didn't intend to instrument that file, then you can safely ignore that warning. diff --git a/gcc_plugin/README.md b/gcc_plugin/README.md deleted file mode 100644 index f762131e..00000000 --- a/gcc_plugin/README.md +++ /dev/null @@ -1,158 +0,0 @@ -# GCC-based instrumentation for afl-fuzz - - (See [../README.md](../README.md) for the general instruction manual.) - (See [../llvm_mode/README.md](../llvm_mode/README.md) for the LLVM-based instrumentation.) - -!!! TODO items are: -!!! => inline instrumentation has to work! -!!! - - -## 1) Introduction - -The code in this directory allows you to instrument programs for AFL using -true compiler-level instrumentation, instead of the more crude -assembly-level rewriting approach taken by afl-gcc and afl-clang. This has -several interesting properties: - - - The compiler can make many optimizations that are hard to pull off when - manually inserting assembly. As a result, some slow, CPU-bound programs will - run up to around faster. - - The gains are less pronounced for fast binaries, where the speed is limited - chiefly by the cost of creating new processes. In such cases, the gain will - probably stay within 10%. - - - The instrumentation is CPU-independent. At least in principle, you should - be able to rely on it to fuzz programs on non-x86 architectures (after - building afl-fuzz with AFL_NOX86=1). - - - Because the feature relies on the internals of GCC, it is gcc-specific - and will *not* work with LLVM (see ../llvm_mode for an alternative). - -Once this implementation is shown to be sufficiently robust and portable, it -will probably replace afl-gcc. For now, it can be built separately and -co-exists with the original code. - -The idea and much of the implementation comes from Laszlo Szekeres. - -## 2) How to use - -In order to leverage this mechanism, you need to have modern enough GCC -(>= version 4.5.0) and the plugin headers installed on your system. That -should be all you need. On Debian machines, these headers can be acquired by -installing the `gcc--plugin-dev` packages. - -To build the instrumentation itself, type 'make'. This will generate binaries -called afl-gcc-fast and afl-g++-fast in the parent directory. -If the CC/CXX have been overridden, those compilers will be used from -those wrappers without using AFL_CXX/AFL_CC settings. -Once this is done, you can instrument third-party code in a way similar to the -standard operating mode of AFL, e.g.: - - CC=/path/to/afl/afl-gcc-fast ./configure [...options...] - make - -Be sure to also include CXX set to afl-g++-fast for C++ code. - -The tool honors roughly the same environmental variables as afl-gcc (see -[env_variables.md](../docs/env_variables.md). This includes AFL_INST_RATIO, AFL_USE_ASAN, -AFL_HARDEN, and AFL_DONT_OPTIMIZE. - -Note: if you want the GCC plugin to be installed on your system for all -users, you need to build it before issuing 'make install' in the parent -directory. - -## 3) Gotchas, feedback, bugs - -This is an early-stage mechanism, so field reports are welcome. You can send bug -reports to . - -## 4) Bonus feature #1: deferred initialization - -AFL tries to optimize performance by executing the targeted binary just once, -stopping it just before main(), and then cloning this "main" process to get -a steady supply of targets to fuzz. - -Although this approach eliminates much of the OS-, linker- and libc-level -costs of executing the program, it does not always help with binaries that -perform other time-consuming initialization steps - say, parsing a large config -file before getting to the fuzzed data. - -In such cases, it's beneficial to initialize the forkserver a bit later, once -most of the initialization work is already done, but before the binary attempts -to read the fuzzed input and parse it; in some cases, this can offer a 10x+ -performance gain. You can implement delayed initialization in LLVM mode in a -fairly simple way. - -First, locate a suitable location in the code where the delayed cloning can -take place. This needs to be done with *extreme* care to avoid breaking the -binary. In particular, the program will probably malfunction if you select -a location after: - - - The creation of any vital threads or child processes - since the forkserver - can't clone them easily. - - - The initialization of timers via setitimer() or equivalent calls. - - - The creation of temporary files, network sockets, offset-sensitive file - descriptors, and similar shared-state resources - but only provided that - their state meaningfully influences the behavior of the program later on. - - - Any access to the fuzzed input, including reading the metadata about its - size. - -With the location selected, add this code in the appropriate spot: - -``` -#ifdef __AFL_HAVE_MANUAL_CONTROL - __AFL_INIT(); -#endif -``` - -You don't need the #ifdef guards, but they will make the program still work as -usual when compiled with a tool other than afl-gcc-fast/afl-clang-fast. - -Finally, recompile the program with afl-gcc-fast (afl-gcc or afl-clang will -*not* generate a deferred-initialization binary) - and you should be all set! - -## 5) Bonus feature #2: persistent mode - -Some libraries provide APIs that are stateless, or whose state can be reset in -between processing different input files. When such a reset is performed, a -single long-lived process can be reused to try out multiple test cases, -eliminating the need for repeated fork() calls and the associated OS overhead. - -The basic structure of the program that does this would be: - -``` - while (__AFL_LOOP(1000)) { - - /* Read input data. */ - /* Call library code to be fuzzed. */ - /* Reset state. */ - - } - - /* Exit normally */ -``` - -The numerical value specified within the loop controls the maximum number -of iterations before AFL will restart the process from scratch. This minimizes -the impact of memory leaks and similar glitches; 1000 is a good starting point. - -A more detailed template is shown in ../examples/persistent_demo/. -Similarly to the previous mode, the feature works only with afl-gcc-fast or -afl-clang-fast; #ifdef guards can be used to suppress it when using other -compilers. - -Note that as with the previous mode, the feature is easy to misuse; if you -do not reset the critical state fully, you may end up with false positives or -waste a whole lot of CPU power doing nothing useful at all. Be particularly -wary of memory leaks and the state of file descriptors. - -When running in this mode, the execution paths will inherently vary a bit -depending on whether the input loop is being entered for the first time or -executed again. To avoid spurious warnings, the feature implies -AFL_NO_VAR_CHECK and hides the "variable path" warnings in the UI. - diff --git a/gcc_plugin/afl-gcc-fast.c b/gcc_plugin/afl-gcc-fast.c deleted file mode 100644 index b1bacfbd..00000000 --- a/gcc_plugin/afl-gcc-fast.c +++ /dev/null @@ -1,406 +0,0 @@ -/* - american fuzzy lop++ - GCC wrapper for GCC plugin - ------------------------------------------------ - - Written by Austin Seipp and - Laszlo Szekeres and - Michal Zalewski - - GCC integration design is based on the LLVM design, which comes - from Laszlo Szekeres. - - Copyright 2015 Google Inc. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This program is a drop-in replacement for gcc, similar in most - respects to ../afl-gcc, but with compiler instrumentation through a - plugin. It tries to figure out compilation mode, adds a bunch of - flags, and then calls the real compiler. - - */ - -#define AFL_MAIN - -#include "config.h" -#include "types.h" -#include "debug.h" -#include "common.h" -#include "alloc-inl.h" - -#include -#include -#include -#include - -static u8 * obj_path; /* Path to runtime libraries */ -static u8 **cc_params; /* Parameters passed to the real CC */ -static u32 cc_par_cnt = 1; /* Param count, including argv0 */ -u8 use_stdin = 0; /* dummy */ - -/* Try to find the runtime libraries. If that fails, abort. */ - -static void find_obj(u8 *argv0) { - - u8 *afl_path = getenv("AFL_PATH"); - u8 *slash, *tmp; - - if (afl_path) { - - tmp = alloc_printf("%s/afl-gcc-rt.o", afl_path); - - if (!access(tmp, R_OK)) { - - obj_path = afl_path; - ck_free(tmp); - return; - - } - - ck_free(tmp); - - } - - slash = strrchr(argv0, '/'); - - if (slash) { - - u8 *dir; - - *slash = 0; - dir = ck_strdup(argv0); - *slash = '/'; - - tmp = alloc_printf("%s/afl-gcc-rt.o", dir); - - if (!access(tmp, R_OK)) { - - obj_path = dir; - ck_free(tmp); - return; - - } - - ck_free(tmp); - ck_free(dir); - - } - - if (!access(AFL_PATH "/afl-gcc-rt.o", R_OK)) { - - obj_path = AFL_PATH; - return; - - } - - FATAL( - "Unable to find 'afl-gcc-rt.o' or 'afl-gcc-pass.so'. Please set " - "AFL_PATH"); - -} - -/* Copy argv to cc_params, making the necessary edits. */ - -static void edit_params(u32 argc, char **argv) { - - u8 fortify_set = 0, asan_set = 0, x_set = 0, maybe_linking = 1; - u8 *name; - - cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); - - name = strrchr(argv[0], '/'); - if (!name) - name = argv[0]; - else - ++name; - - if (!strcmp(name, "afl-g++-fast")) { - - u8 *alt_cxx = getenv("AFL_CXX"); - cc_params[0] = alt_cxx && *alt_cxx ? alt_cxx : (u8 *)AFL_GCC_CXX; - - } else if (!strcmp(name, "afl-gcc-fast")) { - - u8 *alt_cc = getenv("AFL_CC"); - cc_params[0] = alt_cc && *alt_cc ? alt_cc : (u8 *)AFL_GCC_CC; - - } else { - - fprintf(stderr, "Name of the binary: %s\n", argv[0]); - FATAL( - "Name of the binary is not a known name, expected afl-(gcc|g++)-fast"); - - } - - char *fplugin_arg = alloc_printf("-fplugin=%s/afl-gcc-pass.so", obj_path); - cc_params[cc_par_cnt++] = fplugin_arg; - - /* Detect stray -v calls from ./configure scripts. */ - - if (argc == 1 && !strcmp(argv[1], "-v")) maybe_linking = 0; - - while (--argc) { - - u8 *cur = *(++argv); - -#if defined(__x86_64__) - if (!strcmp(cur, "-m32")) FATAL("-m32 is not supported"); -#endif - - if (!strcmp(cur, "-x")) x_set = 1; - - if (!strcmp(cur, "-c") || !strcmp(cur, "-S") || !strcmp(cur, "-E") || - !strcmp(cur, "-v")) - maybe_linking = 0; - - if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory")) - asan_set = 1; - - if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; - - if (!strcmp(cur, "-shared")) maybe_linking = 0; - - cc_params[cc_par_cnt++] = cur; - - } - - if (getenv("AFL_HARDEN")) { - - cc_params[cc_par_cnt++] = "-fstack-protector-all"; - - if (!fortify_set) cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2"; - - } - - if (!asan_set) { - - if (getenv("AFL_USE_ASAN")) { - - if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive"); - - if (getenv("AFL_HARDEN")) - FATAL("ASAN and AFL_HARDEN are mutually exclusive"); - - cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; - cc_params[cc_par_cnt++] = "-fsanitize=address"; - - } else if (getenv("AFL_USE_MSAN")) { - - if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive"); - - if (getenv("AFL_HARDEN")) - FATAL("MSAN and AFL_HARDEN are mutually exclusive"); - - cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; - cc_params[cc_par_cnt++] = "-fsanitize=memory"; - - } - - } - - if (getenv("AFL_USE_UBSAN")) { - - cc_params[cc_par_cnt++] = "-fsanitize=undefined"; - cc_params[cc_par_cnt++] = "-fsanitize-undefined-trap-on-error"; - cc_params[cc_par_cnt++] = "-fno-sanitize-recover=all"; - - } - - if (!getenv("AFL_DONT_OPTIMIZE")) { - - cc_params[cc_par_cnt++] = "-g"; - cc_params[cc_par_cnt++] = "-O3"; - cc_params[cc_par_cnt++] = "-funroll-loops"; - - } - - if (getenv("AFL_NO_BUILTIN")) { - - cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-bcmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strstr"; - cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr"; - - } - -#if defined(USEMMAP) && !defined(__HAIKU__) - cc_params[cc_par_cnt++] = "-lrt"; -#endif - - cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; - cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; - cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; - - /* When the user tries to use persistent or deferred forkserver modes by - appending a single line to the program, we want to reliably inject a - signature into the binary (to be picked up by afl-fuzz) and we want - to call a function from the runtime .o file. This is unnecessarily - painful for three reasons: - - 1) We need to convince the compiler not to optimize out the signature. - This is done with __attribute__((used)). - - 2) We need to convince the linker, when called with -Wl,--gc-sections, - not to do the same. This is done by forcing an assignment to a - 'volatile' pointer. - - 3) We need to declare __afl_persistent_loop() in the global namespace, - but doing this within a method in a class is hard - :: and extern "C" - are forbidden and __attribute__((alias(...))) doesn't work. Hence the - __asm__ aliasing trick. - - */ - - cc_params[cc_par_cnt++] = - "-D__AFL_LOOP(_A)=" - "({ static volatile char *_B __attribute__((used)); " - " _B = (char*)\"" PERSIST_SIG - "\"; " -#ifdef __APPLE__ - "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " -#else - "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " -#endif /* ^__APPLE__ */ - "_L(_A); })"; - - cc_params[cc_par_cnt++] = - "-D__AFL_INIT()=" - "do { static volatile char *_A __attribute__((used)); " - " _A = (char*)\"" DEFER_SIG - "\"; " -#ifdef __APPLE__ - "void _I(void) __asm__(\"___afl_manual_init\"); " -#else - "void _I(void) __asm__(\"__afl_manual_init\"); " -#endif /* ^__APPLE__ */ - "_I(); } while (0)"; - - if (maybe_linking) { - - if (x_set) { - - cc_params[cc_par_cnt++] = "-x"; - cc_params[cc_par_cnt++] = "none"; - - } - - cc_params[cc_par_cnt++] = alloc_printf("%s/afl-gcc-rt.o", obj_path); - - } - - cc_params[cc_par_cnt] = NULL; - -} - -/* Main entry point */ - -int main(int argc, char **argv, char **envp) { - - if (argc < 2 || strcmp(argv[1], "-h") == 0) { - - printf(cCYA - "afl-gcc-fast" VERSION cRST - " initially by , maintainer: hexcoder-\n" - "\n" - "afl-gcc-fast [options]\n" - "\n" - "This is a helper application for afl-fuzz. It serves as a drop-in " - "replacement\n" - "for gcc, letting you recompile third-party code with the required " - "runtime\n" - "instrumentation. A common use pattern would be one of the " - "following:\n\n" - - " CC=%s/afl-gcc-fast ./configure\n" - " CXX=%s/afl-g++-fast ./configure\n\n" - - "In contrast to the traditional afl-gcc tool, this version is " - "implemented as\n" - "a GCC plugin and tends to offer improved performance with slow " - "programs\n" - "(similarly to the LLVM plugin used by afl-clang-fast).\n\n" - - "Environment variables used:\n" - "AFL_CC: path to the C compiler to use\n" - "AFL_CXX: path to the C++ compiler to use\n" - "AFL_PATH: path to instrumenting pass and runtime (afl-gcc-rt.*o)\n" - "AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" - "AFL_NO_BUILTIN: compile for use with libtokencap.so\n" - "AFL_INST_RATIO: percentage of branches to instrument\n" - "AFL_QUIET: suppress verbose output\n" - "AFL_DEBUG: enable developer debugging output\n" - "AFL_HARDEN: adds code hardening to catch memory bugs\n" - "AFL_USE_ASAN: activate address sanitizer\n" - "AFL_USE_MSAN: activate memory sanitizer\n" - "AFL_USE_UBSAN: activate undefined behaviour sanitizer\n" - "AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by " - "filename\n" - - "\nafl-gcc-fast was built for gcc %s with the gcc binary path of " - "\"%s\".\n\n", - BIN_PATH, BIN_PATH, GCC_VERSION, GCC_BINDIR); - - exit(1); - - } else if ((isatty(2) && !getenv("AFL_QUIET")) || - - getenv("AFL_DEBUG") != NULL) { - - SAYF(cCYA "afl-gcc-fast" VERSION cRST - " initially by , maintainer: hexcoder-\n"); - - if (getenv("AFL_GCC_INSTRUMENT_FILE") == NULL && - getenv("AFL_GCC_WHITELIST") == NULL) { - - SAYF( - cYEL - "Warning:" cRST - " using afl-gcc-fast without using AFL_GCC_INSTRUMENT_FILE currently " - "produces worse results than afl-gcc. Even better, use " - "llvm_mode for now.\n"); - - } - - } else - - be_quiet = 1; - - u8 *ptr; - if (!be_quiet && - ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE")))) { - - u32 map_size = atoi(ptr); - if (map_size != MAP_SIZE) - WARNF("AFL_MAP_SIZE is not supported by afl-gcc-fast"); - - } - - check_environment_vars(envp); - - find_obj(argv[0]); - - edit_params(argc, argv); - /*if (isatty(2) && !getenv("AFL_QUIET")) { - - printf("Calling \"%s\" with:\n", cc_params[0]); - for(int i=1; i with bits from - Emese Revfy - - Fixed by Heiko Eißfeldt 2019-2020 for AFL++ - - GCC integration design is based on the LLVM design, which comes - from Laszlo Szekeres. Some of the boilerplate code below for - afl_pass to adapt to different GCC versions was taken from Emese - Revfy's Size Overflow plugin for GCC, licensed under the GPLv2/v3. - - (NOTE: this plugin code is under GPLv3, in order to comply with the - GCC runtime library exception, which states that you may distribute - "Target Code" from the compiler under a license of your choice, as - long as the "Compilation Process" is "Eligible", and contains no - GPL-incompatible software in GCC "during the process of - transforming high level code to target code". In this case, the - plugin will be used to generate "Target Code" during the - "Compilation Process", and thus it must be GPLv3 to be "eligible".) - - Copyright (C) 2015 Austin Seipp - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - */ - -#define BUILD_INLINE_INST - -#include "../include/config.h" -#include "../include/debug.h" - -/* clear helper macros AFL types pull in, which intervene with gcc-plugin - * headers from GCC-8 */ -#ifdef likely - #undef likely -#endif -#ifdef unlikely - #undef unlikely -#endif - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* -------------------------------------------------------------------------- */ -/* -- AFL instrumentation pass ---------------------------------------------- */ - -static int be_quiet = 0; -static unsigned int inst_ratio = 100; -static bool inst_ext = true; -static std::list myInstrumentList; - -static unsigned int ext_call_instrument(function *fun) { - - /* Instrument all the things! */ - basic_block bb; - unsigned finst_blocks = 0; - unsigned fcnt_blocks = 0; - - tree fntype = build_function_type_list(void_type_node, /* return */ - uint32_type_node, /* args */ - NULL_TREE); /* done */ - tree fndecl = build_fn_decl("__afl_trace", fntype); - TREE_STATIC(fndecl) = 1; /* Defined elsewhere */ - TREE_PUBLIC(fndecl) = 1; /* Public */ - DECL_EXTERNAL(fndecl) = 1; /* External linkage */ - DECL_ARTIFICIAL(fndecl) = 1; /* Injected by compiler */ - - FOR_EACH_BB_FN(bb, fun) { - - gimple_seq fcall; - gimple_seq seq = NULL; - gimple_stmt_iterator bentry; - ++fcnt_blocks; - - // only instrument if this basic block is the destination of a previous - // basic block that has multiple successors - // this gets rid of ~5-10% of instrumentations that are unnecessary - // result: a little more speed and less map pollution - - int more_than_one = -1; - edge ep; - edge_iterator eip; - - FOR_EACH_EDGE(ep, eip, bb->preds) { - - int count = 0; - if (more_than_one == -1) more_than_one = 0; - - basic_block Pred = ep->src; - edge es; - edge_iterator eis; - FOR_EACH_EDGE(es, eis, Pred->succs) { - - basic_block Succ = es->dest; - if (Succ != NULL) count++; - - } - - if (count > 1) more_than_one = 1; - - } - - if (more_than_one != 1) continue; - - /* Bail on this block if we trip the specified ratio */ - if (R(100) >= inst_ratio) continue; - - /* Make up cur_loc */ - unsigned int rand_loc = R(MAP_SIZE); - tree cur_loc = build_int_cst(uint32_type_node, rand_loc); - - /* Update bitmap via external call */ - /* to quote: - * /+ Trace a basic block with some ID +/ - * void __afl_trace(u32 x); - */ - - fcall = gimple_build_call( - fndecl, 1, - cur_loc); /* generate the function _call_ to above built reference, with - *1* parameter -> the random const for the location */ - gimple_seq_add_stmt(&seq, fcall); /* and insert into a sequence */ - - /* Done - grab the entry to the block and insert sequence */ - bentry = gsi_after_labels(bb); - gsi_insert_seq_before(&bentry, seq, GSI_SAME_STMT); - - ++finst_blocks; - - } - - /* Say something nice. */ - if (!be_quiet) { - - if (!finst_blocks) - WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST), - function_name(fun)); - else if (finst_blocks < fcnt_blocks) - OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST), - finst_blocks, fcnt_blocks, function_name(fun)); - else - OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST), finst_blocks, - function_name(fun)); - - } - - return 0; - -} - -static unsigned int inline_instrument(function *fun) { - - /* Instrument all the things! */ - basic_block bb; - unsigned finst_blocks = 0; - unsigned fcnt_blocks = 0; - tree one = build_int_cst(unsigned_char_type_node, 1); - // tree zero = build_int_cst(unsigned_char_type_node, 0); - - /* Set up global type declarations */ - tree map_type = build_pointer_type(unsigned_char_type_node); - tree map_ptr_g = - build_decl(UNKNOWN_LOCATION, VAR_DECL, - get_identifier_with_length("__afl_area_ptr", 14), map_type); - TREE_USED(map_ptr_g) = 1; - TREE_STATIC(map_ptr_g) = 1; /* Defined elsewhere */ - DECL_EXTERNAL(map_ptr_g) = 1; /* External linkage */ - DECL_PRESERVE_P(map_ptr_g) = 1; - DECL_ARTIFICIAL(map_ptr_g) = 1; /* Injected by compiler */ - rest_of_decl_compilation(map_ptr_g, 1, 0); - - tree prev_loc_g = build_decl(UNKNOWN_LOCATION, VAR_DECL, - get_identifier_with_length("__afl_prev_loc", 14), - uint32_type_node); - TREE_USED(prev_loc_g) = 1; - TREE_STATIC(prev_loc_g) = 1; /* Defined elsewhere */ - DECL_EXTERNAL(prev_loc_g) = 1; /* External linkage */ - DECL_PRESERVE_P(prev_loc_g) = 1; - DECL_ARTIFICIAL(prev_loc_g) = 1; /* Injected by compiler */ - set_decl_tls_model(prev_loc_g, TLS_MODEL_REAL); /* TLS attribute */ - rest_of_decl_compilation(prev_loc_g, 1, 0); - - FOR_EACH_BB_FN(bb, fun) { - - gimple_seq seq = NULL; - gimple_stmt_iterator bentry; - ++fcnt_blocks; - - // only instrument if this basic block is the destination of a previous - // basic block that has multiple successors - // this gets rid of ~5-10% of instrumentations that are unnecessary - // result: a little more speed and less map pollution - - int more_than_one = -1; - edge ep; - edge_iterator eip; - FOR_EACH_EDGE(ep, eip, bb->preds) { - - int count = 0; - if (more_than_one == -1) more_than_one = 0; - - basic_block Pred = ep->src; - edge es; - edge_iterator eis; - FOR_EACH_EDGE(es, eis, Pred->succs) { - - basic_block Succ = es->dest; - if (Succ != NULL) count++; - - } - - if (count > 1) more_than_one = 1; - - } - - if (more_than_one != 1) continue; - - /* Bail on this block if we trip the specified ratio */ - if (R(100) >= inst_ratio) continue; - - /* Make up cur_loc */ - - unsigned int rand_loc = R(MAP_SIZE); - tree cur_loc = build_int_cst(uint32_type_node, rand_loc); - - /* Load prev_loc, xor with cur_loc */ - // gimple_assign - tree prev_loc = create_tmp_var_raw(uint32_type_node, "prev_loc"); - gassign *g = gimple_build_assign(prev_loc, VAR_DECL, prev_loc_g); - gimple_seq_add_stmt(&seq, g); // load prev_loc - update_stmt(g); - - // gimple_assign - tree area_off = create_tmp_var_raw(uint32_type_node, "area_off"); - g = gimple_build_assign(area_off, BIT_XOR_EXPR, prev_loc, cur_loc); - gimple_seq_add_stmt(&seq, g); // area_off = prev_loc ^ cur_loc - update_stmt(g); - - /* Update bitmap */ - - // gimple_assign - tree map_ptr = create_tmp_var(map_type, "map_ptr"); - tree map_ptr2 = create_tmp_var(map_type, "map_ptr2"); - - g = gimple_build_assign(map_ptr, map_ptr_g); - gimple_seq_add_stmt(&seq, g); // map_ptr = __afl_area_ptr - update_stmt(g); - -#if 1 - #if 0 - tree addr = build2(ADDR_EXPR, map_type, map_ptr, area_off); - g = gimple_build_assign(map_ptr2, MODIFY_EXPR, addr); - gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off - update_stmt(g); - #else - g = gimple_build_assign(map_ptr2, PLUS_EXPR, map_ptr, area_off); - gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off - update_stmt(g); - #endif - - // gimple_assign - tree tmp1 = create_tmp_var_raw(unsigned_char_type_node, "tmp1"); - g = gimple_build_assign(tmp1, MEM_REF, map_ptr2); - gimple_seq_add_stmt(&seq, g); // tmp1 = *map_ptr2 - update_stmt(g); -#else - tree atIndex = build2(PLUS_EXPR, uint32_type_node, map_ptr, area_off); - tree array_address = build1(ADDR_EXPR, map_type, atIndex); - tree array_access = build1(INDIRECT_REF, map_type, array_address); - tree tmp1 = create_tmp_var(unsigned_char_type_node, "tmp1"); - g = gimple_build_assign(tmp1, array_access); - gimple_seq_add_stmt(&seq, g); // tmp1 = *(map_ptr + area_off) - update_stmt(g); -#endif - // gimple_assign - tree tmp2 = create_tmp_var_raw(unsigned_char_type_node, "tmp2"); - g = gimple_build_assign(tmp2, PLUS_EXPR, tmp1, one); - gimple_seq_add_stmt(&seq, g); // tmp2 = tmp1 + 1 - update_stmt(g); - - // TODO: neverZero: here we have to check if tmp3 == 0 - // and add 1 if so - - // gimple_assign - // tree map_ptr3 = create_tmp_var_raw(map_type, "map_ptr3"); - g = gimple_build_assign(map_ptr2, INDIRECT_REF, tmp2); - gimple_seq_add_stmt(&seq, g); // *map_ptr2 = tmp2 - update_stmt(g); - - /* Set prev_loc to cur_loc >> 1 */ - - // gimple_assign - tree shifted_loc = build_int_cst(TREE_TYPE(prev_loc_g), rand_loc >> 1); - tree prev_loc2 = create_tmp_var_raw(uint32_type_node, "prev_loc2"); - g = gimple_build_assign(prev_loc2, shifted_loc); - gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1 - update_stmt(g); - g = gimple_build_assign(prev_loc_g, prev_loc2); - gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1 - update_stmt(g); - - /* Done - grab the entry to the block and insert sequence */ - - bentry = gsi_after_labels(bb); - gsi_insert_seq_before(&bentry, seq, GSI_NEW_STMT); - - ++finst_blocks; - - } - - /* Say something nice. */ - if (!be_quiet) { - - if (!finst_blocks) - WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST), - function_name(fun)); - else if (finst_blocks < fcnt_blocks) - OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST), - finst_blocks, fcnt_blocks, function_name(fun)); - else - OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST), finst_blocks, - function_name(fun)); - - } - - return 0; - -} - -/* -------------------------------------------------------------------------- */ -/* -- Boilerplate and initialization ---------------------------------------- */ - -static const struct pass_data afl_pass_data = { - - .type = GIMPLE_PASS, - .name = "afl-inst", - .optinfo_flags = OPTGROUP_NONE, - - .tv_id = TV_NONE, - .properties_required = 0, - .properties_provided = 0, - .properties_destroyed = 0, - .todo_flags_start = 0, - // NOTE(aseipp): it's very, very important to include - // at least 'TODO_update_ssa' here so that GCC will - // properly update the resulting SSA form, e.g., to - // include new PHI nodes for newly added symbols or - // names. Do not remove this. Do not taunt Happy Fun - // Ball. - .todo_flags_finish = TODO_update_ssa | TODO_verify_il | TODO_cleanup_cfg, - -}; - -namespace { - -class afl_pass : public gimple_opt_pass { - - private: - bool do_ext_call; - - public: - afl_pass(bool ext_call, gcc::context *g) - : gimple_opt_pass(afl_pass_data, g), do_ext_call(ext_call) { - - } - - unsigned int execute(function *fun) override { - - if (!myInstrumentList.empty()) { - - bool instrumentBlock = false; - std::string instFilename; - unsigned int instLine = 0; - - /* EXPR_FILENAME - This macro returns the name of the file in which the entity was declared, - as a char*. For an entity declared implicitly by the compiler (like - __builtin_ memcpy), this will be the string "". - */ - const char *fname = DECL_SOURCE_FILE(fun->decl); - - if (0 != strncmp("", fname, 10) && - 0 != strncmp("", fname, 10)) { - - instFilename = fname; - instLine = DECL_SOURCE_LINE(fun->decl); - - /* Continue only if we know where we actually are */ - if (!instFilename.empty()) { - - for (std::list::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. */ - if (instFilename.length() >= it->length()) { - - if (instFilename.compare(instFilename.length() - it->length(), - it->length(), *it) == 0) { - - instrumentBlock = true; - break; - - } - - } - - } - - } - - } - - /* Either we couldn't figure out our location or the location is - * not in the instrument list, so we skip instrumentation. */ - if (!instrumentBlock) { - - if (!be_quiet) { - - if (!instFilename.empty()) - SAYF(cYEL "[!] " cBRI - "Not in instrument list, skipping %s line %u...\n", - instFilename.c_str(), instLine); - else - SAYF(cYEL "[!] " cBRI "No filename information found, skipping it"); - - } - - return 0; - - } - - } - - return do_ext_call ? ext_call_instrument(fun) : inline_instrument(fun); - - } - -}; /* class afl_pass */ - -} // namespace - -static struct opt_pass *make_afl_pass(bool ext_call, gcc::context *ctxt) { - - return new afl_pass(ext_call, ctxt); - -} - -/* -------------------------------------------------------------------------- */ -/* -- Initialization -------------------------------------------------------- */ - -int plugin_is_GPL_compatible = 1; - -static struct plugin_info afl_plugin_info = { - - .version = "20200519", - .help = "AFL++ gcc plugin\n", - -}; - -int plugin_init(struct plugin_name_args * plugin_info, - struct plugin_gcc_version *version) { - - struct register_pass_info afl_pass_info; - struct timeval tv; - struct timezone tz; - u32 rand_seed; - - /* Setup random() so we get Actually Random(TM) outputs from R() */ - gettimeofday(&tv, &tz); - rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); - SR(rand_seed); - - /* Pass information */ - afl_pass_info.pass = make_afl_pass(inst_ext, g); - afl_pass_info.reference_pass_name = "ssa"; - afl_pass_info.ref_pass_instance_number = 1; - afl_pass_info.pos_op = PASS_POS_INSERT_AFTER; - - if (!plugin_default_version_check(version, &gcc_version)) { - - FATAL(G_("Incompatible gcc/plugin versions! Expected GCC %d.%d"), - GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR); - - } - - /* Show a banner */ - if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { - - SAYF(G_(cCYA "afl-gcc-pass" VERSION cRST - " initially by , maintainer: hexcoder-\n")); - - } else - - be_quiet = 1; - - /* Decide instrumentation ratio */ - char *inst_ratio_str = getenv("AFL_INST_RATIO"); - - if (inst_ratio_str) { - - if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || - inst_ratio > 100) - FATAL(G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)")); - else { - - if (!be_quiet) - ACTF(G_("%s instrumentation at ratio of %u%% in %s mode."), - inst_ext ? G_("Call-based") : G_("Inline"), inst_ratio, - getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened")); - - } - - } - - char *instInstrumentListFilename = getenv("AFL_GCC_INSTRUMENT_FILE"); - if (!instInstrumentListFilename) - instInstrumentListFilename = getenv("AFL_GCC_WHITELIST"); - if (instInstrumentListFilename) { - - std::string line; - std::ifstream fileStream; - fileStream.open(instInstrumentListFilename); - if (!fileStream) PFATAL("Unable to open AFL_GCC_INSTRUMENT_FILE"); - getline(fileStream, line); - while (fileStream) { - - myInstrumentList.push_back(line); - getline(fileStream, line); - - } - - } else if (!be_quiet && (getenv("AFL_LLVM_WHITELIST") || - - getenv("AFL_LLVM_INSTRUMENT_FILE"))) { - - SAYF(cYEL "[-] " cRST - "AFL_LLVM_INSTRUMENT_FILE environment variable detected - did " - "you mean AFL_GCC_INSTRUMENT_FILE?\n"); - - } - - /* Go go gadget */ - register_callback(plugin_info->base_name, PLUGIN_INFO, NULL, - &afl_plugin_info); - register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, NULL, - &afl_pass_info); - return 0; - -} - diff --git a/gcc_plugin/afl-gcc-rt.o.c b/gcc_plugin/afl-gcc-rt.o.c deleted file mode 100644 index 49a03cae..00000000 --- a/gcc_plugin/afl-gcc-rt.o.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - american fuzzy lop++ - GCC plugin instrumentation bootstrap - --------------------------------------------------------- - - Written by Austin Seipp and - Laszlo Szekeres and - Michal Zalewski - - GCC integration design is based on the LLVM design, which comes - from Laszlo Szekeres. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This code is the rewrite of afl-as.h's main_payload. - -*/ - -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif -#include "../config.h" -#include "../types.h" - -#ifdef USEMMAP - #include -#endif -#include -#include -#include -#include -#include - -#include -#ifndef USEMMAP - #include -#endif -#include -#include - -#include -#include - -/* Globals needed by the injected instrumentation. The __afl_area_initial region - is used for instrumentation output before __afl_map_shm() has a chance to - run. It will end up as .comm, so it shouldn't be too wasteful. */ - -u8 __afl_area_initial[MAP_SIZE]; -u8 *__afl_area_ptr = __afl_area_initial; - -#ifdef __ANDROID__ -u32 __afl_prev_loc; -u32 __afl_final_loc; -#else -__thread u32 __afl_prev_loc; -__thread u32 __afl_final_loc; -#endif - -/* Trace a basic block with some ID */ -void __afl_trace(const u32 x) { - -#if 1 /* enable for neverZero feature. */ - __afl_area_ptr[__afl_prev_loc ^ x] += - 1 + ((u8)(1 + __afl_area_ptr[__afl_prev_loc ^ x]) == 0); -#else - ++__afl_area_ptr[__afl_prev_loc ^ x]; -#endif - - __afl_prev_loc = (x >> 1); - return; - -} - -/* Running in persistent mode? */ - -static u8 is_persistent; - -/* SHM setup. */ - -static void __afl_map_shm(void) { - - u8 *id_str = getenv(SHM_ENV_VAR); - - /* If we're running under AFL, attach to the appropriate region, replacing the - 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 (id_str) { - -#ifdef USEMMAP - const char * shm_file_path = id_str; - int shm_fd = -1; - unsigned char *shm_base = NULL; - - /* create the shared memory segment as if it was a file */ - shm_fd = shm_open(shm_file_path, O_RDWR, 0600); - if (shm_fd == -1) { - - fprintf(stderr, "shm_open() failed\n"); - exit(1); - - } - - /* map the shared memory segment to the address space of the process */ - shm_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); - if (shm_base == MAP_FAILED) { - - close(shm_fd); - shm_fd = -1; - - fprintf(stderr, "mmap() failed\n"); - exit(2); - - } - - __afl_area_ptr = shm_base; -#else - u32 shm_id = atoi(id_str); - - __afl_area_ptr = shmat(shm_id, NULL, 0); -#endif - - /* Whooooops. */ - - if (__afl_area_ptr == (void *)-1) exit(1); - - /* Write something into the bitmap so that even with low AFL_INST_RATIO, - our parent doesn't give up on us. */ - - __afl_area_ptr[0] = 1; - - } - -} - -/* Fork server logic. */ - -static void __afl_start_forkserver(void) { - - u8 tmp[4] = {0, 0, 0, 0}; - u32 map_size = MAP_SIZE; - s32 child_pid; - - u8 child_stopped = 0; - - void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL); - - /* Phone home and tell the parent that we're OK. If parent isn't there, - assume we're not running in forkserver mode and just execute program. */ - - if (MAP_SIZE <= 0x800000) { - - map_size = (FS_OPT_ENABLED | FS_OPT_MAPSIZE | FS_OPT_SET_MAPSIZE(MAP_SIZE)); - memcpy(tmp, &map_size, 4); - - } - - if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; - - while (1) { - - u32 was_killed; - int status; - - /* Wait for parent by reading from the pipe. Abort if read fails. */ - - if (read(FORKSRV_FD, &was_killed, 4) != 4) exit(1); - - /* If we stopped the child in persistent mode, but there was a race - condition and afl-fuzz already issued SIGKILL, write off the old - process. */ - - if (child_stopped && was_killed) { - - child_stopped = 0; - if (waitpid(child_pid, &status, 0) < 0) exit(1); - - } - - if (!child_stopped) { - - /* Once woken up, create a clone of our process. */ - - child_pid = fork(); - if (child_pid < 0) exit(1); - - /* In child process: close fds, resume execution. */ - - if (!child_pid) { - - signal(SIGCHLD, old_sigchld_handler); - - close(FORKSRV_FD); - close(FORKSRV_FD + 1); - return; - - } - - } else { - - /* Special handling for persistent mode: if the child is alive but - currently stopped, simply restart it with SIGCONT. */ - - kill(child_pid, SIGCONT); - child_stopped = 0; - - } - - /* In parent process: write PID to pipe, then wait for child. */ - - if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(1); - - if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0) exit(1); - - /* In persistent mode, the child stops itself with SIGSTOP to indicate - a successful run. In this case, we want to wake it up without forking - again. */ - - if (WIFSTOPPED(status)) child_stopped = 1; - - /* Relay wait status to pipe, then loop back. */ - - if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1); - - } - -} - -/* A simplified persistent mode handler, used as explained in README.md. */ - -int __afl_persistent_loop(unsigned int max_cnt) { - - static u8 first_pass = 1; - static u32 cycle_cnt; - - if (first_pass) { - - /* Make sure that every iteration of __AFL_LOOP() starts with a clean slate. - On subsequent calls, the parent will take care of that, but on the first - iteration, it's our job to erase any trace of whatever happened - before the loop. */ - - if (is_persistent) { - - memset(__afl_area_ptr, 0, MAP_SIZE); - __afl_area_ptr[0] = 1; - __afl_prev_loc = 0; - - } - - cycle_cnt = max_cnt; - first_pass = 0; - return 1; - - } - - if (is_persistent) { - - if (--cycle_cnt) { - - raise(SIGSTOP); - - __afl_area_ptr[0] = 1; - __afl_prev_loc = 0; - - return 1; - - } else { - - /* When exiting __AFL_LOOP(), make sure that the subsequent code that - follows the loop is not traced. We do that by pivoting back to the - dummy output region. */ - - __afl_area_ptr = __afl_area_initial; - - } - - } - - return 0; - -} - -/* This one can be called from user code when deferred forkserver mode - is enabled. */ - -void __afl_manual_init(void) { - - static u8 init_done; - - if (!init_done) { - - __afl_map_shm(); - __afl_start_forkserver(); - init_done = 1; - - } - -} - -/* Proper initialization routine. */ - -__attribute__((constructor(101))) void __afl_auto_init(void) { - - is_persistent = !!getenv(PERSIST_ENV_VAR); - - if (getenv(DEFER_ENV_VAR)) return; - - __afl_manual_init(); - -} - diff --git a/instrumentation/LLVMInsTrim.so.cc b/instrumentation/LLVMInsTrim.so.cc new file mode 100644 index 00000000..61a420ba --- /dev/null +++ b/instrumentation/LLVMInsTrim.so.cc @@ -0,0 +1,598 @@ +#include +#include +#include +#include + +#include "llvm/Config/llvm-config.h" +#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5 +typedef long double max_align_t; +#endif + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) + #include "llvm/IR/CFG.h" + #include "llvm/IR/Dominators.h" + #include "llvm/IR/DebugInfo.h" +#else + #include "llvm/Support/CFG.h" + #include "llvm/Analysis/Dominators.h" + #include "llvm/DebugInfo.h" +#endif +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/IR/BasicBlock.h" +#include +#include +#include +#include +#include + +#include "MarkNodes.h" +#include "afl-llvm-common.h" +#include "llvm-ngram-coverage.h" + +#include "config.h" +#include "debug.h" + +using namespace llvm; + +static cl::opt MarkSetOpt("markset", cl::desc("MarkSet"), + cl::init(false)); +static cl::opt LoopHeadOpt("loophead", cl::desc("LoopHead"), + cl::init(false)); + +namespace { + +struct InsTrim : public ModulePass { + + protected: + uint32_t function_minimum_size = 1; + char * skip_nozero = NULL; + + private: + std::mt19937 generator; + int total_instr = 0; + + unsigned int genLabel() { + + return generator() & (MAP_SIZE - 1); + + } + + public: + static char ID; + + InsTrim() : ModulePass(ID), generator(0) { + + initInstrumentList(); + + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + + AU.addRequired(); + + } + +#if LLVM_VERSION_MAJOR < 4 + const char * +#else + StringRef +#endif + getPassName() const override { + + return "InstTrim Instrumentation"; + + } + +#if LLVM_VERSION_MAJOR > 4 || \ + (LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1) + #define AFL_HAVE_VECTOR_INTRINSICS 1 +#endif + + bool runOnModule(Module &M) override { + + setvbuf(stdout, NULL, _IONBF, 0); + + if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { + + SAYF(cCYA "LLVMInsTrim" VERSION cRST " by csienslab\n"); + + } else + + be_quiet = 1; + + if (getenv("AFL_DEBUG") != NULL) debug = 1; + + LLVMContext &C = M.getContext(); + + IntegerType *Int8Ty = IntegerType::getInt8Ty(C); + IntegerType *Int32Ty = IntegerType::getInt32Ty(C); + +#if LLVM_VERSION_MAJOR < 9 + char *neverZero_counters_str; + if ((neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO")) != NULL) + if (!be_quiet) OKF("LLVM neverZero activated (by hexcoder)\n"); +#endif + skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); + + if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL || + getenv("LOOPHEAD") != NULL) { + + LoopHeadOpt = true; + + } + + unsigned int PrevLocSize = 0; + char * ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); + if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); + char *ctx_str = getenv("AFL_LLVM_CTX"); + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + unsigned int ngram_size = 0; + /* Decide previous location vector size (must be a power of two) */ + VectorType *PrevLocTy = NULL; + + if (ngram_size_str) + if (sscanf(ngram_size_str, "%u", &ngram_size) != 1 || ngram_size < 2 || + ngram_size > NGRAM_SIZE_MAX) + FATAL( + "Bad value of AFL_NGRAM_SIZE (must be between 2 and NGRAM_SIZE_MAX " + "(%u))", + NGRAM_SIZE_MAX); + + if (ngram_size) + PrevLocSize = ngram_size - 1; + else +#else + if (ngram_size_str) + #ifdef LLVM_VERSION_STRING + FATAL( + "Sorry, NGRAM branch coverage is not supported with llvm version %s!", + LLVM_VERSION_STRING); + #else + #ifndef LLVM_VERSION_PATCH + FATAL( + "Sorry, NGRAM branch coverage is not supported with llvm version " + "%d.%d.%d!", + LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0); + #else + FATAL( + "Sorry, NGRAM branch coverage is not supported with llvm version " + "%d.%d.%d!", + LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERISON_PATCH); + #endif + #endif +#endif + PrevLocSize = 1; + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + // IntegerType *Int64Ty = IntegerType::getInt64Ty(C); + int PrevLocVecSize = PowerOf2Ceil(PrevLocSize); + IntegerType *IntLocTy = + IntegerType::getIntNTy(C, sizeof(PREV_LOC_T) * CHAR_BIT); + if (ngram_size) + PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize + #if LLVM_VERSION_MAJOR >= 12 + , + false + #endif + ); +#endif + + /* Get globals for the SHM region and the previous location. Note that + __afl_prev_loc is thread-local. */ + + GlobalVariable *AFLMapPtr = + new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, + GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); + GlobalVariable *AFLPrevLoc; + GlobalVariable *AFLContext = NULL; + LoadInst * PrevCtx = NULL; // for CTX sensitive coverage + + if (ctx_str) +#ifdef __ANDROID__ + AFLContext = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx"); +#else + AFLContext = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx", + 0, GlobalVariable::GeneralDynamicTLSModel, 0, false); +#endif + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ngram_size) + #ifdef __ANDROID__ + AFLPrevLoc = new GlobalVariable( + M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage, + /* Initializer */ nullptr, "__afl_prev_loc"); + #else + AFLPrevLoc = new GlobalVariable( + M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage, + /* Initializer */ nullptr, "__afl_prev_loc", + /* InsertBefore */ nullptr, GlobalVariable::GeneralDynamicTLSModel, + /* AddressSpace */ 0, /* IsExternallyInitialized */ false); + #endif + else +#endif +#ifdef __ANDROID__ + AFLPrevLoc = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc"); +#else + AFLPrevLoc = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0, + GlobalVariable::GeneralDynamicTLSModel, 0, false); +#endif + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + /* Create the vector shuffle mask for updating the previous block history. + Note that the first element of the vector will store cur_loc, so just set + it to undef to allow the optimizer to do its thing. */ + + SmallVector PrevLocShuffle = {UndefValue::get(Int32Ty)}; + + for (unsigned I = 0; I < PrevLocSize - 1; ++I) + PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, I)); + + for (int I = PrevLocSize; I < PrevLocVecSize; ++I) + PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize)); + + Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle); +#endif + + // this is our default + MarkSetOpt = true; + + ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); + ConstantInt *One = ConstantInt::get(Int8Ty, 1); + + u64 total_rs = 0; + u64 total_hs = 0; + + scanForDangerousFunctions(&M); + + for (Function &F : M) { + + if (debug) { + + uint32_t bb_cnt = 0; + + for (auto &BB : F) + if (BB.size() > 0) ++bb_cnt; + SAYF(cMGN "[D] " cRST "Function %s size %zu %u\n", + F.getName().str().c_str(), F.size(), bb_cnt); + + } + + if (!isInInstrumentList(&F)) continue; + + // if the function below our minimum size skip it (1 or 2) + if (F.size() < function_minimum_size) { continue; } + + std::unordered_set MS; + if (!MarkSetOpt) { + + for (auto &BB : F) { + + MS.insert(&BB); + + } + + total_rs += F.size(); + + } else { + + auto Result = markNodes(&F); + auto RS = Result.first; + auto HS = Result.second; + + MS.insert(RS.begin(), RS.end()); + if (!LoopHeadOpt) { + + MS.insert(HS.begin(), HS.end()); + total_rs += MS.size(); + + } else { + + DenseSet> EdgeSet; + DominatorTreeWrapperPass * DTWP = + &getAnalysis(F); + auto DT = &DTWP->getDomTree(); + + total_rs += RS.size(); + total_hs += HS.size(); + + for (BasicBlock *BB : HS) { + + bool Inserted = false; + for (auto BI = pred_begin(BB), BE = pred_end(BB); BI != BE; ++BI) { + + auto Edge = BasicBlockEdge(*BI, BB); + if (Edge.isSingleEdge() && DT->dominates(Edge, BB)) { + + EdgeSet.insert({*BI, BB}); + Inserted = true; + break; + + } + + } + + if (!Inserted) { + + MS.insert(BB); + total_rs += 1; + total_hs -= 1; + + } + + } + + for (auto I = EdgeSet.begin(), E = EdgeSet.end(); I != E; ++I) { + + auto PredBB = I->first; + auto SuccBB = I->second; + auto NewBB = + SplitBlockPredecessors(SuccBB, {PredBB}, ".split", DT, nullptr, +#if LLVM_VERSION_MAJOR >= 8 + nullptr, +#endif + false); + MS.insert(NewBB); + + } + + } + + for (BasicBlock &BB : F) { + + if (MS.find(&BB) == MS.end()) { continue; } + IRBuilder<> IRB(&*BB.getFirstInsertionPt()); + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ngram_size) { + + LoadInst *PrevLoc = IRB.CreateLoad(AFLPrevLoc); + PrevLoc->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + Value *ShuffledPrevLoc = IRB.CreateShuffleVector( + PrevLoc, UndefValue::get(PrevLocTy), PrevLocShuffleMask); + Value *UpdatedPrevLoc = IRB.CreateInsertElement( + ShuffledPrevLoc, ConstantInt::get(Int32Ty, genLabel()), + (uint64_t)0); + + IRB.CreateStore(UpdatedPrevLoc, AFLPrevLoc) + ->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } else + +#endif + { + + IRB.CreateStore(ConstantInt::get(Int32Ty, genLabel()), AFLPrevLoc); + + } + + } + + } + + int has_calls = 0; + for (BasicBlock &BB : F) { + + auto PI = pred_begin(&BB); + auto PE = pred_end(&BB); + IRBuilder<> IRB(&*BB.getFirstInsertionPt()); + Value * L = NULL; + unsigned int cur_loc; + + // Context sensitive coverage + if (ctx_str && &BB == &F.getEntryBlock()) { + + PrevCtx = IRB.CreateLoad(AFLContext); + PrevCtx->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + // does the function have calls? and is any of the calls larger than + // one basic block? + has_calls = 0; + for (auto &BB : F) { + + if (has_calls) break; + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + if ((callInst = dyn_cast(&IN))) { + + Function *Callee = callInst->getCalledFunction(); + if (!Callee || Callee->size() < function_minimum_size) + continue; + else { + + has_calls = 1; + break; + + } + + } + + } + + } + + // if yes we store a context ID for this function in the global var + if (has_calls) { + + ConstantInt *NewCtx = ConstantInt::get(Int32Ty, genLabel()); + StoreInst * StoreCtx = IRB.CreateStore(NewCtx, AFLContext); + StoreCtx->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + } // END of ctx_str + + if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; } + + if (PI == PE) { + + cur_loc = genLabel(); + L = ConstantInt::get(Int32Ty, cur_loc); + + } else { + + auto *PN = PHINode::Create(Int32Ty, 0, "", &*BB.begin()); + DenseMap PredMap; + for (auto PI = pred_begin(&BB), PE = pred_end(&BB); PI != PE; ++PI) { + + BasicBlock *PBB = *PI; + auto It = PredMap.insert({PBB, genLabel()}); + unsigned Label = It.first->second; + cur_loc = Label; + PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB); + + } + + L = PN; + + } + + /* Load prev_loc */ + LoadInst *PrevLoc = IRB.CreateLoad(AFLPrevLoc); + PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + Value *PrevLocTrans; + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + /* "For efficiency, we propose to hash the tuple as a key into the + hit_count map as (prev_block_trans << 1) ^ curr_block_trans, where + prev_block_trans = (block_trans_1 ^ ... ^ block_trans_(n-1)" */ + + if (ngram_size) + PrevLocTrans = + IRB.CreateZExt(IRB.CreateXorReduce(PrevLoc), IRB.getInt32Ty()); + else +#endif + PrevLocTrans = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty()); + + if (ctx_str) + PrevLocTrans = + IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty); + + /* Load SHM pointer */ + LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); + MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + Value *MapPtrIdx; +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ngram_size) + MapPtrIdx = IRB.CreateGEP( + MapPtr, IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, L), Int32Ty)); + else +#endif + MapPtrIdx = IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocTrans, L)); + + /* Update bitmap */ + LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); + Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + Value *Incr = IRB.CreateAdd(Counter, One); + +#if LLVM_VERSION_MAJOR < 9 + if (neverZero_counters_str != + NULL) // with llvm 9 we make this the default as the bug in llvm is + // then fixed +#else + if (!skip_nozero) +#endif + { + + /* hexcoder: Realize a counter that skips zero during overflow. + * Once this counter reaches its maximum value, it next increments to + * 1 + * + * Instead of + * Counter + 1 -> Counter + * we inject now this + * Counter + 1 -> {Counter, OverflowFlag} + * Counter + OverflowFlag -> Counter + */ + auto cf = IRB.CreateICmpEQ(Incr, Zero); + auto carry = IRB.CreateZExt(cf, Int8Ty); + Incr = IRB.CreateAdd(Incr, carry); + + } + + IRB.CreateStore(Incr, MapPtrIdx) + ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + if (ctx_str && has_calls) { + + // in CTX mode we have to restore the original context for the + // caller - she might be calling other functions which need the + // correct CTX + Instruction *Inst = BB.getTerminator(); + if (isa(Inst) || isa(Inst)) { + + IRBuilder<> Post_IRB(Inst); + StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + } + + total_instr++; + + } + + } + + if (!be_quiet) { + + char modeline[100]; + snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", + getenv("AFL_HARDEN") ? "hardened" : "non-hardened", + getenv("AFL_USE_ASAN") ? ", ASAN" : "", + getenv("AFL_USE_MSAN") ? ", MSAN" : "", + getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", + getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); + + OKF("Instrumented %u locations (%llu, %llu) (%s mode)\n", total_instr, + total_rs, total_hs, modeline); + + } + + return false; + + } + +}; // end of struct InsTrim + +} // end of anonymous namespace + +char InsTrim::ID = 0; + +static void registerAFLPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + PM.add(new InsTrim()); + +} + +static RegisterStandardPasses RegisterAFLPass( + PassManagerBuilder::EP_OptimizerLast, registerAFLPass); + +static RegisterStandardPasses RegisterAFLPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass); + diff --git a/instrumentation/MarkNodes.cc b/instrumentation/MarkNodes.cc new file mode 100644 index 00000000..20a7df35 --- /dev/null +++ b/instrumentation/MarkNodes.cc @@ -0,0 +1,481 @@ +#include +#include +#include +#include +#include + +#include "llvm/Config/llvm-config.h" +#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5 +typedef long double max_align_t; +#endif + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/BasicBlock.h" +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) + #include "llvm/IR/CFG.h" +#else + #include "llvm/Support/CFG.h" +#endif +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +DenseMap LMap; +std::vector Blocks; +std::set Marked, Markabove; +std::vector > Succs, Preds; + +void reset() { + + LMap.clear(); + Blocks.clear(); + Marked.clear(); + Markabove.clear(); + +} + +uint32_t start_point; + +void labelEachBlock(Function *F) { + + // Fake single endpoint; + LMap[NULL] = Blocks.size(); + Blocks.push_back(NULL); + + // Assign the unique LabelID to each block; + for (auto I = F->begin(), E = F->end(); I != E; ++I) { + + BasicBlock *BB = &*I; + LMap[BB] = Blocks.size(); + Blocks.push_back(BB); + + } + + start_point = LMap[&F->getEntryBlock()]; + +} + +void buildCFG(Function *F) { + + Succs.resize(Blocks.size()); + Preds.resize(Blocks.size()); + for (size_t i = 0; i < Succs.size(); i++) { + + Succs[i].clear(); + Preds[i].clear(); + + } + + for (auto S = F->begin(), E = F->end(); S != E; ++S) { + + BasicBlock *BB = &*S; + uint32_t MyID = LMap[BB]; + + for (auto I = succ_begin(BB), E = succ_end(BB); I != E; ++I) { + + Succs[MyID].push_back(LMap[*I]); + + } + + } + +} + +std::vector > tSuccs; +std::vector tag, indfs; + +void DFStree(size_t now_id) { + + if (tag[now_id]) return; + tag[now_id] = true; + indfs[now_id] = true; + for (auto succ : tSuccs[now_id]) { + + if (tag[succ] and indfs[succ]) { + + Marked.insert(succ); + Markabove.insert(succ); + continue; + + } + + Succs[now_id].push_back(succ); + Preds[succ].push_back(now_id); + DFStree(succ); + + } + + indfs[now_id] = false; + +} + +void turnCFGintoDAG() { + + tSuccs = Succs; + tag.resize(Blocks.size()); + indfs.resize(Blocks.size()); + for (size_t i = 0; i < Blocks.size(); ++i) { + + Succs[i].clear(); + tag[i] = false; + indfs[i] = false; + + } + + DFStree(start_point); + for (size_t i = 0; i < Blocks.size(); ++i) + if (Succs[i].empty()) { + + Succs[i].push_back(0); + Preds[0].push_back(i); + + } + +} + +uint32_t timeStamp; +namespace DominatorTree { + +std::vector > cov; +std::vector dfn, nfd, par, sdom, idom, mom, mn; + +bool Compare(uint32_t u, uint32_t v) { + + return dfn[u] < dfn[v]; + +} + +uint32_t eval(uint32_t u) { + + if (mom[u] == u) return u; + uint32_t res = eval(mom[u]); + if (Compare(sdom[mn[mom[u]]], sdom[mn[u]])) { mn[u] = mn[mom[u]]; } + return mom[u] = res; + +} + +void DFS(uint32_t now) { + + timeStamp += 1; + dfn[now] = timeStamp; + nfd[timeStamp - 1] = now; + for (auto succ : Succs[now]) { + + if (dfn[succ] == 0) { + + par[succ] = now; + DFS(succ); + + } + + } + +} + +void DominatorTree() { + + if (Blocks.empty()) return; + uint32_t s = start_point; + + // Initialization + mn.resize(Blocks.size()); + cov.resize(Blocks.size()); + dfn.resize(Blocks.size()); + nfd.resize(Blocks.size()); + par.resize(Blocks.size()); + mom.resize(Blocks.size()); + sdom.resize(Blocks.size()); + idom.resize(Blocks.size()); + + for (uint32_t i = 0; i < Blocks.size(); i++) { + + dfn[i] = 0; + nfd[i] = Blocks.size(); + cov[i].clear(); + idom[i] = mom[i] = mn[i] = sdom[i] = i; + + } + + timeStamp = 0; + DFS(s); + + for (uint32_t i = Blocks.size() - 1; i >= 1u; i--) { + + uint32_t now = nfd[i]; + if (now == Blocks.size()) { continue; } + for (uint32_t pre : Preds[now]) { + + if (dfn[pre]) { + + eval(pre); + if (Compare(sdom[mn[pre]], sdom[now])) { sdom[now] = sdom[mn[pre]]; } + + } + + } + + cov[sdom[now]].push_back(now); + mom[now] = par[now]; + for (uint32_t x : cov[par[now]]) { + + eval(x); + if (Compare(sdom[mn[x]], par[now])) { + + idom[x] = mn[x]; + + } else { + + idom[x] = par[now]; + + } + + } + + } + + for (uint32_t i = 1; i < Blocks.size(); i += 1) { + + uint32_t now = nfd[i]; + if (now == Blocks.size()) { continue; } + if (idom[now] != sdom[now]) idom[now] = idom[idom[now]]; + + } + +} + +} // namespace DominatorTree + +std::vector Visited, InStack; +std::vector TopoOrder, InDeg; +std::vector > t_Succ, t_Pred; + +void Go(uint32_t now, uint32_t tt) { + + if (now == tt) return; + Visited[now] = InStack[now] = timeStamp; + + for (uint32_t nxt : Succs[now]) { + + if (Visited[nxt] == timeStamp and InStack[nxt] == timeStamp) { + + Marked.insert(nxt); + + } + + t_Succ[now].push_back(nxt); + t_Pred[nxt].push_back(now); + InDeg[nxt] += 1; + if (Visited[nxt] == timeStamp) { continue; } + Go(nxt, tt); + + } + + InStack[now] = 0; + +} + +void TopologicalSort(uint32_t ss, uint32_t tt) { + + timeStamp += 1; + + Go(ss, tt); + + TopoOrder.clear(); + std::queue wait; + wait.push(ss); + while (not wait.empty()) { + + uint32_t now = wait.front(); + wait.pop(); + TopoOrder.push_back(now); + for (uint32_t nxt : t_Succ[now]) { + + InDeg[nxt] -= 1; + if (InDeg[nxt] == 0u) { wait.push(nxt); } + + } + + } + +} + +std::vector > NextMarked; +bool Indistinguish(uint32_t node1, uint32_t node2) { + + if (NextMarked[node1].size() > NextMarked[node2].size()) { + + uint32_t _swap = node1; + node1 = node2; + node2 = _swap; + + } + + for (uint32_t x : NextMarked[node1]) { + + if (NextMarked[node2].find(x) != NextMarked[node2].end()) { return true; } + + } + + return false; + +} + +void MakeUniq(uint32_t now) { + + bool StopFlag = false; + if (Marked.find(now) == Marked.end()) { + + for (uint32_t pred1 : t_Pred[now]) { + + for (uint32_t pred2 : t_Pred[now]) { + + if (pred1 == pred2) continue; + if (Indistinguish(pred1, pred2)) { + + Marked.insert(now); + StopFlag = true; + break; + + } + + } + + if (StopFlag) { break; } + + } + + } + + if (Marked.find(now) != Marked.end()) { + + NextMarked[now].insert(now); + + } else { + + for (uint32_t pred : t_Pred[now]) { + + for (uint32_t x : NextMarked[pred]) { + + NextMarked[now].insert(x); + + } + + } + + } + +} + +bool MarkSubGraph(uint32_t ss, uint32_t tt) { + + TopologicalSort(ss, tt); + if (TopoOrder.empty()) return false; + + for (uint32_t i : TopoOrder) { + + NextMarked[i].clear(); + + } + + NextMarked[TopoOrder[0]].insert(TopoOrder[0]); + for (uint32_t i = 1; i < TopoOrder.size(); i += 1) { + + MakeUniq(TopoOrder[i]); + + } + + // Check if there is an empty path. + if (NextMarked[tt].count(TopoOrder[0]) > 0) return true; + return false; + +} + +void MarkVertice() { + + uint32_t s = start_point; + + InDeg.resize(Blocks.size()); + Visited.resize(Blocks.size()); + InStack.resize(Blocks.size()); + t_Succ.resize(Blocks.size()); + t_Pred.resize(Blocks.size()); + NextMarked.resize(Blocks.size()); + + for (uint32_t i = 0; i < Blocks.size(); i += 1) { + + Visited[i] = InStack[i] = InDeg[i] = 0; + t_Succ[i].clear(); + t_Pred[i].clear(); + + } + + timeStamp = 0; + uint32_t t = 0; + bool emptyPathExists = true; + + while (s != t) { + + emptyPathExists &= MarkSubGraph(DominatorTree::idom[t], t); + t = DominatorTree::idom[t]; + + } + + if (emptyPathExists) { + + // Mark all exit blocks to catch the empty path. + Marked.insert(t_Pred[0].begin(), t_Pred[0].end()); + + } + +} + +// return {marked nodes} +std::pair, std::vector > markNodes( + Function *F) { + + assert(F->size() > 0 && "Function can not be empty"); + + reset(); + labelEachBlock(F); + buildCFG(F); + turnCFGintoDAG(); + DominatorTree::DominatorTree(); + MarkVertice(); + + std::vector Result, ResultAbove; + for (uint32_t x : Markabove) { + + auto it = Marked.find(x); + if (it != Marked.end()) Marked.erase(it); + if (x) ResultAbove.push_back(Blocks[x]); + + } + + for (uint32_t x : Marked) { + + if (x == 0) { + + continue; + + } else { + + Result.push_back(Blocks[x]); + + } + + } + + return {Result, ResultAbove}; + +} + diff --git a/instrumentation/MarkNodes.h b/instrumentation/MarkNodes.h new file mode 100644 index 00000000..8ddc978d --- /dev/null +++ b/instrumentation/MarkNodes.h @@ -0,0 +1,12 @@ +#ifndef __MARK_NODES__ +#define __MARK_NODES__ + +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Function.h" +#include + +std::pair, std::vector> +markNodes(llvm::Function *F); + +#endif + diff --git a/instrumentation/README.cmplog.md b/instrumentation/README.cmplog.md new file mode 100644 index 00000000..5f855e1f --- /dev/null +++ b/instrumentation/README.cmplog.md @@ -0,0 +1,42 @@ +# CmpLog instrumentation + +The CmpLog instrumentation enables the logging of the comparisons operands in a +shared memory. + +These values can be used by various mutators built on top of it. +At the moment we support the RedQueen mutator (input-2-state instructions only). + +## Build + +To use CmpLog, you have to build two versions of the instrumented target +program. + +The first version is built using the regular AFL++ instrumentation. + +The second one, the CmpLog binary, with setting AFL_LLVM_CMPLOG during the compilation. + +For example: + +``` +./configure --cc=~/path/to/afl-clang-fast +make +cp ./program ./program.afl +make clean +export AFL_LLVM_CMPLOG=1 +./configure --cc=~/path/to/afl-clang-fast +make +cp ./program ./program.cmplog +``` + +## Use + +AFL++ has the new -c option that needs to be used to specify the CmpLog binary (the second +build). + +For example: + +``` +afl-fuzz -i input -o output -c ./program.cmplog -m none -- ./program.afl @@ +``` + +Be sure to use `-m none` because CmpLog can map a lot of pages. diff --git a/instrumentation/README.ctx.md b/instrumentation/README.ctx.md new file mode 100644 index 00000000..caf2c09a --- /dev/null +++ b/instrumentation/README.ctx.md @@ -0,0 +1,22 @@ +# AFL Context Sensitive Branch Coverage + +## What is this? + +This is an LLVM-based implementation of the context sensitive branch coverage. + +Basically every function gets its own ID and that ID is combined with the +edges of the called functions. + +So if both function A and function B call a function C, the coverage +collected in C will be different. + +In math the coverage is collected as follows: +`map[current_location_ID ^ previous_location_ID >> 1 ^ previous_callee_ID] += 1` + +## Usage + +Set the `AFL_LLVM_INSTRUMENT=CTX` or `AFL_LLVM_CTX=1` environment variable. + +It is highly recommended to increase the MAP_SIZE_POW2 definition in +config.h to at least 18 and maybe up to 20 for this as otherwise too +many map collisions occur. diff --git a/instrumentation/README.gcc_plugin.md b/instrumentation/README.gcc_plugin.md new file mode 100644 index 00000000..9d6bc200 --- /dev/null +++ b/instrumentation/README.gcc_plugin.md @@ -0,0 +1,158 @@ +# GCC-based instrumentation for afl-fuzz + + (See [../README.md](../README.md) for the general instruction manual.) + (See [README.llvm.md](README.llvm.md) for the LLVM-based instrumentation.) + +!!! TODO items are: +!!! => inline instrumentation has to work! +!!! + + +## 1) Introduction + +The code in this directory allows you to instrument programs for AFL using +true compiler-level instrumentation, instead of the more crude +assembly-level rewriting approach taken by afl-gcc and afl-clang. This has +several interesting properties: + + - The compiler can make many optimizations that are hard to pull off when + manually inserting assembly. As a result, some slow, CPU-bound programs will + run up to around faster. + + The gains are less pronounced for fast binaries, where the speed is limited + chiefly by the cost of creating new processes. In such cases, the gain will + probably stay within 10%. + + - The instrumentation is CPU-independent. At least in principle, you should + be able to rely on it to fuzz programs on non-x86 architectures (after + building afl-fuzz with AFL_NOX86=1). + + - Because the feature relies on the internals of GCC, it is gcc-specific + and will *not* work with LLVM (see ../llvm_mode for an alternative). + +Once this implementation is shown to be sufficiently robust and portable, it +will probably replace afl-gcc. For now, it can be built separately and +co-exists with the original code. + +The idea and much of the implementation comes from Laszlo Szekeres. + +## 2) How to use + +In order to leverage this mechanism, you need to have modern enough GCC +(>= version 4.5.0) and the plugin headers installed on your system. That +should be all you need. On Debian machines, these headers can be acquired by +installing the `gcc--plugin-dev` packages. + +To build the instrumentation itself, type 'make'. This will generate binaries +called afl-gcc-fast and afl-g++-fast in the parent directory. +If the CC/CXX have been overridden, those compilers will be used from +those wrappers without using AFL_CXX/AFL_CC settings. +Once this is done, you can instrument third-party code in a way similar to the +standard operating mode of AFL, e.g.: + + CC=/path/to/afl/afl-gcc-fast ./configure [...options...] + make + +Be sure to also include CXX set to afl-g++-fast for C++ code. + +The tool honors roughly the same environmental variables as afl-gcc (see +[env_variables.md](../docs/env_variables.md). This includes AFL_INST_RATIO, AFL_USE_ASAN, +AFL_HARDEN, and AFL_DONT_OPTIMIZE. + +Note: if you want the GCC plugin to be installed on your system for all +users, you need to build it before issuing 'make install' in the parent +directory. + +## 3) Gotchas, feedback, bugs + +This is an early-stage mechanism, so field reports are welcome. You can send bug +reports to . + +## 4) Bonus feature #1: deferred initialization + +AFL tries to optimize performance by executing the targeted binary just once, +stopping it just before main(), and then cloning this "main" process to get +a steady supply of targets to fuzz. + +Although this approach eliminates much of the OS-, linker- and libc-level +costs of executing the program, it does not always help with binaries that +perform other time-consuming initialization steps - say, parsing a large config +file before getting to the fuzzed data. + +In such cases, it's beneficial to initialize the forkserver a bit later, once +most of the initialization work is already done, but before the binary attempts +to read the fuzzed input and parse it; in some cases, this can offer a 10x+ +performance gain. You can implement delayed initialization in LLVM mode in a +fairly simple way. + +First, locate a suitable location in the code where the delayed cloning can +take place. This needs to be done with *extreme* care to avoid breaking the +binary. In particular, the program will probably malfunction if you select +a location after: + + - The creation of any vital threads or child processes - since the forkserver + can't clone them easily. + + - The initialization of timers via setitimer() or equivalent calls. + + - The creation of temporary files, network sockets, offset-sensitive file + descriptors, and similar shared-state resources - but only provided that + their state meaningfully influences the behavior of the program later on. + + - Any access to the fuzzed input, including reading the metadata about its + size. + +With the location selected, add this code in the appropriate spot: + +``` +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif +``` + +You don't need the #ifdef guards, but they will make the program still work as +usual when compiled with a tool other than afl-gcc-fast/afl-clang-fast. + +Finally, recompile the program with afl-gcc-fast (afl-gcc or afl-clang will +*not* generate a deferred-initialization binary) - and you should be all set! + +## 5) Bonus feature #2: persistent mode + +Some libraries provide APIs that are stateless, or whose state can be reset in +between processing different input files. When such a reset is performed, a +single long-lived process can be reused to try out multiple test cases, +eliminating the need for repeated fork() calls and the associated OS overhead. + +The basic structure of the program that does this would be: + +``` + while (__AFL_LOOP(1000)) { + + /* Read input data. */ + /* Call library code to be fuzzed. */ + /* Reset state. */ + + } + + /* Exit normally */ +``` + +The numerical value specified within the loop controls the maximum number +of iterations before AFL will restart the process from scratch. This minimizes +the impact of memory leaks and similar glitches; 1000 is a good starting point. + +A more detailed template is shown in ../examples/persistent_demo/. +Similarly to the previous mode, the feature works only with afl-gcc-fast or +afl-clang-fast; #ifdef guards can be used to suppress it when using other +compilers. + +Note that as with the previous mode, the feature is easy to misuse; if you +do not reset the critical state fully, you may end up with false positives or +waste a whole lot of CPU power doing nothing useful at all. Be particularly +wary of memory leaks and the state of file descriptors. + +When running in this mode, the execution paths will inherently vary a bit +depending on whether the input loop is being entered for the first time or +executed again. To avoid spurious warnings, the feature implies +AFL_NO_VAR_CHECK and hides the "variable path" warnings in the UI. + diff --git a/instrumentation/README.instrim.md b/instrumentation/README.instrim.md new file mode 100644 index 00000000..99f6477a --- /dev/null +++ b/instrumentation/README.instrim.md @@ -0,0 +1,30 @@ +# InsTrim + +InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing + +## Introduction + +InsTrim is the work of Chin-Chia Hsu, Che-Yu Wu, Hsu-Chun Hsiao and Shih-Kun Huang. + +It uses a CFG (call flow graph) and markers to instrument just what +is necessary in the binary (ie less than llvm_mode). As a result the binary is +about 10-15% faster compared to normal llvm_mode however with some coverage loss. +It requires at least llvm version 3.8.0 to build. +If you have LLVM 7+ we recommend PCGUARD instead. + +## Usage + +Set the environment variable `AFL_LLVM_INSTRUMENT=CFG` or `AFL_LLVM_INSTRIM=1` +during compilation of the target. + +There is also special mode which instruments loops in a way so that +afl-fuzz can see which loop path has been selected but not being able to +see how often the loop has been rerun. +This again is a tradeoff for speed for less path information. +To enable this mode set `AFL_LLVM_INSTRIM_LOOPHEAD=1`. + +## Background + +The paper from Chin-Chia Hsu, Che-Yu Wu, Hsu-Chun Hsiao and Shih-Kun Huang: +[InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing] +(https://www.ndss-symposium.org/wp-content/uploads/2018/07/bar2018_14_Hsu_paper.pdf) diff --git a/instrumentation/README.instrument_list.md b/instrumentation/README.instrument_list.md new file mode 100644 index 00000000..60474ec6 --- /dev/null +++ b/instrumentation/README.instrument_list.md @@ -0,0 +1,87 @@ +# Using afl++ with partial instrumentation + + This file describes how to selectively instrument only source files + or functions that are of interest to you using the LLVM instrumentation + provided by afl++. + +## 1) Description and purpose + +When building and testing complex programs where only a part of the program is +the fuzzing target, it often helps to only instrument the necessary parts of +the program, leaving the rest uninstrumented. This helps to focus the fuzzer +on the important parts of the program, avoiding undesired noise and +disturbance by uninteresting code being exercised. + +For this purpose, a "partial instrumentation" support en par with llvm sancov +is provided by afl++ that allows to specify on a source file and function +level which function should be compiled with or without instrumentation. + +Note: When using PCGUARD mode - and llvm 12+ - you can use this instead: +https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation + +The llvm sancov list format is fully supported by afl++, however afl++ has +more flexibility. + +## 2) Building the LLVM module + +The new code is part of the existing afl++ LLVM module in the instrumentation/ +subdirectory. There is nothing specifically to do for the build :) + +## 3) How to use the partial instrumentation mode + +In order to build with partial instrumentation, you need to build with +afl-clang-fast/afl-clang-fast++ or afl-clang-lto/afl-clang-lto++. +The only required change is that you need to set either the environment variable +AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST set with a filename. + +That file should contain the file names or functions that are to be instrumented +(AFL_LLVM_ALLOWLIST) or are specifically NOT to be instrumented (AFL_LLVM_DENYLIST). + +For matching to succeed, the function/file name that is being compiled must end in the +function/file name entry contained in this instrument file list. That is to avoid +breaking the match when absolute paths are used during compilation. + +**NOTE:** In builds with optimization enabled, functions might be inlined and would not match! + +For example if your source tree looks like this: +``` +project/ +project/feature_a/a1.cpp +project/feature_a/a2.cpp +project/feature_b/b1.cpp +project/feature_b/b2.cpp +``` + +and you only want to test feature_a, then create an "instrument file list" file containing: +``` +feature_a/a1.cpp +feature_a/a2.cpp +``` + +However if the "instrument file list" file contains only this, it works as well: +``` +a1.cpp +a2.cpp +``` +but it might lead to files being unwantedly instrumented if the same filename +exists somewhere else in the project directories. + +You can also specify function names. Note that for C++ the function names +must be mangled to match! `nm` can print these names. + +afl++ is able to identify whether an entry is a filename or a function. +However if you want to be sure (and compliant to the sancov allow/blocklist +format), you can specify source file entries like this: +``` +src: *malloc.c +``` +and function entries like this: +``` +fun: MallocFoo +``` +Note that whitespace is ignored and comments (`# foo`) are supported. + +## 4) UNIX-style pattern matching + +You can add UNIX-style pattern matching in the "instrument file list" entries. +See `man fnmatch` for the syntax. We do not set any of the `fnmatch` flags. diff --git a/instrumentation/README.laf-intel.md b/instrumentation/README.laf-intel.md new file mode 100644 index 00000000..c50a6979 --- /dev/null +++ b/instrumentation/README.laf-intel.md @@ -0,0 +1,56 @@ +# laf-intel instrumentation + +## Introduction + +This originally is the work of an individual nicknamed laf-intel. +His blog [Circumventing Fuzzing Roadblocks with Compiler Transformations] +(https://lafintel.wordpress.com/) and gitlab repo [laf-llvm-pass] +(https://gitlab.com/laf-intel/laf-llvm-pass/) +describe some code transformations that +help afl++ to enter conditional blocks, where conditions consist of +comparisons of large values. + +## Usage + +By default these passes will not run when you compile programs using +afl-clang-fast. Hence, you can use AFL as usual. +To enable the passes you must set environment variables before you +compile the target project. + +The following options exist: + +`export AFL_LLVM_LAF_SPLIT_SWITCHES=1` + +Enables the split-switches pass. + +`export AFL_LLVM_LAF_TRANSFORM_COMPARES=1` + +Enables the transform-compares pass (strcmp, memcmp, strncmp, +strcasecmp, strncasecmp). + +`export AFL_LLVM_LAF_SPLIT_COMPARES=1` + +Enables the split-compares pass. +By default it will +1. simplify operators >= (and <=) into chains of > (<) and == comparisons +2. change signed integer comparisons to a chain of sign-only comparison +and unsigned integer comparisons +3. split all unsigned integer comparisons with bit widths of +64, 32 or 16 bits to chains of 8 bits comparisons. + +You can change the behaviour of the last step by setting +`export AFL_LLVM_LAF_SPLIT_COMPARES_BITW=`, where +bit_width may be 64, 32 or 16. For example, a bit_width of 16 +would split larger comparisons down to 16 bit comparisons. + +A new experimental feature is splitting floating point comparisons into a +series of sign, exponent and mantissa comparisons followed by splitting each +of them into 8 bit comparisons when necessary. +It is activated with the `AFL_LLVM_LAF_SPLIT_FLOATS` setting. +Please note that full IEEE 754 functionality is not preserved, that is +values of nan and infinity will probably behave differently. + +Note that setting this automatically activates `AFL_LLVM_LAF_SPLIT_COMPARES` + +You can also set `AFL_LLVM_LAF_ALL` and have all of the above enabled :-) + diff --git a/instrumentation/README.llvm.md b/instrumentation/README.llvm.md new file mode 100644 index 00000000..51e9995b --- /dev/null +++ b/instrumentation/README.llvm.md @@ -0,0 +1,194 @@ +# Fast LLVM-based instrumentation for afl-fuzz + + (See [../README.md](../README.md) for the general instruction manual.) + + (See [README.gcc_plugon.md](../README.gcc_plugin.md) for the GCC-based instrumentation.) + +## 1) Introduction + +! llvm_mode works with llvm versions 3.4 up to 12 ! + +The code in this directory allows you to instrument programs for AFL using +true compiler-level instrumentation, instead of the more crude +assembly-level rewriting approach taken by afl-gcc and afl-clang. This has +several interesting properties: + + - The compiler can make many optimizations that are hard to pull off when + manually inserting assembly. As a result, some slow, CPU-bound programs will + run up to around 2x faster. + + The gains are less pronounced for fast binaries, where the speed is limited + chiefly by the cost of creating new processes. In such cases, the gain will + probably stay within 10%. + + - The instrumentation is CPU-independent. At least in principle, you should + be able to rely on it to fuzz programs on non-x86 architectures (after + building afl-fuzz with AFL_NO_X86=1). + + - The instrumentation can cope a bit better with multi-threaded targets. + + - Because the feature relies on the internals of LLVM, it is clang-specific + and will *not* work with GCC (see ../gcc_plugin/ for an alternative once + it is available). + +Once this implementation is shown to be sufficiently robust and portable, it +will probably replace afl-clang. For now, it can be built separately and +co-exists with the original code. + +The idea and much of the intial implementation came from Laszlo Szekeres. + +## 2a) How to use this - short + +Set the `LLVM_CONFIG` variable to the clang version you want to use, e.g. +``` +LLVM_CONFIG=llvm-config-9 make +``` +In case you have your own compiled llvm version specify the full path: +``` +LLVM_CONFIG=~/llvm-project/build/bin/llvm-config make +``` +If you try to use a new llvm version on an old Linux this can fail because of +old c++ libraries. In this case usually switching to gcc/g++ to compile +llvm_mode will work: +``` +LLVM_CONFIG=llvm-config-7 REAL_CC=gcc REAL_CXX=g++ make +``` +It is highly recommended to use the newest clang version you can put your +hands on :) + +Then look at [README.persistent_mode.md](README.persistent_mode.md). + +## 2b) How to use this - long + +In order to leverage this mechanism, you need to have clang installed on your +system. You should also make sure that the llvm-config tool is in your path +(or pointed to via LLVM_CONFIG in the environment). + +Note that if you have several LLVM versions installed, pointing LLVM_CONFIG +to the version you want to use will switch compiling to this specific +version - if you installation is set up correctly :-) + +Unfortunately, some systems that do have clang come without llvm-config or the +LLVM development headers; one example of this is FreeBSD. FreeBSD users will +also run into problems with clang being built statically and not being able to +load modules (you'll see "Service unavailable" when loading afl-llvm-pass.so). + +To solve all your problems, you can grab pre-built binaries for your OS from: + + http://llvm.org/releases/download.html + +...and then put the bin/ directory from the tarball at the beginning of your +$PATH when compiling the feature and building packages later on. You don't need +to be root for that. + +To build the instrumentation itself, type 'make'. This will generate binaries +called afl-clang-fast and afl-clang-fast++ in the parent directory. Once this +is done, you can instrument third-party code in a way similar to the standard +operating mode of AFL, e.g.: + +``` + CC=/path/to/afl/afl-clang-fast ./configure [...options...] + make +``` + +Be sure to also include CXX set to afl-clang-fast++ for C++ code. + +Note that afl-clang-fast/afl-clang-fast++ are just pointers to afl-cc. +You can also use afl-cc/afl-c++ and instead direct it to use LLVM +instrumentation by either setting `AFL_CC_COMPILER=LLVM` or pass the parameter +`--afl-llvm` via CFLAGS/CXXFLAGS/CPPFLAGS. + +The tool honors roughly the same environmental variables as afl-gcc (see +[docs/env_variables.md](../docs/env_variables.md)). This includes AFL_USE_ASAN, +AFL_HARDEN, and AFL_DONT_OPTIMIZE. However AFL_INST_RATIO is not honored +as it does not serve a good purpose with the more effective PCGUARD, LTO and + instrim CFG analysis. + +## 3) Options + +Several options are present to make llvm_mode faster or help it rearrange +the code to make afl-fuzz path discovery easier. + +If you need just to instrument specific parts of the code, you can the instrument file list +which C/C++ files to actually instrument. See [README.instrument_list.md](README.instrument_list.md) + +For splitting memcmp, strncmp, etc. please see [README.laf-intel.md](README.laf-intel.md) + +Then there are different ways of instrumenting the target: + +1. There is an optimized instrumentation strategy that uses CFGs and +markers to just instrument what is needed. This increases speed by 10-15% +without any disadvantages +If you want to use this, set AFL_LLVM_INSTRUMENT=CFG or AFL_LLVM_INSTRIM=1 +See [README.instrim.md](README.instrim.md) + +2. An even better instrumentation strategy uses LTO and link time +instrumentation. Note that not all targets can compile in this mode, however +if it works it is the best option you can use. +Simply use afl-clang-lto/afl-clang-lto++ to use this option. +See [README.lto.md](README.lto.md) + +3. Alternativly you can choose a completely different coverage method: + +3a. N-GRAM coverage - which combines the previous visited edges with the +current one. This explodes the map but on the other hand has proven to be +effective for fuzzing. +See [README.ngram.md](README.ngram.md) + +3b. Context sensitive coverage - which combines the visited edges with an +individual caller ID (the function that called the current one) +[README.ctx.md](README.ctx.md) + +Then - additionally to one of the instrumentation options above - there is +a very effective new instrumentation option called CmpLog as an alternative to +laf-intel that allow AFL++ to apply mutations similar to Redqueen. +See [README.cmplog.md](README.cmplog.md) + +Finally if your llvm version is 8 or lower, you can activate a mode that +prevents that a counter overflow result in a 0 value. This is good for +path discovery, but the llvm implementation for x86 for this functionality +is not optimal and was only fixed in llvm 9. +You can set this with AFL_LLVM_NOT_ZERO=1 +See [README.neverzero.md](README.neverzero.md) + +## 4) Snapshot feature + +To speed up fuzzing you can use a linux loadable kernel module which enables +a snapshot feature. +See [README.snapshot.md](README.snapshot.md) + +## 5) Gotchas, feedback, bugs + +This is an early-stage mechanism, so field reports are welcome. You can send bug +reports to . + +## 6) deferred initialization, persistent mode, shared memory fuzzing + +This is the most powerful and effective fuzzing you can do. +Please see [README.persistent_mode.md](README.persistent_mode.md) for a +full explanation. + +## 7) Bonus feature: 'trace-pc-guard' mode + +LLVM is shipping with a built-in execution tracing feature +that provides AFL with the necessary tracing data without the need to +post-process the assembly or install any compiler plugins. See: + + http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards + +If you have not an outdated compiler and want to give it a try, build +targets this way: + +``` +AFL_LLVM_INSTRUMENT=PCGUARD make +``` + +Note that this us currently the default if you use LLVM >= 7, as it is the best +mode. Recommended is LLVM >= 9. +If you have llvm 11+ and compiled afl-clang-lto - this is the only better mode. + +## 8) Bonus feature: 'dict2file' pass + +Just specify `AFL_LLVM_DICT2FILE=/absolute/path/file.txt` and during compilation +all constant string compare parameters will be written to this file to be +used with afl-fuzz' `-x` option. diff --git a/instrumentation/README.lto.md b/instrumentation/README.lto.md new file mode 100644 index 00000000..abdbd2ac --- /dev/null +++ b/instrumentation/README.lto.md @@ -0,0 +1,290 @@ +# afl-clang-lto - collision free instrumentation at link time + +## TLDR; + +This version requires a current llvm 11+ compiled from the github master. + +1. Use afl-clang-lto/afl-clang-lto++ because it is faster and gives better + coverage than anything else that is out there in the AFL world + +2. You can use it together with llvm_mode: laf-intel and the instrument file listing + features and can be combined with cmplog/Redqueen + +3. It only works with llvm 11+ + +4. AUTODICTIONARY feature! see below + +5. If any problems arise be sure to set `AR=llvm-ar RANLIB=llvm-ranlib`. + Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`. + +## Introduction and problem description + +A big issue with how afl/afl++ works is that the basic block IDs that are +set during compilation are random - and hence naturally the larger the number +of instrumented locations, the higher the number of edge collisions are in the +map. This can result in not discovering new paths and therefore degrade the +efficiency of the fuzzing process. + +*This issue is underestimated in the fuzzing community!* +With a 2^16 = 64kb standard map at already 256 instrumented blocks there is +on average one collision. On average a target has 10.000 to 50.000 +instrumented blocks hence the real collisions are between 750-18.000! + +To reach a solution that prevents any collisions took several approaches +and many dead ends until we got to this: + + * We instrument at link time when we have all files pre-compiled + * To instrument at link time we compile in LTO (link time optimization) mode + * Our compiler (afl-clang-lto/afl-clang-lto++) takes care of setting the + correct LTO options and runs our own afl-ld linker instead of the system + linker + * The LLVM linker collects all LTO files to link and instruments them so that + we have non-colliding edge overage + * We use a new (for afl) edge coverage - which is the same as in llvm + -fsanitize=coverage edge coverage mode :) + +The result: + * 10-25% speed gain compared to llvm_mode + * guaranteed non-colliding edge coverage :-) + * The compile time especially for binaries to an instrumented library can be + much longer + +Example build output from a libtiff build: +``` +libtool: link: afl-clang-lto -g -O2 -Wall -W -o thumbnail thumbnail.o ../libtiff/.libs/libtiff.a ../port/.libs/libport.a -llzma -ljbig -ljpeg -lz -lm +afl-clang-lto++2.63d by Marc "vanHauser" Heuse in mode LTO +afl-llvm-lto++2.63d by Marc "vanHauser" Heuse +AUTODICTIONARY: 11 strings found +[+] Instrumented 12071 locations with no collisions (on average 1046 collisions would be in afl-gcc/afl-clang-fast) (non-hardened mode). +``` + +## Getting llvm 11+ + +### Installing llvm from the llvm repository (version 11) + +Installing the llvm snapshot builds is easy and mostly painless: + +In the follow line change `NAME` for your Debian or Ubuntu release name +(e.g. buster, focal, eon, etc.): +``` +echo deb http://apt.llvm.org/NAME/ llvm-toolchain-NAME NAME >> /etc/apt/sources.list +``` +then add the pgp key of llvm and install the packages: +``` +wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - +apt-get update && apt-get upgrade -y +apt-get install -y clang-11 clang-tools-11 libc++1-11 libc++-11-dev \ + libc++abi1-11 libc++abi-11-dev libclang1-11 libclang-11-dev \ + libclang-common-11-dev libclang-cpp11 libclang-cpp11-dev liblld-11 \ + liblld-11-dev liblldb-11 liblldb-11-dev libllvm11 libomp-11-dev \ + libomp5-11 lld-11 lldb-11 llvm-11 llvm-11-dev llvm-11-runtime llvm-11-tools +``` + +### Building llvm yourself (version 12) + +Building llvm from github takes quite some long time and is not painless: +``` +sudo apt install binutils-dev # this is *essential*! +git clone https://github.com/llvm/llvm-project +cd llvm-project +mkdir build +cd build +cmake -DLLVM_ENABLE_PROJECTS='clang;clang-tools-extra;compiler-rt;libclc;libcxx;libcxxabi;libunwind;lld' -DCMAKE_BUILD_TYPE=Release -DLLVM_BINUTILS_INCDIR=/usr/include/ ../llvm/ +make -j $(nproc) +export PATH=`pwd`/bin:$PATH +export LLVM_CONFIG=`pwd`/bin/llvm-config +cd /path/to/AFLplusplus/ +make +sudo make install +``` + +## How to use afl-clang-lto + +Just use afl-clang-lto like you did with afl-clang-fast or afl-gcc. + +Also the instrument file listing (AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST -> [README.instrument_list.md](README.instrument_list.md)) and +laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work. + +Example: +``` +CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure +make +``` + +NOTE: some targets also need to set the linker, try both `afl-clang-lto` and +`afl-ld-lto` for `LD=` before `configure`. + +## AUTODICTIONARY feature + +While compiling, a dictionary based on string comparisons is automatically +generated and put into the target binary. This dictionary is transfered to afl-fuzz +on start. This improves coverage statistically by 5-10% :) + +## Fixed memory map + +To speed up fuzzing, it is possible to set a fixed shared memory map. +Recommended is the value 0x10000. +In most cases this will work without any problems. However if a target uses +early constructors, ifuncs or a deferred forkserver this can crash the target. +On unusual operating systems/processors/kernels or weird libraries this might +fail so to change the fixed address at compile time set +AFL_LLVM_MAP_ADDR with a better value (a value of 0 or empty sets the map address +to be dynamic - the original afl way, which is slower). + +## Document edge IDs + +Setting `export AFL_LLVM_DOCUMENT_IDS=file` will document in a file which edge +ID was given to which function. This helps to identify functions with variable +bytes or which functions were touched by an input. + +## Solving difficult targets + +Some targets are difficult because the configure script does unusual stuff that +is unexpected for afl. See the next chapter `Potential issues` for how to solve +these. + +### Example: ffmpeg + +An example of a hard to solve target is ffmpeg. Here is how to successfully +instrument it: + +1. Get and extract the current ffmpeg and change to its directory + +2. Running configure with --cc=clang fails and various other items will fail + when compiling, so we have to trick configure: + +``` +./configure --enable-lto --disable-shared --disable-inline-asm +``` + +3. Now the configuration is done - and we edit the settings in `./ffbuild/config.mak` + (-: the original line, +: what to change it into): +``` +-CC=gcc ++CC=afl-clang-lto +-CXX=g++ ++CXX=afl-clang-lto++ +-AS=gcc ++AS=llvm-as +-LD=gcc ++LD=afl-clang-lto++ +-DEPCC=gcc ++DEPCC=afl-clang-lto +-DEPAS=gcc ++DEPAS=afl-clang-lto++ +-AR=ar ++AR=llvm-ar +-AR_CMD=ar ++AR_CMD=llvm-ar +-NM_CMD=nm -g ++NM_CMD=llvm-nm -g +-RANLIB=ranlib -D ++RANLIB=llvm-ranlib -D +``` + +4. Then type make, wait for a long time and you are done :) + +### Example: WebKit jsc + +Building jsc is difficult as the build script has bugs. + +1. checkout Webkit: +``` +svn checkout https://svn.webkit.org/repository/webkit/trunk WebKit +cd WebKit +``` + +2. Fix the build environment: +``` +mkdir -p WebKitBuild/Release +cd WebKitBuild/Release +ln -s ../../../../../usr/bin/llvm-ar-12 llvm-ar-12 +ln -s ../../../../../usr/bin/llvm-ranlib-12 llvm-ranlib-12 +cd ../.. +``` + +3. Build :) + +``` +Tools/Scripts/build-jsc --jsc-only --cli --cmakeargs="-DCMAKE_AR='llvm-ar-12' -DCMAKE_RANLIB='llvm-ranlib-12' -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_CC_FLAGS='-O3 -lrt' -DCMAKE_CXX_FLAGS='-O3 -lrt' -DIMPORTED_LOCATION='/lib/x86_64-linux-gnu/' -DCMAKE_CC=afl-clang-lto -DCMAKE_CXX=afl-clang-lto++ -DENABLE_STATIC_JSC=ON" +``` + +## Potential issues + +### compiling libraries fails + +If you see this message: +``` +/bin/ld: libfoo.a: error adding symbols: archive has no index; run ranlib to add one +``` +This is because usually gnu gcc ranlib is being called which cannot deal with clang LTO files. +The solution is simple: when you ./configure you also have to set RANLIB=llvm-ranlib and AR=llvm-ar + +Solution: +``` +AR=llvm-ar RANLIB=llvm-ranlib CC=afl-clang-lto CXX=afl-clang-lto++ ./configure --disable-shared +``` +and on some targets you have to set AR=/RANLIB= even for make as the configure script does not save it. +Other targets ignore environment variables and need the parameters set via +`./configure --cc=... --cxx= --ranlib= ...` etc. (I am looking at you ffmpeg!). + + +If you see this message +``` +assembler command failed ... +``` +then try setting `llvm-as` for configure: +``` +AS=llvm-as ... +``` + +### compiling programs still fail + +afl-clang-lto is still work in progress. + +Known issues: + * Anything that llvm 11+ cannot compile, afl-clang-lto cannot compile either - obviously + * Anything that does not compile with LTO, afl-clang-lto cannot compile either - obviously + +Hence if building a target with afl-clang-lto fails try to build it with llvm12 +and LTO enabled (`CC=clang-12` `CXX=clang++-12` `CFLAGS=-flto=full` and +`CXXFLAGS=-flto=full`). + +If this succeeeds then there is an issue with afl-clang-lto. Please report at +[https://github.com/AFLplusplus/AFLplusplus/issues/226](https://github.com/AFLplusplus/AFLplusplus/issues/226) + +Even some targets where clang-12 fails can be build if the fail is just in +`./configure`, see `Solving difficult targets` above. + +## History + +This was originally envisioned by hexcoder- in Summer 2019, however we saw no +way to create a pass that is run at link time - although there is a option +for this in the PassManager: EP_FullLinkTimeOptimizationLast +("Fun" info - nobody knows what this is doing. And the developer who +implemented this didn't respond to emails.) + +In December then came the idea to implement this as a pass that is run via +the llvm "opt" program, which is performed via an own linker that afterwards +calls the real linker. +This was first implemented in January and work ... kinda. +The LTO time instrumentation worked, however "how" the basic blocks were +instrumented was a problem, as reducing duplicates turned out to be very, +very difficult with a program that has so many paths and therefore so many +dependencies. A lot of strategies were implemented - and failed. +And then sat solvers were tried, but with over 10.000 variables that turned +out to be a dead-end too. + +The final idea to solve this came from domenukk who proposed to insert a block +into an edge and then just use incremental counters ... and this worked! +After some trials and errors to implement this vanhauser-thc found out that +there is actually an llvm function for this: SplitEdge() :-) + +Still more problems came up though as this only works without bugs from +llvm 9 onwards, and with high optimization the link optimization ruins +the instrumented control flow graph. + +This is all now fixed with llvm 11+. The llvm's own linker is now able to +load passes and this bypasses all problems we had. + +Happy end :) diff --git a/instrumentation/README.neverzero.md b/instrumentation/README.neverzero.md new file mode 100644 index 00000000..5c894d6e --- /dev/null +++ b/instrumentation/README.neverzero.md @@ -0,0 +1,35 @@ +# NeverZero counters for LLVM instrumentation + +## Usage + +In larger, complex or reiterative programs the byte sized counters that collect +the edge coverage can easily fill up and wrap around. +This is not that much of an issue - unless by chance it wraps just to a value +of zero when the program execution ends. +In this case afl-fuzz is not able to see that the edge has been accessed and +will ignore it. + +NeverZero prevents this behaviour. If a counter wraps, it jumps over the value +0 directly to a 1. This improves path discovery (by a very little amount) +at a very little cost (one instruction per edge). + +(The alternative of saturated counters has been tested also and proved to be +inferior in terms of path discovery.) + +This is implemented in afl-gcc, however for llvm_mode this is optional if +the llvm version is below 9 - as there is a perfomance bug that is only fixed +in version 9 and onwards. + +If you want to enable this for llvm versions below 9 then set + +``` +export AFL_LLVM_NOT_ZERO=1 +``` + +In case you are on llvm 9 or greater and you do not want this behaviour then +you can set: +``` +AFL_LLVM_SKIP_NEVERZERO=1 +``` +If the target does not have extensive loops or functions that are called +a lot then this can give a small performance boost. diff --git a/instrumentation/README.ngram.md b/instrumentation/README.ngram.md new file mode 100644 index 00000000..de3ba432 --- /dev/null +++ b/instrumentation/README.ngram.md @@ -0,0 +1,28 @@ +# AFL N-Gram Branch Coverage + +## Source + +This is an LLVM-based implementation of the n-gram branch coverage proposed in +the paper ["Be Sensitive and Collaborative: Analzying Impact of Coverage Metrics +in Greybox Fuzzing"](https://www.usenix.org/system/files/raid2019-wang-jinghan.pdf), +by Jinghan Wang, et. al. + +Note that the original implementation (available +[here](https://github.com/bitsecurerlab/afl-sensitive)) +is built on top of AFL's QEMU mode. +This is essentially a port that uses LLVM vectorized instructions to achieve +the same results when compiling source code. + +In math the branch coverage is performed as follows: +`map[current_location ^ prev_location[0] >> 1 ^ prev_location[1] >> 1 ^ ... up to n-1`] += 1` + +## Usage + +The size of `n` (i.e., the number of branches to remember) is an option +that is specified either in the `AFL_LLVM_INSTRUMENT=NGRAM-{value}` or the +`AFL_LLVM_NGRAM_SIZE` environment variable. +Good values are 2, 4 or 8, valid are 2-16. + +It is highly recommended to increase the MAP_SIZE_POW2 definition in +config.h to at least 18 and maybe up to 20 for this as otherwise too +many map collisions occur. diff --git a/instrumentation/README.persistent_mode.md b/instrumentation/README.persistent_mode.md new file mode 100644 index 00000000..e095f036 --- /dev/null +++ b/instrumentation/README.persistent_mode.md @@ -0,0 +1,209 @@ +# llvm_mode persistent mode + +## 1) Introduction + +The most effective way is to fuzz in persistent mode, as the speed can easily +be x10 or x20 times faster without any disadvanges. +*All professional fuzzing is using this mode.* + +This requires that the target can be called in a (or several) function(s), +and that its state can be resetted so that multiple calls can be performed +without resource leaks and former runs having no impact on following runs +(this can be seen by the `stability` indicator in the `afl-fuzz` UI). + +Examples can be found in [examples/persistent_mode](../examples/persistent_mode). + +## 2) TLDR; + +Example `fuzz_target.c`: +``` +#include "what_you_need_for_your_target.h" + +__AFL_FUZZ_INIT(); + +main() { + +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif + + unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT + + while (__AFL_LOOP(10000)) { + + int len = __AFL_FUZZ_TESTCASE_LEN; + if (len < 8) continue; // check for a required/useful minimum input length + + /* Setup function call, e.g. struct target *tmp = libtarget_init() */ + /* Call function to be fuzzed, e.g.: */ + target_function(buf, len); + /* Reset state. e.g. libtarget_free(tmp) */ + + } + + return 0; + +} +``` +And then compile: +``` +afl-clang-fast -o fuzz_target fuzz_target.c -lwhat_you_need_for_your_target +``` +And that is it! +The speed increase is usually x10 to x20. + +If you want to be able to compile the target without afl-clang-fast/lto then +add this just after the includes: + +``` +#ifndef __AFL_FUZZ_TESTCASE_LEN + ssize_t fuzz_len; + #define __AFL_FUZZ_TESTCASE_LEN fuzz_len + unsigned char fuzz_buf[1024000]; + #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf + #define __AFL_FUZZ_INIT() void sync(void); + #define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? + #define __AFL_INIT() sync() +#endif +``` + +## 3) Deferred initialization + +AFL tries to optimize performance by executing the targeted binary just once, +stopping it just before main(), and then cloning this "main" process to get +a steady supply of targets to fuzz. + +Although this approach eliminates much of the OS-, linker- and libc-level +costs of executing the program, it does not always help with binaries that +perform other time-consuming initialization steps - say, parsing a large config +file before getting to the fuzzed data. + +In such cases, it's beneficial to initialize the forkserver a bit later, once +most of the initialization work is already done, but before the binary attempts +to read the fuzzed input and parse it; in some cases, this can offer a 10x+ +performance gain. You can implement delayed initialization in LLVM mode in a +fairly simple way. + +First, find a suitable location in the code where the delayed cloning can +take place. This needs to be done with *extreme* care to avoid breaking the +binary. In particular, the program will probably malfunction if you select +a location after: + + - The creation of any vital threads or child processes - since the forkserver + can't clone them easily. + + - The initialization of timers via setitimer() or equivalent calls. + + - The creation of temporary files, network sockets, offset-sensitive file + descriptors, and similar shared-state resources - but only provided that + their state meaningfully influences the behavior of the program later on. + + - Any access to the fuzzed input, including reading the metadata about its + size. + +With the location selected, add this code in the appropriate spot: + +```c +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif +``` + +You don't need the #ifdef guards, but including them ensures that the program +will keep working normally when compiled with a tool other than afl-clang-fast. + +Finally, recompile the program with afl-clang-fast/lto (afl-gcc or afl-clang will +*not* generate a deferred-initialization binary) - and you should be all set! + +*NOTE:* In the code between `main` and `__AFL_INIT()` should not be any code +run that is instrumented - otherwise a crash might occure. +In case this is useful (e.g. for expensive one time initialization) you can +try to do the following: + +Add after the includes: +``` +extern unsigned char *__afl_area_ptr; +#define MAX_DUMMY_SIZE 256000 + +__attribute__((constructor(1))) void __afl_protect(void) { +#ifdef MAP_FIXED_NOREPLACE + __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if ((uint64_t)__afl_area_ptr == -1) +#endif + __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if ((uint64_t)__afl_area_ptr == -1) + __afl_area_ptr = (unsigned char*) mmap(NULL, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); +} + +``` +and just before `__AFL_INIT()`: +``` + munmap(__afl_area_ptr, MAX_DUMMY_SIZE); + __afl_area_ptr = NULL; +``` + +## 4) Persistent mode + +Some libraries provide APIs that are stateless, or whose state can be reset in +between processing different input files. When such a reset is performed, a +single long-lived process can be reused to try out multiple test cases, +eliminating the need for repeated fork() calls and the associated OS overhead. + +The basic structure of the program that does this would be: + +```c + while (__AFL_LOOP(1000)) { + + /* Read input data. */ + /* Call library code to be fuzzed. */ + /* Reset state. */ + + } + + /* Exit normally */ +``` + +The numerical value specified within the loop controls the maximum number +of iterations before AFL will restart the process from scratch. This minimizes +the impact of memory leaks and similar glitches; 1000 is a good starting point, +and going much higher increases the likelihood of hiccups without giving you +any real performance benefits. + +A more detailed template is shown in ../examples/persistent_demo/. +Similarly to the previous mode, the feature works only with afl-clang-fast; #ifdef +guards can be used to suppress it when using other compilers. + +Note that as with the previous mode, the feature is easy to misuse; if you +do not fully reset the critical state, you may end up with false positives or +waste a whole lot of CPU power doing nothing useful at all. Be particularly +wary of memory leaks and of the state of file descriptors. + +PS. Because there are task switches still involved, the mode isn't as fast as +"pure" in-process fuzzing offered, say, by LLVM's LibFuzzer; but it is a lot +faster than the normal fork() model, and compared to in-process fuzzing, +should be a lot more robust. + +## 5) Shared memory fuzzing + +You can speed up the fuzzing process even more by receiving the fuzzing data +via shared memory instead of stdin or files. +This is a further speed multiplier of about 2x. + +Setting this up is very easy: + +After the includes set the following macro: + +``` +__AFL_FUZZ_INIT(); +``` +Directly at the start of main - or if you are using the deferred forkserver +with `__AFL_INIT()` then *after* `__AFL_INIT? : +``` + unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; +``` + +Then as first line after the `__AFL_LOOP` while loop: +``` + int len = __AFL_FUZZ_TESTCASE_LEN; +``` +and that is all! diff --git a/instrumentation/README.snapshot.md b/instrumentation/README.snapshot.md new file mode 100644 index 00000000..c40a956a --- /dev/null +++ b/instrumentation/README.snapshot.md @@ -0,0 +1,16 @@ +# AFL++ snapshot feature + +Snapshotting is a feature that makes a snapshot from a process and then +restores its state, which is faster then forking it again. + +All targets compiled with llvm_mode are automatically enabled for the +snapshot feature. + +To use the snapshot feature for fuzzing compile and load this kernel +module: [https://github.com/AFLplusplus/AFL-Snapshot-LKM](https://github.com/AFLplusplus/AFL-Snapshot-LKM) + +Note that is has little value for persistent (__AFL_LOOP) fuzzing. + +## Notes + +Snapshot does not work with multithreaded targets yet. Still in WIP, it is now usable only for single threaded applications. diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc new file mode 100644 index 00000000..1dd65188 --- /dev/null +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -0,0 +1,1503 @@ +/* SanitizeCoverage.cpp ported to afl++ LTO :-) */ + +#define AFL_LLVM_PASS + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Analysis/EHPersonalities.h" +#include "llvm/Analysis/PostDominators.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Mangler.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/SpecialCaseList.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +#include "config.h" +#include "debug.h" +#include "afl-llvm-common.h" + +using namespace llvm; + +#define DEBUG_TYPE "sancov" + +static const char *const SanCovTracePCIndirName = + "__sanitizer_cov_trace_pc_indir"; +static const char *const SanCovTracePCName = "__sanitizer_cov_trace_pc"; +// static const char *const SanCovTracePCGuardName = +// "__sanitizer_cov_trace_pc_guard"; +static const char *const SanCovGuardsSectionName = "sancov_guards"; +static const char *const SanCovCountersSectionName = "sancov_cntrs"; +static const char *const SanCovBoolFlagSectionName = "sancov_bools"; +static const char *const SanCovPCsSectionName = "sancov_pcs"; + +static cl::opt ClCoverageLevel( + "lto-coverage-level", + cl::desc("Sanitizer Coverage. 0: none, 1: entry block, 2: all blocks, " + "3: all blocks and critical edges"), + cl::Hidden, cl::init(3)); + +static cl::opt ClTracePC("lto-coverage-trace-pc", + cl::desc("Experimental pc tracing"), cl::Hidden, + cl::init(false)); + +static cl::opt ClTracePCGuard("lto-coverage-trace-pc-guard", + cl::desc("pc tracing with a guard"), + cl::Hidden, cl::init(false)); + +// If true, we create a global variable that contains PCs of all instrumented +// BBs, put this global into a named section, and pass this section's bounds +// to __sanitizer_cov_pcs_init. +// This way the coverage instrumentation does not need to acquire the PCs +// at run-time. Works with trace-pc-guard, inline-8bit-counters, and +// inline-bool-flag. +static cl::opt ClCreatePCTable("lto-coverage-pc-table", + cl::desc("create a static PC table"), + cl::Hidden, cl::init(false)); + +static cl::opt ClInline8bitCounters( + "lto-coverage-inline-8bit-counters", + cl::desc("increments 8-bit counter for every edge"), cl::Hidden, + cl::init(false)); + +static cl::opt ClInlineBoolFlag( + "lto-coverage-inline-bool-flag", + cl::desc("sets a boolean flag for every edge"), cl::Hidden, + cl::init(false)); + +static cl::opt ClPruneBlocks( + "lto-coverage-prune-blocks", + cl::desc("Reduce the number of instrumented blocks"), cl::Hidden, + cl::init(true)); + +namespace { + +SanitizerCoverageOptions getOptions(int LegacyCoverageLevel) { + + SanitizerCoverageOptions Res; + switch (LegacyCoverageLevel) { + + case 0: + Res.CoverageType = SanitizerCoverageOptions::SCK_None; + break; + case 1: + Res.CoverageType = SanitizerCoverageOptions::SCK_Function; + break; + case 2: + Res.CoverageType = SanitizerCoverageOptions::SCK_BB; + break; + case 3: + Res.CoverageType = SanitizerCoverageOptions::SCK_Edge; + break; + case 4: + Res.CoverageType = SanitizerCoverageOptions::SCK_Edge; + Res.IndirectCalls = true; + break; + + } + + return Res; + +} + +SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) { + + // Sets CoverageType and IndirectCalls. + SanitizerCoverageOptions CLOpts = getOptions(ClCoverageLevel); + Options.CoverageType = std::max(Options.CoverageType, CLOpts.CoverageType); + Options.IndirectCalls |= CLOpts.IndirectCalls; + Options.TracePC |= ClTracePC; + Options.TracePCGuard |= ClTracePCGuard; + Options.Inline8bitCounters |= ClInline8bitCounters; + Options.InlineBoolFlag |= ClInlineBoolFlag; + Options.PCTable |= ClCreatePCTable; + Options.NoPrune |= !ClPruneBlocks; + if (!Options.TracePCGuard && !Options.TracePC && + !Options.Inline8bitCounters && !Options.InlineBoolFlag) + Options.TracePCGuard = true; // TracePCGuard is default. + return Options; + +} + +using DomTreeCallback = function_ref; +using PostDomTreeCallback = + function_ref; + +class ModuleSanitizerCoverage { + + public: + ModuleSanitizerCoverage( + const SanitizerCoverageOptions &Options = SanitizerCoverageOptions()) + : Options(OverrideFromCL(Options)) { + + /* , + const SpecialCaseList * Allowlist = nullptr, + const SpecialCaseList * Blocklist = nullptr) + , + Allowlist(Allowlist), + Blocklist(Blocklist) { + + */ + + } + + bool instrumentModule(Module &M, DomTreeCallback DTCallback, + PostDomTreeCallback PDTCallback); + + private: + void instrumentFunction(Function &F, DomTreeCallback DTCallback, + PostDomTreeCallback PDTCallback); + void InjectCoverageForIndirectCalls(Function & F, + ArrayRef IndirCalls); + bool InjectCoverage(Function &F, ArrayRef AllBlocks, + bool IsLeafFunc = true); + GlobalVariable *CreateFunctionLocalArrayInSection(size_t NumElements, + Function &F, Type *Ty, + const char *Section); + GlobalVariable *CreatePCArray(Function &F, ArrayRef AllBlocks); + void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks); + void InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx, + bool IsLeafFunc = true); + std::pair CreateSecStartEnd(Module &M, const char *Section, + Type *Ty); + + void SetNoSanitizeMetadata(Instruction *I) { + + I->setMetadata(I->getModule()->getMDKindID("nosanitize"), + MDNode::get(*C, None)); + + } + + std::string getSectionName(const std::string &Section) const; + std::string getSectionStart(const std::string &Section) const; + std::string getSectionEnd(const std::string &Section) const; + FunctionCallee SanCovTracePCIndir; + FunctionCallee SanCovTracePC /*, SanCovTracePCGuard*/; + Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy, + *Int16Ty, *Int8Ty, *Int8PtrTy, *Int1Ty, *Int1PtrTy; + Module * CurModule; + std::string CurModuleUniqueId; + Triple TargetTriple; + LLVMContext * C; + const DataLayout *DL; + + GlobalVariable *FunctionGuardArray; // for trace-pc-guard. + GlobalVariable *Function8bitCounterArray; // for inline-8bit-counters. + GlobalVariable *FunctionBoolArray; // for inline-bool-flag. + GlobalVariable *FunctionPCsArray; // for pc-table. + SmallVector GlobalsToAppendToUsed; + SmallVector GlobalsToAppendToCompilerUsed; + + SanitizerCoverageOptions Options; + + // afl++ START + // const SpecialCaseList * Allowlist; + // const SpecialCaseList * Blocklist; + uint32_t autodictionary = 1; + uint32_t inst = 0; + uint32_t afl_global_id = 0; + uint64_t map_addr = 0; + char * skip_nozero = NULL; + std::vector BlockList; + DenseMap valueMap; + std::vector dictionary; + IntegerType * Int8Tyi = NULL; + IntegerType * Int32Tyi = NULL; + IntegerType * Int64Tyi = NULL; + ConstantInt * Zero = NULL; + ConstantInt * One = NULL; + LLVMContext * Ct = NULL; + Module * Mo = NULL; + GlobalVariable * AFLMapPtr = NULL; + Value * MapPtrFixed = NULL; + FILE * documentFile = NULL; + // afl++ END + +}; + +class ModuleSanitizerCoverageLegacyPass : public ModulePass { + + public: + static char ID; + StringRef getPassName() const override { + + return "sancov"; + + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + + AU.addRequired(); + AU.addRequired(); + + } + + ModuleSanitizerCoverageLegacyPass( + const SanitizerCoverageOptions &Options = SanitizerCoverageOptions()) + : ModulePass(ID), Options(Options) { + + /* , + const std::vector &AllowlistFiles = + std::vector(), + const std::vector &BlocklistFiles = + std::vector()) + if (AllowlistFiles.size() > 0) + Allowlist = SpecialCaseList::createOrDie(AllowlistFiles, + *vfs::getRealFileSystem()); + if (BlocklistFiles.size() > 0) + Blocklist = SpecialCaseList::createOrDie(BlocklistFiles, + *vfs::getRealFileSystem()); + */ + initializeModuleSanitizerCoverageLegacyPassPass( + *PassRegistry::getPassRegistry()); + + } + + bool runOnModule(Module &M) override { + + ModuleSanitizerCoverage ModuleSancov(Options); + // , Allowlist.get(), Blocklist.get()); + auto DTCallback = [this](Function &F) -> const DominatorTree * { + + return &this->getAnalysis(F).getDomTree(); + + }; + + auto PDTCallback = [this](Function &F) -> const PostDominatorTree * { + + return &this->getAnalysis(F) + .getPostDomTree(); + + }; + + return ModuleSancov.instrumentModule(M, DTCallback, PDTCallback); + + } + + private: + SanitizerCoverageOptions Options; + + // std::unique_ptr Allowlist; + // std::unique_ptr Blocklist; + +}; + +} // namespace + +PreservedAnalyses ModuleSanitizerCoveragePass::run(Module & M, + ModuleAnalysisManager &MAM) { + + ModuleSanitizerCoverage ModuleSancov(Options); + // Allowlist.get(), Blocklist.get()); + auto &FAM = MAM.getResult(M).getManager(); + auto DTCallback = [&FAM](Function &F) -> const DominatorTree * { + + return &FAM.getResult(F); + + }; + + auto PDTCallback = [&FAM](Function &F) -> const PostDominatorTree * { + + return &FAM.getResult(F); + + }; + + if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback)) + return PreservedAnalyses::none(); + + return PreservedAnalyses::all(); + +} + +std::pair ModuleSanitizerCoverage::CreateSecStartEnd( + Module &M, const char *Section, Type *Ty) { + + GlobalVariable *SecStart = + new GlobalVariable(M, Ty, false, GlobalVariable::ExternalLinkage, nullptr, + getSectionStart(Section)); + SecStart->setVisibility(GlobalValue::HiddenVisibility); + GlobalVariable *SecEnd = + new GlobalVariable(M, Ty, false, GlobalVariable::ExternalLinkage, nullptr, + getSectionEnd(Section)); + SecEnd->setVisibility(GlobalValue::HiddenVisibility); + IRBuilder<> IRB(M.getContext()); + Value * SecEndPtr = IRB.CreatePointerCast(SecEnd, Ty); + if (!TargetTriple.isOSBinFormatCOFF()) + return std::make_pair(IRB.CreatePointerCast(SecStart, Ty), SecEndPtr); + + // Account for the fact that on windows-msvc __start_* symbols actually + // point to a uint64_t before the start of the array. + auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy); + auto GEP = IRB.CreateGEP(Int8Ty, SecStartI8Ptr, + ConstantInt::get(IntptrTy, sizeof(uint64_t))); + return std::make_pair(IRB.CreatePointerCast(GEP, Ty), SecEndPtr); + +} + +bool ModuleSanitizerCoverage::instrumentModule( + Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { + + if (Options.CoverageType == SanitizerCoverageOptions::SCK_None) return false; + /* + if (Allowlist && + !Allowlist->inSection("coverage", "src", M.getSourceFileName())) + return false; + if (Blocklist && + Blocklist->inSection("coverage", "src", M.getSourceFileName())) + return false; + */ + BlockList.clear(); + valueMap.clear(); + dictionary.clear(); + C = &(M.getContext()); + DL = &M.getDataLayout(); + CurModule = &M; + CurModuleUniqueId = getUniqueModuleId(CurModule); + TargetTriple = Triple(M.getTargetTriple()); + FunctionGuardArray = nullptr; + Function8bitCounterArray = nullptr; + FunctionBoolArray = nullptr; + FunctionPCsArray = nullptr; + IntptrTy = Type::getIntNTy(*C, DL->getPointerSizeInBits()); + IntptrPtrTy = PointerType::getUnqual(IntptrTy); + Type * VoidTy = Type::getVoidTy(*C); + IRBuilder<> IRB(*C); + Int64PtrTy = PointerType::getUnqual(IRB.getInt64Ty()); + Int32PtrTy = PointerType::getUnqual(IRB.getInt32Ty()); + Int8PtrTy = PointerType::getUnqual(IRB.getInt8Ty()); + Int1PtrTy = PointerType::getUnqual(IRB.getInt1Ty()); + Int64Ty = IRB.getInt64Ty(); + Int32Ty = IRB.getInt32Ty(); + Int16Ty = IRB.getInt16Ty(); + Int8Ty = IRB.getInt8Ty(); + Int1Ty = IRB.getInt1Ty(); + + /* afl++ START */ + char * ptr; + LLVMContext &Ctx = M.getContext(); + Ct = &Ctx; + Int8Tyi = IntegerType::getInt8Ty(Ctx); + Int32Tyi = IntegerType::getInt32Ty(Ctx); + Int64Tyi = IntegerType::getInt64Ty(Ctx); + + /* Show a banner */ + setvbuf(stdout, NULL, _IONBF, 0); + if (getenv("AFL_DEBUG")) debug = 1; + + if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { + + SAYF(cCYA "afl-llvm-lto" VERSION cRST + " by Marc \"vanHauser\" Heuse \n"); + + } else + + be_quiet = 1; + + skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); + + if ((ptr = getenv("AFL_LLVM_LTO_STARTID")) != NULL) + if ((afl_global_id = atoi(ptr)) < 0) + FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is negative\n", ptr); + + if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { + + if ((documentFile = fopen(ptr, "a")) == NULL) + WARNF("Cannot access document file %s", ptr); + + } + + // we make this the default as the fixed map has problems with + // defered forkserver, early constructors, ifuncs and maybe more + /*if (getenv("AFL_LLVM_MAP_DYNAMIC"))*/ + map_addr = 0; + + if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { + + uint64_t val; + if (!*ptr || !strcmp(ptr, "0") || !strcmp(ptr, "0x0")) { + + map_addr = 0; + + } else if (getenv("AFL_LLVM_MAP_DYNAMIC")) { + + FATAL( + "AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used together"); + + } else if (strncmp(ptr, "0x", 2) != 0) { + + map_addr = 0x10000; // the default + + } else { + + val = strtoull(ptr, NULL, 16); + if (val < 0x100 || val > 0xffffffff00000000) { + + FATAL( + "AFL_LLVM_MAP_ADDR must be a value between 0x100 and " + "0xffffffff00000000"); + + } + + map_addr = val; + + } + + } + + /* Get/set the globals for the SHM region. */ + + if (!map_addr) { + + AFLMapPtr = + new GlobalVariable(M, PointerType::get(Int8Tyi, 0), false, + GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); + + } else { + + ConstantInt *MapAddr = ConstantInt::get(Int64Tyi, map_addr); + MapPtrFixed = + ConstantExpr::getIntToPtr(MapAddr, PointerType::getUnqual(Int8Tyi)); + + } + + Zero = ConstantInt::get(Int8Tyi, 0); + One = ConstantInt::get(Int8Tyi, 1); + + scanForDangerousFunctions(&M); + Mo = &M; + + if (autodictionary) { + + for (auto &F : M) { + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast(&IN))) { + + bool isStrcmp = true; + bool isMemcmp = true; + bool isStrncmp = true; + bool isStrcasecmp = true; + bool isStrncasecmp = true; + bool isIntMemcpy = true; + bool addedNull = false; + size_t optLen = 0; + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + std::string FuncName = Callee->getName().str(); + isStrcmp &= !FuncName.compare("strcmp"); + isMemcmp &= !FuncName.compare("memcmp"); + isStrncmp &= !FuncName.compare("strncmp"); + isStrcasecmp &= !FuncName.compare("strcasecmp"); + isStrncasecmp &= !FuncName.compare("strncasecmp"); + isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); + + /* we do something different here, putting this BB and the + successors in a block map */ + if (!FuncName.compare("__afl_persistent_loop")) { + + BlockList.push_back(&BB); + for (succ_iterator SI = succ_begin(&BB), SE = succ_end(&BB); + SI != SE; ++SI) { + + BasicBlock *succ = *SI; + BlockList.push_back(succ); + + } + + } + + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && + !isStrncasecmp && !isIntMemcpy) + continue; + + /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function + * prototype */ + FunctionType *FT = Callee->getFunctionType(); + + isStrcmp &= FT->getNumParams() == 2 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()); + isStrcasecmp &= FT->getNumParams() == 2 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()); + isMemcmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy() && + FT->getParamType(2)->isIntegerTy(); + isStrncmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()) && + FT->getParamType(2)->isIntegerTy(); + isStrncasecmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()) && + FT->getParamType(2)->isIntegerTy(); + + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && + !isStrncasecmp && !isIntMemcpy) + continue; + + /* is a str{n,}{case,}cmp/memcmp, check if we have + * str{case,}cmp(x, "const") or str{case,}cmp("const", x) + * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, ..) + * memcmp(x, "const", ..) or memcmp("const", x, ..) */ + Value *Str1P = callInst->getArgOperand(0), + *Str2P = callInst->getArgOperand(1); + std::string Str1, Str2; + StringRef TmpStr; + bool HasStr1 = getConstantStringInfo(Str1P, TmpStr); + if (TmpStr.empty()) + HasStr1 = false; + else + Str1 = TmpStr.str(); + bool HasStr2 = getConstantStringInfo(Str2P, TmpStr); + if (TmpStr.empty()) + HasStr2 = false; + else + Str2 = TmpStr.str(); + + if (debug) + fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n", + FuncName.c_str(), Str1P, Str1P->getName().str().c_str(), + Str1.c_str(), HasStr1 == true ? "true" : "false", Str2P, + Str2P->getName().str().c_str(), Str2.c_str(), + HasStr2 == true ? "true" : "false"); + + // we handle the 2nd parameter first because of llvm memcpy + if (!HasStr2) { + + auto *Ptr = dyn_cast(Str2P); + if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { + + if (auto *Var = dyn_cast(Ptr->getOperand(0))) { + + if (Var->hasInitializer()) { + + if (auto *Array = dyn_cast( + Var->getInitializer())) { + + HasStr2 = true; + Str2 = Array->getAsString().str(); + + } + + } + + } + + } + + } + + // for the internal memcpy routine we only care for the second + // parameter and are not reporting anything. + if (isIntMemcpy == true) { + + if (HasStr2 == true) { + + Value * op2 = callInst->getArgOperand(2); + ConstantInt *ilen = dyn_cast(op2); + if (ilen) { + + uint64_t literalLength = Str2.size(); + uint64_t optLength = ilen->getZExtValue(); + if (literalLength + 1 == optLength) { + + Str2.append("\0", 1); // add null byte + addedNull = true; + + } + + } + + valueMap[Str1P] = new std::string(Str2); + + if (debug) + fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), Str1P); + continue; + + } + + continue; + + } + + // Neither a literal nor a global variable? + // maybe it is a local variable that we saved + if (!HasStr2) { + + std::string *strng = valueMap[Str2P]; + if (strng && !strng->empty()) { + + Str2 = *strng; + HasStr2 = true; + if (debug) + fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(), + Str2P); + + } + + } + + if (!HasStr1) { + + auto Ptr = dyn_cast(Str1P); + + if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { + + if (auto *Var = dyn_cast(Ptr->getOperand(0))) { + + if (Var->hasInitializer()) { + + if (auto *Array = dyn_cast( + Var->getInitializer())) { + + HasStr1 = true; + Str1 = Array->getAsString().str(); + + } + + } + + } + + } + + } + + // Neither a literal nor a global variable? + // maybe it is a local variable that we saved + if (!HasStr1) { + + std::string *strng = valueMap[Str1P]; + if (strng && !strng->empty()) { + + Str1 = *strng; + HasStr1 = true; + if (debug) + fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(), + Str1P); + + } + + } + + /* handle cases of one string is const, one string is variable */ + if (!(HasStr1 ^ HasStr2)) continue; + + std::string thestring; + + if (HasStr1) + thestring = Str1; + else + thestring = Str2; + + optLen = thestring.length(); + + if (isMemcmp || isStrncmp || isStrncasecmp) { + + Value * op2 = callInst->getArgOperand(2); + ConstantInt *ilen = dyn_cast(op2); + if (ilen) { + + uint64_t literalLength = optLen; + optLen = ilen->getZExtValue(); + if (literalLength + 1 == optLen) { // add null byte + thestring.append("\0", 1); + addedNull = true; + + } + + } + + } + + // add null byte if this is a string compare function and a null + // was not already added + if (!isMemcmp) { + + if (addedNull == false) { + + thestring.append("\0", 1); // add null byte + optLen++; + + } + + // ensure we do not have garbage + size_t offset = thestring.find('\0', 0); + if (offset + 1 < optLen) optLen = offset + 1; + thestring = thestring.substr(0, optLen); + + } + + if (!be_quiet) { + + std::string outstring; + fprintf(stderr, "%s: length %zu/%zu \"", FuncName.c_str(), optLen, + thestring.length()); + for (uint8_t i = 0; i < thestring.length(); i++) { + + uint8_t c = thestring[i]; + if (c <= 32 || c >= 127) + fprintf(stderr, "\\x%02x", c); + else + fprintf(stderr, "%c", c); + + } + + fprintf(stderr, "\"\n"); + + } + + // we take the longer string, even if the compare was to a + // shorter part. Note that depending on the optimizer of the + // compiler this can be wrong, but it is more likely that this + // is helping the fuzzer + if (optLen != thestring.length()) optLen = thestring.length(); + if (optLen > MAX_AUTO_EXTRA) optLen = MAX_AUTO_EXTRA; + if (optLen < MIN_AUTO_EXTRA) // too short? skip + continue; + + dictionary.push_back(thestring.substr(0, optLen)); + + } + + } + + } + + } + + } + + // afl++ END + + SanCovTracePCIndir = + M.getOrInsertFunction(SanCovTracePCIndirName, VoidTy, IntptrTy); + // Make sure smaller parameters are zero-extended to i64 as required by the + // x86_64 ABI. + AttributeList SanCovTraceCmpZeroExtAL; + if (TargetTriple.getArch() == Triple::x86_64) { + + SanCovTraceCmpZeroExtAL = + SanCovTraceCmpZeroExtAL.addParamAttribute(*C, 0, Attribute::ZExt); + SanCovTraceCmpZeroExtAL = + SanCovTraceCmpZeroExtAL.addParamAttribute(*C, 1, Attribute::ZExt); + + } + + SanCovTracePC = M.getOrInsertFunction(SanCovTracePCName, VoidTy); + + // SanCovTracePCGuard = + // M.getOrInsertFunction(SanCovTracePCGuardName, VoidTy, Int32PtrTy); + + for (auto &F : M) + instrumentFunction(F, DTCallback, PDTCallback); + + // afl++ START + if (documentFile) { + + fclose(documentFile); + documentFile = NULL; + + } + + if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) { + + // yes we could create our own function, insert it into ctors ... + // but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o + + Function *f = M.getFunction("__afl_auto_init_globals"); + + if (!f) { + + fprintf(stderr, + "Error: init function could not be found (this should not " + "happen)\n"); + exit(-1); + + } + + BasicBlock *bb = &f->getEntryBlock(); + if (!bb) { + + fprintf(stderr, + "Error: init function does not have an EntryBlock (this should " + "not happen)\n"); + exit(-1); + + } + + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + + if (map_addr) { + + GlobalVariable *AFLMapAddrFixed = new GlobalVariable( + M, Int64Tyi, true, GlobalValue::ExternalLinkage, 0, "__afl_map_addr"); + ConstantInt *MapAddr = ConstantInt::get(Int64Tyi, map_addr); + StoreInst * StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed); + StoreMapAddr->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(Ctx, None)); + + } + + if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) { + + uint32_t write_loc = afl_global_id; + + if (afl_global_id % 8) write_loc = (((afl_global_id + 8) >> 3) << 3); + + GlobalVariable *AFLFinalLoc = + new GlobalVariable(M, Int32Tyi, true, GlobalValue::ExternalLinkage, 0, + "__afl_final_loc"); + ConstantInt *const_loc = ConstantInt::get(Int32Tyi, write_loc); + StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc); + StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(Ctx, None)); + + } + + if (dictionary.size()) { + + size_t memlen = 0, count = 0, offset = 0; + char * ptr; + + for (auto token : dictionary) { + + memlen += token.length(); + count++; + + } + + if (!be_quiet) + printf("AUTODICTIONARY: %lu string%s found\n", count, + count == 1 ? "" : "s"); + + if (count) { + + if ((ptr = (char *)malloc(memlen + count)) == NULL) { + + fprintf(stderr, "Error: malloc for %lu bytes failed!\n", + memlen + count); + exit(-1); + + } + + count = 0; + + for (auto token : dictionary) { + + if (offset + token.length() < 0xfffff0 && count < MAX_AUTO_EXTRAS) { + + ptr[offset++] = (uint8_t)token.length(); + memcpy(ptr + offset, token.c_str(), token.length()); + offset += token.length(); + count++; + + } + + } + + GlobalVariable *AFLDictionaryLen = + new GlobalVariable(M, Int32Tyi, false, GlobalValue::ExternalLinkage, + 0, "__afl_dictionary_len"); + ConstantInt *const_len = ConstantInt::get(Int32Tyi, offset); + StoreInst *StoreDictLen = IRB.CreateStore(const_len, AFLDictionaryLen); + StoreDictLen->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(Ctx, None)); + + ArrayType *ArrayTy = ArrayType::get(IntegerType::get(Ctx, 8), offset); + GlobalVariable *AFLInternalDictionary = new GlobalVariable( + M, ArrayTy, true, GlobalValue::ExternalLinkage, + ConstantDataArray::get(Ctx, + *(new ArrayRef((char *)ptr, offset))), + "__afl_internal_dictionary"); + AFLInternalDictionary->setInitializer(ConstantDataArray::get( + Ctx, *(new ArrayRef((char *)ptr, offset)))); + AFLInternalDictionary->setConstant(true); + + GlobalVariable *AFLDictionary = new GlobalVariable( + M, PointerType::get(Int8Tyi, 0), false, + GlobalValue::ExternalLinkage, 0, "__afl_dictionary"); + + Value *AFLDictOff = IRB.CreateGEP(AFLInternalDictionary, Zero); + Value *AFLDictPtr = + IRB.CreatePointerCast(AFLDictOff, PointerType::get(Int8Tyi, 0)); + StoreInst *StoreDict = IRB.CreateStore(AFLDictPtr, AFLDictionary); + StoreDict->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(Ctx, None)); + + } + + } + + } + + /* Say something nice. */ + + if (!be_quiet) { + + if (!inst) + WARNF("No instrumentation targets found."); + else { + + char modeline[100]; + snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", + getenv("AFL_HARDEN") ? "hardened" : "non-hardened", + getenv("AFL_USE_ASAN") ? ", ASAN" : "", + getenv("AFL_USE_MSAN") ? ", MSAN" : "", + getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", + getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); + OKF("Instrumented %u locations with no collisions (on average %llu " + "collisions would be in afl-gcc/afl-clang-fast) (%s mode).", + inst, calculateCollisions(inst), modeline); + + } + + } + + // afl++ END + + // We don't reference these arrays directly in any of our runtime functions, + // so we need to prevent them from being dead stripped. + if (TargetTriple.isOSBinFormatMachO()) appendToUsed(M, GlobalsToAppendToUsed); + appendToCompilerUsed(M, GlobalsToAppendToCompilerUsed); + return true; + +} + +// True if block has successors and it dominates all of them. +static bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) { + + if (succ_begin(BB) == succ_end(BB)) return false; + + for (const BasicBlock *SUCC : make_range(succ_begin(BB), succ_end(BB))) { + + if (!DT->dominates(BB, SUCC)) return false; + + } + + return true; + +} + +// True if block has predecessors and it postdominates all of them. +static bool isFullPostDominator(const BasicBlock * BB, + const PostDominatorTree *PDT) { + + if (pred_begin(BB) == pred_end(BB)) return false; + + for (const BasicBlock *PRED : make_range(pred_begin(BB), pred_end(BB))) { + + if (!PDT->dominates(BB, PRED)) return false; + + } + + return true; + +} + +static bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB, + const DominatorTree * DT, + const PostDominatorTree * PDT, + const SanitizerCoverageOptions &Options) { + + // Don't insert coverage for blocks containing nothing but unreachable: we + // will never call __sanitizer_cov() for them, so counting them in + // NumberOfInstrumentedBlocks() might complicate calculation of code coverage + // percentage. Also, unreachable instructions frequently have no debug + // locations. + if (isa(BB->getFirstNonPHIOrDbgOrLifetime())) return false; + + // Don't insert coverage into blocks without a valid insertion point + // (catchswitch blocks). + if (BB->getFirstInsertionPt() == BB->end()) return false; + + // afl++ START + if (!Options.NoPrune && &F.getEntryBlock() == BB && F.size() > 1) + return false; + // afl++ END + + if (Options.NoPrune || &F.getEntryBlock() == BB) return true; + + if (Options.CoverageType == SanitizerCoverageOptions::SCK_Function && + &F.getEntryBlock() != BB) + return false; + + // Do not instrument full dominators, or full post-dominators with multiple + // predecessors. + return !isFullDominator(BB, DT) && + !(isFullPostDominator(BB, PDT) && !BB->getSinglePredecessor()); + +} + +void ModuleSanitizerCoverage::instrumentFunction( + Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { + + if (F.empty()) return; + if (F.getName().find(".module_ctor") != std::string::npos) + return; // Should not instrument sanitizer init functions. + if (F.getName().startswith("__sanitizer_")) + return; // Don't instrument __sanitizer_* callbacks. + // Don't touch available_externally functions, their actual body is elewhere. + if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) return; + // Don't instrument MSVC CRT configuration helpers. They may run before normal + // initialization. + if (F.getName() == "__local_stdio_printf_options" || + F.getName() == "__local_stdio_scanf_options") + return; + if (isa(F.getEntryBlock().getTerminator())) return; + // Don't instrument functions using SEH for now. Splitting basic blocks like + // we do for coverage breaks WinEHPrepare. + // FIXME: Remove this when SEH no longer uses landingpad pattern matching. + if (F.hasPersonalityFn() && + isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) + return; + // if (Allowlist && !Allowlist->inSection("coverage", "fun", F.getName())) + // return; + // if (Blocklist && Blocklist->inSection("coverage", "fun", F.getName())) + // return; + + // afl++ START + if (!F.size()) return; + if (isIgnoreFunction(&F)) return; + // afl++ END + + if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge) + SplitAllCriticalEdges( + F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests()); + SmallVector IndirCalls; + SmallVector BlocksToInstrument; + + const DominatorTree * DT = DTCallback(F); + const PostDominatorTree *PDT = PDTCallback(F); + bool IsLeafFunc = true; + + for (auto &BB : F) { + + if (shouldInstrumentBlock(F, &BB, DT, PDT, Options)) + BlocksToInstrument.push_back(&BB); + for (auto &Inst : BB) { + + if (Options.IndirectCalls) { + + CallBase *CB = dyn_cast(&Inst); + if (CB && !CB->getCalledFunction()) IndirCalls.push_back(&Inst); + + } + + } + + } + + InjectCoverage(F, BlocksToInstrument, IsLeafFunc); + InjectCoverageForIndirectCalls(F, IndirCalls); + +} + +GlobalVariable *ModuleSanitizerCoverage::CreateFunctionLocalArrayInSection( + size_t NumElements, Function &F, Type *Ty, const char *Section) { + + ArrayType *ArrayTy = ArrayType::get(Ty, NumElements); + auto Array = new GlobalVariable( + *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage, + Constant::getNullValue(ArrayTy), "__sancov_gen_"); + + if (TargetTriple.supportsCOMDAT() && !F.isInterposable()) + if (auto Comdat = + GetOrCreateFunctionComdat(F, TargetTriple, CurModuleUniqueId)) + Array->setComdat(Comdat); + Array->setSection(getSectionName(Section)); + Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize())); + GlobalsToAppendToUsed.push_back(Array); + GlobalsToAppendToCompilerUsed.push_back(Array); + MDNode *MD = MDNode::get(F.getContext(), ValueAsMetadata::get(&F)); + Array->addMetadata(LLVMContext::MD_associated, *MD); + + return Array; + +} + +GlobalVariable *ModuleSanitizerCoverage::CreatePCArray( + Function &F, ArrayRef AllBlocks) { + + size_t N = AllBlocks.size(); + assert(N); + SmallVector PCs; + IRBuilder<> IRB(&*F.getEntryBlock().getFirstInsertionPt()); + for (size_t i = 0; i < N; i++) { + + if (&F.getEntryBlock() == AllBlocks[i]) { + + PCs.push_back((Constant *)IRB.CreatePointerCast(&F, IntptrPtrTy)); + PCs.push_back((Constant *)IRB.CreateIntToPtr( + ConstantInt::get(IntptrTy, 1), IntptrPtrTy)); + + } else { + + PCs.push_back((Constant *)IRB.CreatePointerCast( + BlockAddress::get(AllBlocks[i]), IntptrPtrTy)); + PCs.push_back((Constant *)IRB.CreateIntToPtr( + ConstantInt::get(IntptrTy, 0), IntptrPtrTy)); + + } + + } + + auto *PCArray = CreateFunctionLocalArrayInSection(N * 2, F, IntptrPtrTy, + SanCovPCsSectionName); + PCArray->setInitializer( + ConstantArray::get(ArrayType::get(IntptrPtrTy, N * 2), PCs)); + PCArray->setConstant(true); + + return PCArray; + +} + +void ModuleSanitizerCoverage::CreateFunctionLocalArrays( + Function &F, ArrayRef AllBlocks) { + + if (Options.TracePCGuard) + FunctionGuardArray = CreateFunctionLocalArrayInSection( + AllBlocks.size(), F, Int32Ty, SanCovGuardsSectionName); + if (Options.Inline8bitCounters) + Function8bitCounterArray = CreateFunctionLocalArrayInSection( + AllBlocks.size(), F, Int8Ty, SanCovCountersSectionName); + if (Options.InlineBoolFlag) + FunctionBoolArray = CreateFunctionLocalArrayInSection( + AllBlocks.size(), F, Int1Ty, SanCovBoolFlagSectionName); + if (Options.PCTable) FunctionPCsArray = CreatePCArray(F, AllBlocks); + +} + +bool ModuleSanitizerCoverage::InjectCoverage(Function & F, + ArrayRef AllBlocks, + bool IsLeafFunc) { + + if (AllBlocks.empty()) return false; + CreateFunctionLocalArrays(F, AllBlocks); + for (size_t i = 0, N = AllBlocks.size(); i < N; i++) { + + // afl++ START + if (BlockList.size()) { + + int skip = 0; + for (uint32_t k = 0; k < BlockList.size(); k++) { + + if (AllBlocks[i] == BlockList[k]) { + + if (debug) + fprintf(stderr, + "DEBUG: Function %s skipping BB with/after __afl_loop\n", + F.getName().str().c_str()); + skip = 1; + + } + + } + + if (skip) continue; + + } + + // afl++ END + + InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc); + + } + + return true; + +} + +// On every indirect call we call a run-time function +// __sanitizer_cov_indir_call* with two parameters: +// - callee address, +// - global cache array that contains CacheSize pointers (zero-initialized). +// The cache is used to speed up recording the caller-callee pairs. +// The address of the caller is passed implicitly via caller PC. +// CacheSize is encoded in the name of the run-time function. +void ModuleSanitizerCoverage::InjectCoverageForIndirectCalls( + Function &F, ArrayRef IndirCalls) { + + if (IndirCalls.empty()) return; + assert(Options.TracePC || Options.TracePCGuard || + Options.Inline8bitCounters || Options.InlineBoolFlag); + for (auto I : IndirCalls) { + + IRBuilder<> IRB(I); + CallBase & CB = cast(*I); + Value * Callee = CB.getCalledOperand(); + if (isa(Callee)) continue; + IRB.CreateCall(SanCovTracePCIndir, IRB.CreatePointerCast(Callee, IntptrTy)); + + } + +} + +void ModuleSanitizerCoverage::InjectCoverageAtBlock(Function &F, BasicBlock &BB, + size_t Idx, + bool IsLeafFunc) { + + BasicBlock::iterator IP = BB.getFirstInsertionPt(); + bool IsEntryBB = &BB == &F.getEntryBlock(); + DebugLoc EntryLoc; + if (IsEntryBB) { + + if (auto SP = F.getSubprogram()) + EntryLoc = DebugLoc::get(SP->getScopeLine(), 0, SP); + // Keep static allocas and llvm.localescape calls in the entry block. Even + // if we aren't splitting the block, it's nice for allocas to be before + // calls. + IP = PrepareToSplitEntryBlock(BB, IP); + + } else { + + EntryLoc = IP->getDebugLoc(); + + } + + IRBuilder<> IRB(&*IP); + IRB.SetCurrentDebugLocation(EntryLoc); + if (Options.TracePC) { + + IRB.CreateCall(SanCovTracePC) +#if LLVM_VERSION_MAJOR < 12 + ->cannotMerge(); // gets the PC using GET_CALLER_PC. +#else + ->setCannotMerge(); // gets the PC using GET_CALLER_PC. +#endif + + } + + if (Options.TracePCGuard) { + + // afl++ START + ++afl_global_id; + + if (documentFile) { + + unsigned long long int moduleID = + (((unsigned long long int)(rand() & 0xffffffff)) << 32) | getpid(); + fprintf(documentFile, "ModuleID=%llu Function=%s edgeID=%u\n", moduleID, + F.getName().str().c_str(), afl_global_id); + + } + + /* Set the ID of the inserted basic block */ + + ConstantInt *CurLoc = ConstantInt::get(Int32Tyi, afl_global_id); + + /* Load SHM pointer */ + + Value *MapPtrIdx; + + if (map_addr) { + + MapPtrIdx = IRB.CreateGEP(MapPtrFixed, CurLoc); + + } else { + + LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); + MapPtr->setMetadata(Mo->getMDKindID("nosanitize"), + MDNode::get(*Ct, None)); + MapPtrIdx = IRB.CreateGEP(MapPtr, CurLoc); + + } + + /* Update bitmap */ + + LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); + Counter->setMetadata(Mo->getMDKindID("nosanitize"), MDNode::get(*Ct, None)); + + Value *Incr = IRB.CreateAdd(Counter, One); + + if (skip_nozero == NULL) { + + auto cf = IRB.CreateICmpEQ(Incr, Zero); + auto carry = IRB.CreateZExt(cf, Int8Tyi); + Incr = IRB.CreateAdd(Incr, carry); + + } + + IRB.CreateStore(Incr, MapPtrIdx) + ->setMetadata(Mo->getMDKindID("nosanitize"), MDNode::get(*Ct, None)); + + // done :) + + inst++; + // afl++ END + + /* + XXXXXXXXXXXXXXXXXXX + + auto GuardPtr = IRB.CreateIntToPtr( + IRB.CreateAdd(IRB.CreatePointerCast(FunctionGuardArray, IntptrTy), + ConstantInt::get(IntptrTy, Idx * 4)), + Int32PtrTy); + + IRB.CreateCall(SanCovTracePCGuard, GuardPtr)->setCannotMerge(); + */ + + } + + if (Options.Inline8bitCounters) { + + auto CounterPtr = IRB.CreateGEP( + Function8bitCounterArray->getValueType(), Function8bitCounterArray, + {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)}); + auto Load = IRB.CreateLoad(Int8Ty, CounterPtr); + auto Inc = IRB.CreateAdd(Load, ConstantInt::get(Int8Ty, 1)); + auto Store = IRB.CreateStore(Inc, CounterPtr); + SetNoSanitizeMetadata(Load); + SetNoSanitizeMetadata(Store); + + } + + if (Options.InlineBoolFlag) { + + auto FlagPtr = IRB.CreateGEP( + FunctionBoolArray->getValueType(), FunctionBoolArray, + {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)}); + auto Load = IRB.CreateLoad(Int1Ty, FlagPtr); + auto ThenTerm = + SplitBlockAndInsertIfThen(IRB.CreateIsNull(Load), &*IP, false); + IRBuilder<> ThenIRB(ThenTerm); + auto Store = ThenIRB.CreateStore(ConstantInt::getTrue(Int1Ty), FlagPtr); + SetNoSanitizeMetadata(Load); + SetNoSanitizeMetadata(Store); + + } + +} + +std::string ModuleSanitizerCoverage::getSectionName( + const std::string &Section) const { + + if (TargetTriple.isOSBinFormatCOFF()) { + + if (Section == SanCovCountersSectionName) return ".SCOV$CM"; + if (Section == SanCovBoolFlagSectionName) return ".SCOV$BM"; + if (Section == SanCovPCsSectionName) return ".SCOVP$M"; + return ".SCOV$GM"; // For SanCovGuardsSectionName. + + } + + if (TargetTriple.isOSBinFormatMachO()) return "__DATA,__" + Section; + return "__" + Section; + +} + +std::string ModuleSanitizerCoverage::getSectionStart( + const std::string &Section) const { + + if (TargetTriple.isOSBinFormatMachO()) + return "\1section$start$__DATA$__" + Section; + return "__start___" + Section; + +} + +std::string ModuleSanitizerCoverage::getSectionEnd( + const std::string &Section) const { + + if (TargetTriple.isOSBinFormatMachO()) + return "\1section$end$__DATA$__" + Section; + return "__stop___" + Section; + +} + +char ModuleSanitizerCoverageLegacyPass::ID = 0; + +INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLegacyPass, "sancov", + "Pass for instrumenting coverage on functions", false, + false) +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) +INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass) +INITIALIZE_PASS_END(ModuleSanitizerCoverageLegacyPass, "sancov", + "Pass for instrumenting coverage on functions", false, + false) + +ModulePass *llvm::createModuleSanitizerCoverageLegacyPassPass( + const SanitizerCoverageOptions &Options, + const std::vector &AllowlistFiles, + const std::vector &BlocklistFiles) { + + return new ModuleSanitizerCoverageLegacyPass(Options); + //, AllowlistFiles, BlocklistFiles); + +} + +static void registerLTOPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + auto p = new ModuleSanitizerCoverageLegacyPass(); + PM.add(p); + +} + +static RegisterStandardPasses RegisterCompTransPass( + PassManagerBuilder::EP_OptimizerLast, registerLTOPass); + +static RegisterStandardPasses RegisterCompTransPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerLTOPass); + +#if LLVM_VERSION_MAJOR >= 11 +static RegisterStandardPasses RegisterCompTransPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerLTOPass); +#endif + diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c new file mode 100644 index 00000000..a3d75b15 --- /dev/null +++ b/instrumentation/afl-compiler-rt.o.c @@ -0,0 +1,1254 @@ +/* + american fuzzy lop++ - instrumentation bootstrap + ------------------------------------------------ + + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + +*/ + +#ifdef __ANDROID__ + #include "android-ashmem.h" +#endif +#include "config.h" +#include "types.h" +#include "cmplog.h" +#include "llvm-ngram-coverage.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "llvm/Config/llvm-config.h" + +#ifdef __linux__ + #include "snapshot-inl.h" +#endif + +/* This is a somewhat ugly hack for the experimental 'trace-pc-guard' mode. + Basically, we need to make sure that the forkserver is initialized after + the LLVM-generated runtime initialization pass, not before. */ + +#ifndef MAP_FIXED_NOREPLACE + #ifdef MAP_EXCL + #define MAP_FIXED_NOREPLACE MAP_EXCL | MAP_FIXED + #else + #define MAP_FIXED_NOREPLACE MAP_FIXED + #endif +#endif + +#define CTOR_PRIO 3 + +#include +#include + +/* Globals needed by the injected instrumentation. The __afl_area_initial region + is used for instrumentation output before __afl_map_shm() has a chance to + run. It will end up as .comm, so it shouldn't be too wasteful. */ + +#if MAP_SIZE <= 65536 + #define MAP_INITIAL_SIZE 256000 +#else + #define MAP_INITIAL_SIZE MAP_SIZE +#endif + +u8 __afl_area_initial[MAP_INITIAL_SIZE]; +u8 * __afl_area_ptr = __afl_area_initial; +u8 * __afl_dictionary; +u8 * __afl_fuzz_ptr; +u32 __afl_fuzz_len_dummy; +u32 *__afl_fuzz_len = &__afl_fuzz_len_dummy; + +u32 __afl_final_loc; +u32 __afl_map_size = MAP_SIZE; +u32 __afl_dictionary_len; +u64 __afl_map_addr; + +#ifdef __ANDROID__ +PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX]; +u32 __afl_prev_ctx; +u32 __afl_cmp_counter; +#else +__thread PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX]; +__thread u32 __afl_prev_ctx; +__thread u32 __afl_cmp_counter; +#endif + +int __afl_sharedmem_fuzzing __attribute__((weak)); + +struct cmp_map *__afl_cmp_map; + +/* Running in persistent mode? */ + +static u8 is_persistent; + +/* Are we in sancov mode? */ + +static u8 _is_sancov; + +/* Uninspired gcc plugin instrumentation */ + +void __afl_trace(const u32 x) { + +#if 1 /* enable for neverZero feature. */ + __afl_area_ptr[__afl_prev_loc[0] ^ x] += + 1 + ((u8)(1 + __afl_area_ptr[__afl_prev_loc[0] ^ x]) == 0); +#else + ++__afl_area_ptr[__afl_prev_loc[0] ^ x]; +#endif + + __afl_prev_loc[0] = (x >> 1); + return; + +} + +/* Error reporting to forkserver controller */ + +void send_forkserver_error(int error) { + + u32 status; + if (!error || error > 0xffff) return; + status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); + if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; + +} + +/* SHM fuzzing setup. */ + +static void __afl_map_shm_fuzz() { + + char *id_str = getenv(SHM_FUZZ_ENV_VAR); + + if (id_str) { + + u8 *map = NULL; + +#ifdef USEMMAP + const char * shm_file_path = id_str; + int shm_fd = -1; + unsigned char *shm_base = NULL; + + /* create the shared memory segment as if it was a file */ + shm_fd = shm_open(shm_file_path, O_RDWR, 0600); + if (shm_fd == -1) { + + fprintf(stderr, "shm_open() failed for fuzz\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); + exit(1); + + } + + map = + (u8 *)mmap(0, MAX_FILE + sizeof(u32), PROT_READ, MAP_SHARED, shm_fd, 0); + +#else + u32 shm_id = atoi(id_str); + map = (u8 *)shmat(shm_id, NULL, 0); + +#endif + + /* Whooooops. */ + + if (!map || map == (void *)-1) { + + perror("Could not access fuzzign shared memory"); + exit(1); + + } + + __afl_fuzz_len = (u32 *)map; + __afl_fuzz_ptr = map + sizeof(u32); + + if (getenv("AFL_DEBUG")) { + + fprintf(stderr, "DEBUG: successfully got fuzzing shared memory\n"); + + } + + } else { + + fprintf(stderr, "Error: variable for fuzzing shared memory is not set\n"); + exit(1); + + } + +} + +/* SHM setup. */ + +static void __afl_map_shm(void) { + + // we we are not running in afl ensure the map exists + if (!__afl_area_ptr) { __afl_area_ptr = __afl_area_initial; } + + char *id_str = getenv(SHM_ENV_VAR); + + if (__afl_final_loc) { + + if (__afl_final_loc % 8) + __afl_final_loc = (((__afl_final_loc + 7) >> 3) << 3); + __afl_map_size = __afl_final_loc; + + if (__afl_final_loc > MAP_SIZE) { + + char *ptr; + u32 val = 0; + if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) val = atoi(ptr); + if (val < __afl_final_loc) { + + if (__afl_final_loc > FS_OPT_MAX_MAPSIZE) { + + if (!getenv("AFL_QUIET")) + fprintf(stderr, + "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u " + "to be able to run this instrumented program!\n", + __afl_final_loc); + + if (id_str) { + + send_forkserver_error(FS_ERROR_MAP_SIZE); + exit(-1); + + } + + } else { + + if (!getenv("AFL_QUIET")) + fprintf(stderr, + "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u " + "to be able to run this instrumented program!\n", + __afl_final_loc); + + } + + } + + } + + } + + /* If we're running under AFL, attach to the appropriate region, replacing the + 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")) + 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, " + "max_size_forkserver %u/0x%x\n", + id_str == 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); + + if (id_str) { + + if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial) { + + if (__afl_map_addr) + munmap((void *)__afl_map_addr, __afl_final_loc); + else + free(__afl_area_ptr); + __afl_area_ptr = __afl_area_initial; + + } + +#ifdef USEMMAP + const char * shm_file_path = id_str; + int shm_fd = -1; + unsigned char *shm_base = NULL; + + /* create the shared memory segment as if it was a file */ + shm_fd = shm_open(shm_file_path, O_RDWR, 0600); + if (shm_fd == -1) { + + fprintf(stderr, "shm_open() failed\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); + exit(1); + + } + + /* map the shared memory segment to the address space of the process */ + if (__afl_map_addr) { + + shm_base = + mmap((void *)__afl_map_addr, __afl_map_size, PROT_READ | PROT_WRITE, + MAP_FIXED_NOREPLACE | MAP_SHARED, shm_fd, 0); + + } else { + + shm_base = mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, + shm_fd, 0); + + } + + if (shm_base == MAP_FAILED) { + + close(shm_fd); + shm_fd = -1; + + fprintf(stderr, "mmap() failed\n"); + if (__afl_map_addr) + send_forkserver_error(FS_ERROR_MAP_ADDR); + else + send_forkserver_error(FS_ERROR_MMAP); + exit(2); + + } + + __afl_area_ptr = shm_base; +#else + u32 shm_id = atoi(id_str); + + __afl_area_ptr = shmat(shm_id, (void *)__afl_map_addr, 0); + +#endif + + /* Whooooops. */ + + if (__afl_area_ptr == (void *)-1) { + + if (__afl_map_addr) + send_forkserver_error(FS_ERROR_MAP_ADDR); + else + send_forkserver_error(FS_ERROR_SHMAT); + _exit(1); + + } + + /* Write something into the bitmap so that even with low AFL_INST_RATIO, + our parent doesn't give up on us. */ + + __afl_area_ptr[0] = 1; + + } else if ((!__afl_area_ptr || __afl_area_ptr == __afl_area_initial) && + + __afl_map_addr) { + + __afl_area_ptr = + mmap((void *)__afl_map_addr, __afl_map_size, PROT_READ | PROT_WRITE, + MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + if (__afl_area_ptr == MAP_FAILED) { + + fprintf(stderr, "can not acquire mmap for address %p\n", + (void *)__afl_map_addr); + exit(1); + + } + + } else if (_is_sancov && __afl_area_ptr != __afl_area_initial) { + + free(__afl_area_ptr); + __afl_area_ptr = NULL; + if (__afl_final_loc > MAP_INITIAL_SIZE) + __afl_area_ptr = malloc(__afl_final_loc); + if (!__afl_area_ptr) __afl_area_ptr = __afl_area_initial; + + } + + id_str = getenv(CMPLOG_SHM_ENV_VAR); + + if (getenv("AFL_DEBUG")) { + + fprintf(stderr, "DEBUG: cmplog id_str %s\n", + id_str == NULL ? "" : id_str); + + } + + if (id_str) { + +#ifdef USEMMAP + const char * shm_file_path = id_str; + int shm_fd = -1; + unsigned char *shm_base = NULL; + + /* create the shared memory segment as if it was a file */ + shm_fd = shm_open(shm_file_path, O_RDWR, 0600); + if (shm_fd == -1) { + + fprintf(stderr, "shm_open() failed\n"); + exit(1); + + } + + /* map the shared memory segment to the address space of the process */ + shm_base = mmap(0, sizeof(struct cmp_map), PROT_READ | PROT_WRITE, + MAP_SHARED, shm_fd, 0); + if (shm_base == MAP_FAILED) { + + close(shm_fd); + shm_fd = -1; + + fprintf(stderr, "mmap() failed\n"); + exit(2); + + } + + __afl_cmp_map = shm_base; +#else + u32 shm_id = atoi(id_str); + + __afl_cmp_map = shmat(shm_id, NULL, 0); +#endif + + if (__afl_cmp_map == (void *)-1) _exit(1); + + } + +} + +#ifdef __linux__ +static void __afl_start_snapshots(void) { + + static u8 tmp[4] = {0, 0, 0, 0}; + s32 child_pid; + u32 status = 0; + u32 already_read_first = 0; + u32 was_killed; + + u8 child_stopped = 0; + + void (*old_sigchld_handler)(int) = 0; // = signal(SIGCHLD, SIG_DFL); + + /* Phone home and tell the parent that we're OK. If parent isn't there, + assume we're not running in forkserver mode and just execute program. */ + + status |= (FS_OPT_ENABLED | FS_OPT_SNAPSHOT); + if (__afl_sharedmem_fuzzing != 0) status |= FS_OPT_SHDMEM_FUZZ; + if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) + status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); + if (__afl_dictionary_len && __afl_dictionary) status |= FS_OPT_AUTODICT; + memcpy(tmp, &status, 4); + + if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; + + if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) { + + if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); + + if (getenv("AFL_DEBUG")) { + + fprintf(stderr, "target forkserver recv: %08x\n", was_killed); + + } + + if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == + (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { + + __afl_map_shm_fuzz(); + + } + + if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) == + (FS_OPT_ENABLED | FS_OPT_AUTODICT)) { + + // great lets pass the dictionary through the forkserver FD + u32 len = __afl_dictionary_len, offset = 0; + s32 ret; + + if (write(FORKSRV_FD + 1, &len, 4) != 4) { + + write(2, "Error: could not send dictionary len\n", + strlen("Error: could not send dictionary len\n")); + _exit(1); + + } + + while (len != 0) { + + ret = write(FORKSRV_FD + 1, __afl_dictionary + offset, len); + + if (ret < 1) { + + write(2, "Error: could not send dictionary\n", + strlen("Error: could not send dictionary\n")); + _exit(1); + + } + + len -= ret; + offset += ret; + + } + + } else { + + // uh this forkserver does not understand extended option passing + // or does not want the dictionary + if (!__afl_fuzz_ptr) already_read_first = 1; + + } + + } + + while (1) { + + int status; + + if (already_read_first) { + + already_read_first = 0; + + } else { + + /* Wait for parent by reading from the pipe. Abort if read fails. */ + if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); + + } + + #ifdef _AFL_DOCUMENT_MUTATIONS + if (__afl_fuzz_ptr) { + + static uint32_t counter = 0; + char fn[32]; + sprintf(fn, "%09u:forkserver", counter); + s32 fd_doc = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd_doc >= 0) { + + if (write(fd_doc, __afl_fuzz_ptr, *__afl_fuzz_len) != *__afl_fuzz_len) { + + fprintf(stderr, "write of mutation file failed: %s\n", fn); + unlink(fn); + + } + + close(fd_doc); + + } + + counter++; + + } + + #endif + + /* If we stopped the child in persistent mode, but there was a race + condition and afl-fuzz already issued SIGKILL, write off the old + process. */ + + if (child_stopped && was_killed) { + + child_stopped = 0; + if (waitpid(child_pid, &status, 0) < 0) _exit(1); + + } + + if (!child_stopped) { + + /* Once woken up, create a clone of our process. */ + + child_pid = fork(); + if (child_pid < 0) _exit(1); + + /* In child process: close fds, resume execution. */ + + if (!child_pid) { + + //(void)nice(-20); // does not seem to improve + + signal(SIGCHLD, old_sigchld_handler); + + close(FORKSRV_FD); + close(FORKSRV_FD + 1); + + if (!afl_snapshot_take(AFL_SNAPSHOT_MMAP | AFL_SNAPSHOT_FDS | + AFL_SNAPSHOT_REGS | AFL_SNAPSHOT_EXIT)) { + + raise(SIGSTOP); + + } + + __afl_area_ptr[0] = 1; + memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T)); + + return; + + } + + } else { + + /* Special handling for persistent mode: if the child is alive but + currently stopped, simply restart it with SIGCONT. */ + + kill(child_pid, SIGCONT); + child_stopped = 0; + + } + + /* In parent process: write PID to pipe, then wait for child. */ + + if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) _exit(1); + + if (waitpid(child_pid, &status, WUNTRACED) < 0) _exit(1); + + /* In persistent mode, the child stops itself with SIGSTOP to indicate + a successful run. In this case, we want to wake it up without forking + again. */ + + if (WIFSTOPPED(status)) child_stopped = 1; + + /* Relay wait status to pipe, then loop back. */ + + if (write(FORKSRV_FD + 1, &status, 4) != 4) _exit(1); + + } + +} + +#endif + +/* Fork server logic. */ + +static void __afl_start_forkserver(void) { + +#ifdef __linux__ + if (/*!is_persistent &&*/ !__afl_cmp_map && !getenv("AFL_NO_SNAPSHOT") && + afl_snapshot_init() >= 0) { + + __afl_start_snapshots(); + return; + + } + +#endif + + u8 tmp[4] = {0, 0, 0, 0}; + s32 child_pid; + u32 status = 0; + u32 already_read_first = 0; + u32 was_killed; + + u8 child_stopped = 0; + + void (*old_sigchld_handler)(int) = 0; // = signal(SIGCHLD, SIG_DFL); + + if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) + status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); + if (__afl_dictionary_len && __afl_dictionary) status |= FS_OPT_AUTODICT; + if (__afl_sharedmem_fuzzing != 0) status |= FS_OPT_SHDMEM_FUZZ; + if (status) status |= (FS_OPT_ENABLED); + memcpy(tmp, &status, 4); + + /* Phone home and tell the parent that we're OK. If parent isn't there, + assume we're not running in forkserver mode and just execute program. */ + + if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; + + if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) { + + if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); + + if (getenv("AFL_DEBUG")) { + + fprintf(stderr, "target forkserver recv: %08x\n", was_killed); + + } + + if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == + (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { + + __afl_map_shm_fuzz(); + + } + + if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) == + (FS_OPT_ENABLED | FS_OPT_AUTODICT)) { + + // great lets pass the dictionary through the forkserver FD + u32 len = __afl_dictionary_len, offset = 0; + s32 ret; + + if (write(FORKSRV_FD + 1, &len, 4) != 4) { + + write(2, "Error: could not send dictionary len\n", + strlen("Error: could not send dictionary len\n")); + _exit(1); + + } + + while (len != 0) { + + ret = write(FORKSRV_FD + 1, __afl_dictionary + offset, len); + + if (ret < 1) { + + write(2, "Error: could not send dictionary\n", + strlen("Error: could not send dictionary\n")); + _exit(1); + + } + + len -= ret; + offset += ret; + + } + + } else { + + // uh this forkserver does not understand extended option passing + // or does not want the dictionary + if (!__afl_fuzz_ptr) already_read_first = 1; + + } + + } + + while (1) { + + int status; + + /* Wait for parent by reading from the pipe. Abort if read fails. */ + + if (already_read_first) { + + already_read_first = 0; + + } else { + + if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); + + } + +#ifdef _AFL_DOCUMENT_MUTATIONS + if (__afl_fuzz_ptr) { + + static uint32_t counter = 0; + char fn[32]; + sprintf(fn, "%09u:forkserver", counter); + s32 fd_doc = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd_doc >= 0) { + + if (write(fd_doc, __afl_fuzz_ptr, *__afl_fuzz_len) != *__afl_fuzz_len) { + + fprintf(stderr, "write of mutation file failed: %s\n", fn); + unlink(fn); + + } + + close(fd_doc); + + } + + counter++; + + } + +#endif + + /* If we stopped the child in persistent mode, but there was a race + condition and afl-fuzz already issued SIGKILL, write off the old + process. */ + + if (child_stopped && was_killed) { + + child_stopped = 0; + if (waitpid(child_pid, &status, 0) < 0) _exit(1); + + } + + if (!child_stopped) { + + /* Once woken up, create a clone of our process. */ + + child_pid = fork(); + if (child_pid < 0) _exit(1); + + /* In child process: close fds, resume execution. */ + + if (!child_pid) { + + //(void)nice(-20); + + signal(SIGCHLD, old_sigchld_handler); + + close(FORKSRV_FD); + close(FORKSRV_FD + 1); + return; + + } + + } else { + + /* Special handling for persistent mode: if the child is alive but + currently stopped, simply restart it with SIGCONT. */ + + kill(child_pid, SIGCONT); + child_stopped = 0; + + } + + /* In parent process: write PID to pipe, then wait for child. */ + + if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) _exit(1); + + if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0) + _exit(1); + + /* In persistent mode, the child stops itself with SIGSTOP to indicate + a successful run. In this case, we want to wake it up without forking + again. */ + + if (WIFSTOPPED(status)) child_stopped = 1; + + /* Relay wait status to pipe, then loop back. */ + + if (write(FORKSRV_FD + 1, &status, 4) != 4) _exit(1); + + } + +} + +/* A simplified persistent mode handler, used as explained in + * README.llvm.md. */ + +int __afl_persistent_loop(unsigned int max_cnt) { + + static u8 first_pass = 1; + static u32 cycle_cnt; + + if (first_pass) { + + /* Make sure that every iteration of __AFL_LOOP() starts with a clean slate. + On subsequent calls, the parent will take care of that, but on the first + iteration, it's our job to erase any trace of whatever happened + before the loop. */ + + if (is_persistent) { + + memset(__afl_area_ptr, 0, __afl_map_size); + __afl_area_ptr[0] = 1; + memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T)); + + } + + cycle_cnt = max_cnt; + first_pass = 0; + return 1; + + } + + if (is_persistent) { + + if (--cycle_cnt) { + + raise(SIGSTOP); + + __afl_area_ptr[0] = 1; + memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T)); + + return 1; + + } else { + + /* When exiting __AFL_LOOP(), make sure that the subsequent code that + follows the loop is not traced. We do that by pivoting back to the + dummy output region. */ + + __afl_area_ptr = __afl_area_initial; + + } + + } + + return 0; + +} + +/* This one can be called from user code when deferred forkserver mode + is enabled. */ + +void __afl_manual_init(void) { + + static u8 init_done; + + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) { + + init_done = 1; + is_persistent = 0; + __afl_sharedmem_fuzzing = 0; + if (__afl_area_ptr == NULL) __afl_area_ptr = __afl_area_initial; + + if (getenv("AFL_DEBUG")) + fprintf(stderr, + "DEBUG: disabled instrumentation because of " + "AFL_DISABLE_LLVM_INSTRUMENTATION\n"); + + } + + if (!init_done) { + + __afl_start_forkserver(); + init_done = 1; + + } + +} + +/* Initialization of the forkserver - latest possible */ + +__attribute__((constructor())) void __afl_auto_init(void) { + + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; + + if (getenv(DEFER_ENV_VAR)) return; + + __afl_manual_init(); + +} + +/* Initialization of the shmem - earliest possible because of LTO fixed mem. */ + +__attribute__((constructor(CTOR_PRIO))) void __afl_auto_early(void) { + + is_persistent = !!getenv(PERSIST_ENV_VAR); + + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; + + __afl_map_shm(); + +} + +/* preset __afl_area_ptr #2 */ + +__attribute__((constructor(1))) void __afl_auto_second(void) { + + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; + u8 *ptr; + + if (__afl_final_loc) { + + if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial) + free(__afl_area_ptr); + + if (__afl_map_addr) + ptr = (u8 *)mmap((void *)__afl_map_addr, __afl_final_loc, + PROT_READ | PROT_WRITE, + MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); + else + ptr = (u8 *)malloc(__afl_final_loc); + + if (ptr && (ssize_t)ptr != -1) __afl_area_ptr = ptr; + + } + +} + +/* 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; + + ptr = (u8 *)malloc(1024000); + + if (ptr && (ssize_t)ptr != -1) __afl_area_ptr = ptr; + +} + +/* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard. + It remains non-operational in the traditional, plugin-backed LLVM mode. + For more info about 'trace-pc-guard', see README.llvm.md. + + The first function (__sanitizer_cov_trace_pc_guard) is called back on every + edge (as opposed to every basic block). */ + +void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { + + // For stability analysis, if you want to know to which function unstable + // edge IDs belong - uncomment, recompile+install llvm_mode, recompile + // the target. libunwind and libbacktrace are better solutions. + // Set AFL_DEBUG_CHILD_OUTPUT=1 and run afl-fuzz with 2>file to capture + // the backtrace output + /* + uint32_t unstable[] = { ... unstable edge IDs }; + uint32_t idx; + char bt[1024]; + for (idx = 0; i < sizeof(unstable)/sizeof(uint32_t); i++) { + + if (unstable[idx] == __afl_area_ptr[*guard]) { + + int bt_size = backtrace(bt, 256); + if (bt_size > 0) { + + char **bt_syms = backtrace_symbols(bt, bt_size); + if (bt_syms) { + + fprintf(stderr, "DEBUG: edge=%u caller=%s\n", unstable[idx], + bt_syms[0]); + free(bt_syms); + + } + + } + + } + + } + + */ + +#if (LLVM_VERSION_MAJOR < 9) + + __afl_area_ptr[*guard]++; + +#else + + __afl_area_ptr[*guard] = + __afl_area_ptr[*guard] + 1 + (__afl_area_ptr[*guard] == 255 ? 1 : 0); + +#endif + +} + +/* Init callback. Populates instrumentation IDs. Note that we're using + ID of 0 as a special value to indicate non-instrumented bits. That may + still touch the bitmap, but in a fairly harmless way. */ + +void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { + + u32 inst_ratio = 100; + char *x; + + _is_sancov = 1; + + if (getenv("AFL_DEBUG")) { + + fprintf(stderr, "Running __sanitizer_cov_trace_pc_guard_init: %p-%p\n", + start, stop); + + } + + if (start == stop || *start) return; + + x = getenv("AFL_INST_RATIO"); + if (x) inst_ratio = (u32)atoi(x); + + if (!inst_ratio || inst_ratio > 100) { + + fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n"); + abort(); + + } + + /* 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). */ + + *(start++) = R(MAP_SIZE - 1) + 1; + + while (start < stop) { + + if (R(100) < inst_ratio) + *start = ++__afl_final_loc; + else + *start = 0; + + start++; + + } + +} + +///// CmpLog instrumentation + +void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { + + if (unlikely(!__afl_cmp_map)) return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + // if (!__afl_cmp_map->headers[k].cnt) + // __afl_cmp_map->headers[k].cnt = __afl_cmp_counter++; + + __afl_cmp_map->headers[k].shape = 0; + + hits &= CMP_MAP_H - 1; + __afl_cmp_map->log[k][hits].v0 = arg1; + __afl_cmp_map->log[k][hits].v1 = arg2; + +} + +void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { + + if (unlikely(!__afl_cmp_map)) return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + + __afl_cmp_map->headers[k].shape = 1; + + hits &= CMP_MAP_H - 1; + __afl_cmp_map->log[k][hits].v0 = arg1; + __afl_cmp_map->log[k][hits].v1 = arg2; + +} + +void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { + + if (unlikely(!__afl_cmp_map)) return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + + __afl_cmp_map->headers[k].shape = 3; + + hits &= CMP_MAP_H - 1; + __afl_cmp_map->log[k][hits].v0 = arg1; + __afl_cmp_map->log[k][hits].v1 = arg2; + +} + +void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) { + + if (unlikely(!__afl_cmp_map)) return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + + __afl_cmp_map->headers[k].shape = 7; + + hits &= CMP_MAP_H - 1; + __afl_cmp_map->log[k][hits].v0 = arg1; + __afl_cmp_map->log[k][hits].v1 = arg2; + +} + +#if defined(__APPLE__) + #pragma weak __sanitizer_cov_trace_const_cmp1 = __cmplog_ins_hook1 + #pragma weak __sanitizer_cov_trace_const_cmp2 = __cmplog_ins_hook2 + #pragma weak __sanitizer_cov_trace_const_cmp4 = __cmplog_ins_hook4 + #pragma weak __sanitizer_cov_trace_const_cmp8 = __cmplog_ins_hook8 + + #pragma weak __sanitizer_cov_trace_cmp1 = __cmplog_ins_hook1 + #pragma weak __sanitizer_cov_trace_cmp2 = __cmplog_ins_hook2 + #pragma weak __sanitizer_cov_trace_cmp4 = __cmplog_ins_hook4 + #pragma weak __sanitizer_cov_trace_cmp8 = __cmplog_ins_hook8 +#else +void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) + __attribute__((alias("__cmplog_ins_hook1"))); +void __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2) + __attribute__((alias("__cmplog_ins_hook2"))); +void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2) + __attribute__((alias("__cmplog_ins_hook4"))); +void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2) + __attribute__((alias("__cmplog_ins_hook8"))); + +void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) + __attribute__((alias("__cmplog_ins_hook1"))); +void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) + __attribute__((alias("__cmplog_ins_hook2"))); +void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) + __attribute__((alias("__cmplog_ins_hook4"))); +void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) + __attribute__((alias("__cmplog_ins_hook8"))); +#endif /* defined(__APPLE__) */ + +void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { + + if (unlikely(!__afl_cmp_map)) return; + + for (uint64_t i = 0; i < cases[0]; i++) { + + uintptr_t k = (uintptr_t)__builtin_return_address(0) + i; + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + + __afl_cmp_map->headers[k].shape = 7; + + hits &= CMP_MAP_H - 1; + __afl_cmp_map->log[k][hits].v0 = val; + __afl_cmp_map->log[k][hits].v1 = cases[i + 2]; + + } + +} + +// 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) { + + char *p = ptr; + char *page = (char *)((uintptr_t)p & ~(sysconf(_SC_PAGE_SIZE) - 1)); + + int r = msync(page, (p - page) + len, MS_ASYNC); + if (r < 0) return errno != ENOMEM; + return 1; + +} + +void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { + + if (unlikely(!__afl_cmp_map)) return; + + if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_RTN; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + + __afl_cmp_map->headers[k].shape = 31; + + hits &= CMP_MAP_RTN_H - 1; + __builtin_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0, + ptr1, 32); + __builtin_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1, + ptr2, 32); + +} + diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc new file mode 100644 index 00000000..c5614aca --- /dev/null +++ b/instrumentation/afl-gcc-pass.so.cc @@ -0,0 +1,601 @@ +// +// There are some TODOs in this file: +// - fix instrumentation via external call +// - fix inline instrumentation +// - implement instrument list feature +// - dont instrument blocks that are uninteresting +// - implement neverZero +// + +/* + american fuzzy lop++ - GCC instrumentation pass + --------------------------------------------- + + Written by Austin Seipp with bits from + Emese Revfy + + Fixed by Heiko Eißfeldt 2019-2020 for AFL++ + + GCC integration design is based on the LLVM design, which comes + from Laszlo Szekeres. Some of the boilerplate code below for + afl_pass to adapt to different GCC versions was taken from Emese + Revfy's Size Overflow plugin for GCC, licensed under the GPLv2/v3. + + (NOTE: this plugin code is under GPLv3, in order to comply with the + GCC runtime library exception, which states that you may distribute + "Target Code" from the compiler under a license of your choice, as + long as the "Compilation Process" is "Eligible", and contains no + GPL-incompatible software in GCC "during the process of + transforming high level code to target code". In this case, the + plugin will be used to generate "Target Code" during the + "Compilation Process", and thus it must be GPLv3 to be "eligible".) + + Copyright (C) 2015 Austin Seipp + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + */ + +#define BUILD_INLINE_INST + +#include "../include/config.h" +#include "../include/debug.h" + +/* clear helper macros AFL types pull in, which intervene with gcc-plugin + * headers from GCC-8 */ +#ifdef likely + #undef likely +#endif +#ifdef unlikely + #undef unlikely +#endif + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* -------------------------------------------------------------------------- */ +/* -- AFL instrumentation pass ---------------------------------------------- */ + +static int be_quiet = 0; +static unsigned int inst_ratio = 100; +static bool inst_ext = true; +static std::list myInstrumentList; + +static unsigned int ext_call_instrument(function *fun) { + + /* Instrument all the things! */ + basic_block bb; + unsigned finst_blocks = 0; + unsigned fcnt_blocks = 0; + + tree fntype = build_function_type_list(void_type_node, /* return */ + uint32_type_node, /* args */ + NULL_TREE); /* done */ + tree fndecl = build_fn_decl("__afl_trace", fntype); + TREE_STATIC(fndecl) = 1; /* Defined elsewhere */ + TREE_PUBLIC(fndecl) = 1; /* Public */ + DECL_EXTERNAL(fndecl) = 1; /* External linkage */ + DECL_ARTIFICIAL(fndecl) = 1; /* Injected by compiler */ + + FOR_EACH_BB_FN(bb, fun) { + + gimple_seq fcall; + gimple_seq seq = NULL; + gimple_stmt_iterator bentry; + ++fcnt_blocks; + + // only instrument if this basic block is the destination of a previous + // basic block that has multiple successors + // this gets rid of ~5-10% of instrumentations that are unnecessary + // result: a little more speed and less map pollution + + int more_than_one = -1; + edge ep; + edge_iterator eip; + + FOR_EACH_EDGE(ep, eip, bb->preds) { + + int count = 0; + if (more_than_one == -1) more_than_one = 0; + + basic_block Pred = ep->src; + edge es; + edge_iterator eis; + FOR_EACH_EDGE(es, eis, Pred->succs) { + + basic_block Succ = es->dest; + if (Succ != NULL) count++; + + } + + if (count > 1) more_than_one = 1; + + } + + if (more_than_one != 1) continue; + + /* Bail on this block if we trip the specified ratio */ + if (R(100) >= inst_ratio) continue; + + /* Make up cur_loc */ + unsigned int rand_loc = R(MAP_SIZE); + tree cur_loc = build_int_cst(uint32_type_node, rand_loc); + + /* Update bitmap via external call */ + /* to quote: + * /+ Trace a basic block with some ID +/ + * void __afl_trace(u32 x); + */ + + fcall = gimple_build_call( + fndecl, 1, + cur_loc); /* generate the function _call_ to above built reference, with + *1* parameter -> the random const for the location */ + gimple_seq_add_stmt(&seq, fcall); /* and insert into a sequence */ + + /* Done - grab the entry to the block and insert sequence */ + bentry = gsi_after_labels(bb); + gsi_insert_seq_before(&bentry, seq, GSI_SAME_STMT); + + ++finst_blocks; + + } + + /* Say something nice. */ + if (!be_quiet) { + + if (!finst_blocks) + WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST), + function_name(fun)); + else if (finst_blocks < fcnt_blocks) + OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST), + finst_blocks, fcnt_blocks, function_name(fun)); + else + OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST), finst_blocks, + function_name(fun)); + + } + + return 0; + +} + +static unsigned int inline_instrument(function *fun) { + + /* Instrument all the things! */ + basic_block bb; + unsigned finst_blocks = 0; + unsigned fcnt_blocks = 0; + tree one = build_int_cst(unsigned_char_type_node, 1); + // tree zero = build_int_cst(unsigned_char_type_node, 0); + + /* Set up global type declarations */ + tree map_type = build_pointer_type(unsigned_char_type_node); + tree map_ptr_g = + build_decl(UNKNOWN_LOCATION, VAR_DECL, + get_identifier_with_length("__afl_area_ptr", 14), map_type); + TREE_USED(map_ptr_g) = 1; + TREE_STATIC(map_ptr_g) = 1; /* Defined elsewhere */ + DECL_EXTERNAL(map_ptr_g) = 1; /* External linkage */ + DECL_PRESERVE_P(map_ptr_g) = 1; + DECL_ARTIFICIAL(map_ptr_g) = 1; /* Injected by compiler */ + rest_of_decl_compilation(map_ptr_g, 1, 0); + + tree prev_loc_g = build_decl(UNKNOWN_LOCATION, VAR_DECL, + get_identifier_with_length("__afl_prev_loc", 14), + uint32_type_node); + TREE_USED(prev_loc_g) = 1; + TREE_STATIC(prev_loc_g) = 1; /* Defined elsewhere */ + DECL_EXTERNAL(prev_loc_g) = 1; /* External linkage */ + DECL_PRESERVE_P(prev_loc_g) = 1; + DECL_ARTIFICIAL(prev_loc_g) = 1; /* Injected by compiler */ + set_decl_tls_model(prev_loc_g, TLS_MODEL_REAL); /* TLS attribute */ + rest_of_decl_compilation(prev_loc_g, 1, 0); + + FOR_EACH_BB_FN(bb, fun) { + + gimple_seq seq = NULL; + gimple_stmt_iterator bentry; + ++fcnt_blocks; + + // only instrument if this basic block is the destination of a previous + // basic block that has multiple successors + // this gets rid of ~5-10% of instrumentations that are unnecessary + // result: a little more speed and less map pollution + + int more_than_one = -1; + edge ep; + edge_iterator eip; + FOR_EACH_EDGE(ep, eip, bb->preds) { + + int count = 0; + if (more_than_one == -1) more_than_one = 0; + + basic_block Pred = ep->src; + edge es; + edge_iterator eis; + FOR_EACH_EDGE(es, eis, Pred->succs) { + + basic_block Succ = es->dest; + if (Succ != NULL) count++; + + } + + if (count > 1) more_than_one = 1; + + } + + if (more_than_one != 1) continue; + + /* Bail on this block if we trip the specified ratio */ + if (R(100) >= inst_ratio) continue; + + /* Make up cur_loc */ + + unsigned int rand_loc = R(MAP_SIZE); + tree cur_loc = build_int_cst(uint32_type_node, rand_loc); + + /* Load prev_loc, xor with cur_loc */ + // gimple_assign + tree prev_loc = create_tmp_var_raw(uint32_type_node, "prev_loc"); + gassign *g = gimple_build_assign(prev_loc, VAR_DECL, prev_loc_g); + gimple_seq_add_stmt(&seq, g); // load prev_loc + update_stmt(g); + + // gimple_assign + tree area_off = create_tmp_var_raw(uint32_type_node, "area_off"); + g = gimple_build_assign(area_off, BIT_XOR_EXPR, prev_loc, cur_loc); + gimple_seq_add_stmt(&seq, g); // area_off = prev_loc ^ cur_loc + update_stmt(g); + + /* Update bitmap */ + + // gimple_assign + tree map_ptr = create_tmp_var(map_type, "map_ptr"); + tree map_ptr2 = create_tmp_var(map_type, "map_ptr2"); + + g = gimple_build_assign(map_ptr, map_ptr_g); + gimple_seq_add_stmt(&seq, g); // map_ptr = __afl_area_ptr + update_stmt(g); + +#if 1 + #if 0 + tree addr = build2(ADDR_EXPR, map_type, map_ptr, area_off); + g = gimple_build_assign(map_ptr2, MODIFY_EXPR, addr); + gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off + update_stmt(g); + #else + g = gimple_build_assign(map_ptr2, PLUS_EXPR, map_ptr, area_off); + gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off + update_stmt(g); + #endif + + // gimple_assign + tree tmp1 = create_tmp_var_raw(unsigned_char_type_node, "tmp1"); + g = gimple_build_assign(tmp1, MEM_REF, map_ptr2); + gimple_seq_add_stmt(&seq, g); // tmp1 = *map_ptr2 + update_stmt(g); +#else + tree atIndex = build2(PLUS_EXPR, uint32_type_node, map_ptr, area_off); + tree array_address = build1(ADDR_EXPR, map_type, atIndex); + tree array_access = build1(INDIRECT_REF, map_type, array_address); + tree tmp1 = create_tmp_var(unsigned_char_type_node, "tmp1"); + g = gimple_build_assign(tmp1, array_access); + gimple_seq_add_stmt(&seq, g); // tmp1 = *(map_ptr + area_off) + update_stmt(g); +#endif + // gimple_assign + tree tmp2 = create_tmp_var_raw(unsigned_char_type_node, "tmp2"); + g = gimple_build_assign(tmp2, PLUS_EXPR, tmp1, one); + gimple_seq_add_stmt(&seq, g); // tmp2 = tmp1 + 1 + update_stmt(g); + + // TODO: neverZero: here we have to check if tmp3 == 0 + // and add 1 if so + + // gimple_assign + // tree map_ptr3 = create_tmp_var_raw(map_type, "map_ptr3"); + g = gimple_build_assign(map_ptr2, INDIRECT_REF, tmp2); + gimple_seq_add_stmt(&seq, g); // *map_ptr2 = tmp2 + update_stmt(g); + + /* Set prev_loc to cur_loc >> 1 */ + + // gimple_assign + tree shifted_loc = build_int_cst(TREE_TYPE(prev_loc_g), rand_loc >> 1); + tree prev_loc2 = create_tmp_var_raw(uint32_type_node, "prev_loc2"); + g = gimple_build_assign(prev_loc2, shifted_loc); + gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1 + update_stmt(g); + g = gimple_build_assign(prev_loc_g, prev_loc2); + gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1 + update_stmt(g); + + /* Done - grab the entry to the block and insert sequence */ + + bentry = gsi_after_labels(bb); + gsi_insert_seq_before(&bentry, seq, GSI_NEW_STMT); + + ++finst_blocks; + + } + + /* Say something nice. */ + if (!be_quiet) { + + if (!finst_blocks) + WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST), + function_name(fun)); + else if (finst_blocks < fcnt_blocks) + OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST), + finst_blocks, fcnt_blocks, function_name(fun)); + else + OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST), finst_blocks, + function_name(fun)); + + } + + return 0; + +} + +/* -------------------------------------------------------------------------- */ +/* -- Boilerplate and initialization ---------------------------------------- */ + +static const struct pass_data afl_pass_data = { + + .type = GIMPLE_PASS, + .name = "afl-inst", + .optinfo_flags = OPTGROUP_NONE, + + .tv_id = TV_NONE, + .properties_required = 0, + .properties_provided = 0, + .properties_destroyed = 0, + .todo_flags_start = 0, + // NOTE(aseipp): it's very, very important to include + // at least 'TODO_update_ssa' here so that GCC will + // properly update the resulting SSA form, e.g., to + // include new PHI nodes for newly added symbols or + // names. Do not remove this. Do not taunt Happy Fun + // Ball. + .todo_flags_finish = TODO_update_ssa | TODO_verify_il | TODO_cleanup_cfg, + +}; + +namespace { + +class afl_pass : public gimple_opt_pass { + + private: + bool do_ext_call; + + public: + afl_pass(bool ext_call, gcc::context *g) + : gimple_opt_pass(afl_pass_data, g), do_ext_call(ext_call) { + + } + + unsigned int execute(function *fun) override { + + if (!myInstrumentList.empty()) { + + bool instrumentBlock = false; + std::string instFilename; + unsigned int instLine = 0; + + /* EXPR_FILENAME + This macro returns the name of the file in which the entity was declared, + as a char*. For an entity declared implicitly by the compiler (like + __builtin_ memcpy), this will be the string "". + */ + const char *fname = DECL_SOURCE_FILE(fun->decl); + + if (0 != strncmp("", fname, 10) && + 0 != strncmp("", fname, 10)) { + + instFilename = fname; + instLine = DECL_SOURCE_LINE(fun->decl); + + /* Continue only if we know where we actually are */ + if (!instFilename.empty()) { + + for (std::list::iterator it = myInstrumentList.begin(); + it != myInstrumentList.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. */ + if (instFilename.length() >= it->length()) { + + if (instFilename.compare(instFilename.length() - it->length(), + it->length(), *it) == 0) { + + instrumentBlock = true; + break; + + } + + } + + } + + } + + } + + /* Either we couldn't figure out our location or the location is + * not in the instrument list, so we skip instrumentation. */ + if (!instrumentBlock) { + + if (!be_quiet) { + + if (!instFilename.empty()) + SAYF(cYEL "[!] " cBRI + "Not in instrument list, skipping %s line %u...\n", + instFilename.c_str(), instLine); + else + SAYF(cYEL "[!] " cBRI "No filename information found, skipping it"); + + } + + return 0; + + } + + } + + return do_ext_call ? ext_call_instrument(fun) : inline_instrument(fun); + + } + +}; /* class afl_pass */ + +} // namespace + +static struct opt_pass *make_afl_pass(bool ext_call, gcc::context *ctxt) { + + return new afl_pass(ext_call, ctxt); + +} + +/* -------------------------------------------------------------------------- */ +/* -- Initialization -------------------------------------------------------- */ + +int plugin_is_GPL_compatible = 1; + +static struct plugin_info afl_plugin_info = { + + .version = "20200519", + .help = "AFL++ gcc plugin\n", + +}; + +int plugin_init(struct plugin_name_args * plugin_info, + struct plugin_gcc_version *version) { + + struct register_pass_info afl_pass_info; + struct timeval tv; + struct timezone tz; + u32 rand_seed; + + /* Setup random() so we get Actually Random(TM) outputs from R() */ + gettimeofday(&tv, &tz); + rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); + SR(rand_seed); + + /* Pass information */ + afl_pass_info.pass = make_afl_pass(inst_ext, g); + afl_pass_info.reference_pass_name = "ssa"; + afl_pass_info.ref_pass_instance_number = 1; + afl_pass_info.pos_op = PASS_POS_INSERT_AFTER; + + if (!plugin_default_version_check(version, &gcc_version)) { + + FATAL(G_("Incompatible gcc/plugin versions! Expected GCC %d.%d"), + GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR); + + } + + /* Show a banner */ + if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { + + SAYF(G_(cCYA "afl-gcc-pass" VERSION cRST + " initially by , maintainer: hexcoder-\n")); + + } else + + be_quiet = 1; + + /* Decide instrumentation ratio */ + char *inst_ratio_str = getenv("AFL_INST_RATIO"); + + if (inst_ratio_str) { + + if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || + inst_ratio > 100) + FATAL(G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)")); + else { + + if (!be_quiet) + ACTF(G_("%s instrumentation at ratio of %u%% in %s mode."), + inst_ext ? G_("Call-based") : G_("Inline"), inst_ratio, + getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened")); + + } + + } + + char *instInstrumentListFilename = getenv("AFL_GCC_INSTRUMENT_FILE"); + if (!instInstrumentListFilename) + instInstrumentListFilename = getenv("AFL_GCC_WHITELIST"); + if (instInstrumentListFilename) { + + std::string line; + std::ifstream fileStream; + fileStream.open(instInstrumentListFilename); + if (!fileStream) PFATAL("Unable to open AFL_GCC_INSTRUMENT_FILE"); + getline(fileStream, line); + while (fileStream) { + + myInstrumentList.push_back(line); + getline(fileStream, line); + + } + + } else if (!be_quiet && (getenv("AFL_LLVM_WHITELIST") || + + getenv("AFL_LLVM_INSTRUMENT_FILE"))) { + + SAYF(cYEL "[-] " cRST + "AFL_LLVM_INSTRUMENT_FILE environment variable detected - did " + "you mean AFL_GCC_INSTRUMENT_FILE?\n"); + + } + + /* Go go gadget */ + register_callback(plugin_info->base_name, PLUGIN_INFO, NULL, + &afl_plugin_info); + register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, NULL, + &afl_pass_info); + return 0; + +} + diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc new file mode 100644 index 00000000..189b4ec6 --- /dev/null +++ b/instrumentation/afl-llvm-common.cc @@ -0,0 +1,575 @@ +#define AFL_LLVM_PASS + +#include "config.h" +#include "debug.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define IS_EXTERN extern +#include "afl-llvm-common.h" + +using namespace llvm; + +static std::list allowListFiles; +static std::list allowListFunctions; +static std::list denyListFiles; +static std::list denyListFunctions; + +char *getBBName(const llvm::BasicBlock *BB) { + + static char *name; + + if (!BB->getName().empty()) { + + name = strdup(BB->getName().str().c_str()); + return name; + + } + + std::string Str; + raw_string_ostream OS(Str); + +#if LLVM_VERSION_MAJOR >= 4 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) + BB->printAsOperand(OS, false); +#endif + name = strdup(OS.str().c_str()); + return name; + +} + +/* Function that we never instrument or analyze */ +/* Note: this ignore check is also called in isInInstrumentList() */ +bool isIgnoreFunction(const llvm::Function *F) { + + // Starting from "LLVMFuzzer" these are functions used in libfuzzer based + // fuzzing campaign installations, e.g. oss-fuzz + + static const char *ignoreList[] = { + + "asan.", + "llvm.", + "sancov.", + "__ubsan_", + "ign.", + "__afl_", + "_fini", + "__libc_csu", + "__asan", + "__msan", + "__cmplog", + "__sancov", + "msan.", + "LLVMFuzzer", + "__decide_deferred", + "maybe_duplicate_stderr", + "discard_output", + "close_stdout", + "dup_and_close_stderr", + "maybe_close_fd_mask", + "ExecuteFilesOnyByOne" + + }; + + for (auto const &ignoreListFunc : ignoreList) { + + if (F->getName().startswith(ignoreListFunc)) { return true; } + + } + + return false; + +} + +void initInstrumentList() { + + char *allowlist = getenv("AFL_LLVM_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); + char *denylist = getenv("AFL_LLVM_DENYLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); + + if (allowlist && denylist) + FATAL( + "You can only specify either AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST " + "but not both!"); + + if (allowlist) { + + std::string line; + std::ifstream fileStream; + fileStream.open(allowlist); + if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_ALLOWLIST"); + getline(fileStream, line); + + while (fileStream) { + + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_LLVM_ALLOWLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + allowListFiles.push_back(line); + else + allowListFunctions.push_back(line); + getline(fileStream, line); + + } + + } + + if (debug) + SAYF(cMGN "[D] " cRST + "loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); + + } + + if (denylist) { + + std::string line; + std::ifstream fileStream; + fileStream.open(denylist); + if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_DENYLIST"); + getline(fileStream, line); + + while (fileStream) { + + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_LLVM_DENYLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + denyListFiles.push_back(line); + else + denyListFunctions.push_back(line); + getline(fileStream, line); + + } + + } + + if (debug) + SAYF(cMGN "[D] " cRST + "loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); + + } + +} + +void scanForDangerousFunctions(llvm::Module *M) { + + if (!M) return; + +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 9) + + for (GlobalIFunc &IF : M->ifuncs()) { + + StringRef ifunc_name = IF.getName(); + Constant *r = IF.getResolver(); + StringRef r_name = cast(r->getOperand(0))->getName(); + if (!be_quiet) + fprintf(stderr, + "Info: Found an ifunc with name %s that points to resolver " + "function %s, we will not instrument this, putting it into the " + "block list.\n", + ifunc_name.str().c_str(), r_name.str().c_str()); + denyListFunctions.push_back(r_name.str()); + + } + + GlobalVariable *GV = M->getNamedGlobal("llvm.global_ctors"); + if (GV && !GV->isDeclaration() && !GV->hasLocalLinkage()) { + + ConstantArray *InitList = dyn_cast(GV->getInitializer()); + + if (InitList) { + + for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) { + + if (ConstantStruct *CS = + dyn_cast(InitList->getOperand(i))) { + + if (CS->getNumOperands() >= 2) { + + if (CS->getOperand(1)->isNullValue()) + break; // Found a null terminator, stop here. + + ConstantInt *CI = dyn_cast(CS->getOperand(0)); + int Priority = CI ? CI->getSExtValue() : 0; + + Constant *FP = CS->getOperand(1); + if (ConstantExpr *CE = dyn_cast(FP)) + if (CE->isCast()) FP = CE->getOperand(0); + if (Function *F = dyn_cast(FP)) { + + if (!F->isDeclaration() && + strncmp(F->getName().str().c_str(), "__afl", 5) != 0) { + + if (!be_quiet) + fprintf(stderr, + "Info: Found constructor function %s with prio " + "%u, we will not instrument this, putting it into a " + "block list.\n", + F->getName().str().c_str(), Priority); + denyListFunctions.push_back(F->getName().str()); + + } + + } + + } + + } + + } + + } + + } + +#endif + +} + +static std::string getSourceName(llvm::Function *F) { + + // let's try to get the filename for the function + auto bb = &F->getEntryBlock(); + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + DebugLoc Loc = IP->getDebugLoc(); + +#if LLVM_VERSION_MAJOR >= 4 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) + if (Loc) { + + StringRef instFilename; + DILocation *cDILoc = dyn_cast(Loc.getAsMDNode()); + + if (cDILoc) { instFilename = cDILoc->getFilename(); } + + if (instFilename.str().empty()) { + + /* If the original location is empty, try using the inlined location + */ + DILocation *oDILoc = cDILoc->getInlinedAt(); + if (oDILoc) { instFilename = oDILoc->getFilename(); } + + } + + return instFilename.str(); + + } + +#else + if (!Loc.isUnknown()) { + + DILocation cDILoc(Loc.getAsMDNode(F->getContext())); + + StringRef instFilename = cDILoc.getFilename(); + + /* Continue only if we know where we actually are */ + return instFilename.str(); + + } + +#endif + + return std::string(""); + +} + +bool isInInstrumentList(llvm::Function *F) { + + bool return_default = true; + + // is this a function with code? If it is external we don't instrument it + // anyway and it can't be in the instrument file list. Or if it is it is + // ignored. + if (!F->size() || isIgnoreFunction(F)) return false; + + if (!denyListFiles.empty() || !denyListFunctions.empty()) { + + if (!denyListFunctions.empty()) { + + std::string instFunction = F->getName().str(); + + for (std::list::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFunction.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the deny function list, " + "not instrumenting ... \n", + instFunction.c_str()); + return false; + + } + + } + + } + + } + + if (!denyListFiles.empty()) { + + std::string source_file = getSourceName(F); + + if (!source_file.empty()) { + + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (source_file.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { + + return false; + + } + + } + + } + + } else { + + // we could not find out the location. in this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); + + } + + } + + } + + // if we do not have a instrument file list return true + if (!allowListFiles.empty() || !allowListFunctions.empty()) { + + return_default = false; + + if (!allowListFunctions.empty()) { + + std::string instFunction = F->getName().str(); + + for (std::list::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFunction.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allow function list, " + "instrumenting ... \n", + instFunction.c_str()); + return true; + + } + + } + + } + + } + + if (!allowListFiles.empty()) { + + std::string source_file = getSourceName(F); + + if (!source_file.empty()) { + + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (source_file.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allowlist (%s), " + "instrumenting ... \n", + F->getName().str().c_str(), source_file.c_str()); + return true; + + } + + } + + } + + } else { + + // we could not find out the location. In this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); + return false; + + } + + } + + } + + return return_default; + +} + +// Calculate the number of average collisions that would occur if all +// location IDs would be assigned randomly (like normal afl/afl++). +// This uses the "balls in bins" algorithm. +unsigned long long int calculateCollisions(uint32_t edges) { + + double bins = MAP_SIZE; + double balls = edges; + double step1 = 1 - (1 / bins); + double step2 = pow(step1, balls); + double step3 = bins * step2; + double step4 = round(step3); + unsigned long long int empty = step4; + unsigned long long int collisions = edges - (MAP_SIZE - empty); + return collisions; + +} + diff --git a/instrumentation/afl-llvm-common.h b/instrumentation/afl-llvm-common.h new file mode 100644 index 00000000..a1561d9c --- /dev/null +++ b/instrumentation/afl-llvm-common.h @@ -0,0 +1,52 @@ +#ifndef __AFLLLVMCOMMON_H +#define __AFLLLVMCOMMON_H + +#include +#include +#include + +#include +#include +#include +#include + +#include "llvm/Config/llvm-config.h" +#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5 +typedef long double max_align_t; +#endif + +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" + +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) + #include "llvm/IR/DebugInfo.h" + #include "llvm/IR/CFG.h" +#else + #include "llvm/DebugInfo.h" + #include "llvm/Support/CFG.h" +#endif + +char * getBBName(const llvm::BasicBlock *BB); +bool isIgnoreFunction(const llvm::Function *F); +void initInstrumentList(); +bool isInInstrumentList(llvm::Function *F); +unsigned long long int calculateCollisions(uint32_t edges); +void scanForDangerousFunctions(llvm::Module *M); + +#ifndef IS_EXTERN + #define IS_EXTERN +#endif + +IS_EXTERN int debug; +IS_EXTERN int be_quiet; + +#undef IS_EXTERN + +#endif + diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc new file mode 100644 index 00000000..e87ecce8 --- /dev/null +++ b/instrumentation/afl-llvm-dict2file.so.cc @@ -0,0 +1,599 @@ +/* + american fuzzy lop++ - LLVM LTO instrumentation pass + ---------------------------------------------------- + + Written by Marc Heuse + + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This library is plugged into LLVM when invoking clang through afl-clang-lto. + + */ + +#define AFL_LLVM_PASS + +#include "config.h" +#include "debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "llvm/Config/llvm-config.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/MemorySSAUpdater.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/Pass.h" +#include "llvm/IR/Constants.h" + +#include "afl-llvm-common.h" + +#ifndef O_DSYNC + #define O_DSYNC O_SYNC +#endif + +using namespace llvm; + +namespace { + +class AFLdict2filePass : public ModulePass { + + public: + static char ID; + + AFLdict2filePass() : ModulePass(ID) { + + if (getenv("AFL_DEBUG")) debug = 1; + + } + + bool runOnModule(Module &M) override; + +}; + +} // namespace + +void dict2file(int fd, u8 *mem, u32 len) { + + int i, j, binary = 0; + char line[MAX_AUTO_EXTRA * 8], tmp[8]; + + strcpy(line, "\""); + j = 1; + for (i = 0; i < len; i++) { + + if (isprint(mem[i])) { + + line[j++] = mem[i]; + + } else { + + if (i + 1 != len || mem[i] != 0 || binary || len == 4 || len == 8) { + + line[j] = 0; + sprintf(tmp, "\\x%02x", (u8)mem[i]); + strcat(line, tmp); + j = strlen(line); + + } + + binary = 1; + + } + + } + + line[j] = 0; + strcat(line, "\"\n"); + if (write(fd, line, strlen(line)) <= 0) + PFATAL("Could not write to dictionary file"); + fsync(fd); + + if (!be_quiet) fprintf(stderr, "Found dictionary token: %s", line); + +} + +bool AFLdict2filePass::runOnModule(Module &M) { + + DenseMap valueMap; + char * ptr; + int fd, found = 0; + + /* Show a banner */ + setvbuf(stdout, NULL, _IONBF, 0); + + if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { + + SAYF(cCYA "afl-llvm-dict2file" VERSION cRST + " by Marc \"vanHauser\" Heuse \n"); + + } else + + be_quiet = 1; + + scanForDangerousFunctions(&M); + + ptr = getenv("AFL_LLVM_DICT2FILE"); + + if (!ptr || *ptr != '/') + FATAL("AFL_LLVM_DICT2FILE is not set to an absolute path: %s", ptr); + + if ((fd = open(ptr, O_WRONLY | O_APPEND | O_CREAT | O_DSYNC, 0644)) < 0) + PFATAL("Could not open/create %s.", ptr); + + /* Instrument all the things! */ + + for (auto &F : M) { + + if (isIgnoreFunction(&F)) continue; + + /* Some implementation notes. + * + * We try to handle 3 cases: + * - memcmp("foo", arg, 3) <- literal string + * - static char globalvar[] = "foo"; + * memcmp(globalvar, arg, 3) <- global variable + * - char localvar[] = "foo"; + * memcmp(locallvar, arg, 3) <- local variable + * + * The local variable case is the hardest. We can only detect that + * case if there is no reassignment or change in the variable. + * And it might not work across llvm version. + * What we do is hooking the initializer function for local variables + * (llvm.memcpy.p0i8.p0i8.i64) and note the string and the assigned + * variable. And if that variable is then used in a compare function + * we use that noted string. + * This seems not to work for tokens that have a size <= 4 :-( + * + * - if the compared length is smaller than the string length we + * save the full string. This is likely better for fuzzing but + * might be wrong in a few cases depending on optimizers + * + * - not using StringRef because there is a bug in the llvm 11 + * checkout I am using which sometimes points to wrong strings + * + * Over and out. Took me a full day. damn. mh/vh + */ + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + CmpInst * cmpInst = nullptr; + + if ((cmpInst = dyn_cast(&IN))) { + + Value * op = cmpInst->getOperand(1); + ConstantInt *ilen = dyn_cast(op); + + if (ilen) { + + u64 val2 = 0, val = ilen->getZExtValue(); + u32 len = 0; + if (val > 0x10000 && val < 0xffffffff) len = 4; + if (val > 0x100000001 && val < 0xffffffffffffffff) len = 8; + + if (len) { + + auto c = cmpInst->getPredicate(); + + switch (c) { + + case CmpInst::FCMP_OGT: // fall through + case CmpInst::FCMP_OLE: // fall through + case CmpInst::ICMP_SLE: // fall through + case CmpInst::ICMP_SGT: + + // signed comparison and it is a negative constant + if ((len == 4 && (val & 80000000)) || + (len == 8 && (val & 8000000000000000))) { + + if ((val & 0xffff) != 1) val2 = val - 1; + break; + + } + + // fall through + + case CmpInst::FCMP_UGT: // fall through + case CmpInst::FCMP_ULE: // fall through + case CmpInst::ICMP_UGT: // fall through + case CmpInst::ICMP_ULE: + if ((val & 0xffff) != 0xfffe) val2 = val + 1; + break; + + case CmpInst::FCMP_OLT: // fall through + case CmpInst::FCMP_OGE: // fall through + case CmpInst::ICMP_SLT: // fall through + case CmpInst::ICMP_SGE: + + // signed comparison and it is a negative constant + if ((len == 4 && (val & 80000000)) || + (len == 8 && (val & 8000000000000000))) { + + if ((val & 0xffff) != 1) val2 = val - 1; + break; + + } + + // fall through + + case CmpInst::FCMP_ULT: // fall through + case CmpInst::FCMP_UGE: // fall through + case CmpInst::ICMP_ULT: // fall through + case CmpInst::ICMP_UGE: + if ((val & 0xffff) != 1) val2 = val - 1; + break; + + default: + val2 = 0; + + } + + dict2file(fd, (u8 *)&val, len); + found++; + if (val2) { + + dict2file(fd, (u8 *)&val2, len); + found++; + + } + + } + + } + + } + + if ((callInst = dyn_cast(&IN))) { + + bool isStrcmp = true; + bool isMemcmp = true; + bool isStrncmp = true; + bool isStrcasecmp = true; + bool isStrncasecmp = true; + bool isIntMemcpy = true; + bool addedNull = false; + size_t optLen = 0; + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + std::string FuncName = Callee->getName().str(); + isStrcmp &= !FuncName.compare("strcmp"); + isMemcmp &= !FuncName.compare("memcmp"); + isStrncmp &= !FuncName.compare("strncmp"); + isStrcasecmp &= !FuncName.compare("strcasecmp"); + isStrncasecmp &= !FuncName.compare("strncasecmp"); + isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); + + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && + !isStrncasecmp && !isIntMemcpy) + continue; + + /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function + * prototype */ + FunctionType *FT = Callee->getFunctionType(); + + isStrcmp &= + FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); + isStrcasecmp &= + FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); + isMemcmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy() && + FT->getParamType(2)->isIntegerTy(); + isStrncmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()) && + FT->getParamType(2)->isIntegerTy(); + isStrncasecmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()) && + FT->getParamType(2)->isIntegerTy(); + + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && + !isStrncasecmp && !isIntMemcpy) + continue; + + /* is a str{n,}{case,}cmp/memcmp, check if we have + * str{case,}cmp(x, "const") or str{case,}cmp("const", x) + * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, ..) + * memcmp(x, "const", ..) or memcmp("const", x, ..) */ + Value *Str1P = callInst->getArgOperand(0), + *Str2P = callInst->getArgOperand(1); + std::string Str1, Str2; + StringRef TmpStr; + bool HasStr1 = getConstantStringInfo(Str1P, TmpStr); + if (TmpStr.empty()) { + + HasStr1 = false; + + } else { + + HasStr1 = true; + Str1 = TmpStr.str(); + + } + + bool HasStr2 = getConstantStringInfo(Str2P, TmpStr); + if (TmpStr.empty()) { + + HasStr2 = false; + + } else { + + HasStr2 = true; + Str2 = TmpStr.str(); + + } + + if (debug) + fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n", + FuncName.c_str(), Str1P, Str1P->getName().str().c_str(), + Str1.c_str(), HasStr1 == true ? "true" : "false", Str2P, + Str2P->getName().str().c_str(), Str2.c_str(), + HasStr2 == true ? "true" : "false"); + + // we handle the 2nd parameter first because of llvm memcpy + if (!HasStr2) { + + auto *Ptr = dyn_cast(Str2P); + if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { + + if (auto *Var = dyn_cast(Ptr->getOperand(0))) { + + if (Var->hasInitializer()) { + + if (auto *Array = + dyn_cast(Var->getInitializer())) { + + HasStr2 = true; + Str2 = Array->getAsString().str(); + + } + + } + + } + + } + + } + + // for the internal memcpy routine we only care for the second + // parameter and are not reporting anything. + if (isIntMemcpy == true) { + + if (HasStr2 == true) { + + Value * op2 = callInst->getArgOperand(2); + ConstantInt *ilen = dyn_cast(op2); + if (ilen) { + + uint64_t literalLength = Str2.size(); + uint64_t optLength = ilen->getZExtValue(); + if (literalLength + 1 == optLength) { + + Str2.append("\0", 1); // add null byte + addedNull = true; + + } + + } + + valueMap[Str1P] = new std::string(Str2); + + if (debug) + fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), Str1P); + continue; + + } + + continue; + + } + + // Neither a literal nor a global variable? + // maybe it is a local variable that we saved + if (!HasStr2) { + + std::string *strng = valueMap[Str2P]; + if (strng && !strng->empty()) { + + Str2 = *strng; + HasStr2 = true; + if (debug) + fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(), Str2P); + + } + + } + + if (!HasStr1) { + + auto Ptr = dyn_cast(Str1P); + + if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { + + if (auto *Var = dyn_cast(Ptr->getOperand(0))) { + + if (Var->hasInitializer()) { + + if (auto *Array = + dyn_cast(Var->getInitializer())) { + + HasStr1 = true; + Str1 = Array->getAsString().str(); + + } + + } + + } + + } + + } + + // Neither a literal nor a global variable? + // maybe it is a local variable that we saved + if (!HasStr1) { + + std::string *strng = valueMap[Str1P]; + if (strng && !strng->empty()) { + + Str1 = *strng; + HasStr1 = true; + if (debug) + fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(), Str1P); + + } + + } + + /* handle cases of one string is const, one string is variable */ + if (!(HasStr1 ^ HasStr2)) continue; + + std::string thestring; + + if (HasStr1) + thestring = Str1; + else + thestring = Str2; + + optLen = thestring.length(); + + if (isMemcmp || isStrncmp || isStrncasecmp) { + + Value * op2 = callInst->getArgOperand(2); + ConstantInt *ilen = dyn_cast(op2); + if (ilen) { + + uint64_t literalLength = optLen; + optLen = ilen->getZExtValue(); + if (literalLength + 1 == optLen) { // add null byte + thestring.append("\0", 1); + addedNull = true; + + } + + } + + } + + // add null byte if this is a string compare function and a null + // was not already added + if (!isMemcmp) { + + if (addedNull == false) { + + thestring.append("\0", 1); // add null byte + optLen++; + + } + + // ensure we do not have garbage + size_t offset = thestring.find('\0', 0); + if (offset + 1 < optLen) optLen = offset + 1; + thestring = thestring.substr(0, optLen); + + } + + // we take the longer string, even if the compare was to a + // shorter part. Note that depending on the optimizer of the + // compiler this can be wrong, but it is more likely that this + // is helping the fuzzer + if (optLen != thestring.length()) optLen = thestring.length(); + if (optLen > MAX_AUTO_EXTRA) optLen = MAX_AUTO_EXTRA; + if (optLen < 3) // too short? skip + continue; + + ptr = (char *)thestring.c_str(); + + dict2file(fd, (u8 *)ptr, optLen); + found++; + + } + + } + + } + + } + + close(fd); + + /* Say something nice. */ + + if (!be_quiet) { + + if (!found) + OKF("No entries for a dictionary found."); + else + OKF("Wrote %d entries to the dictionary file.\n", found); + + } + + return true; + +} + +char AFLdict2filePass::ID = 0; + +static void registerAFLdict2filePass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + PM.add(new AFLdict2filePass()); + +} + +static RegisterPass X("afl-dict2file", + "afl++ dict2file instrumentation pass", + false, false); + +static RegisterStandardPasses RegisterAFLdict2filePass( + PassManagerBuilder::EP_OptimizerLast, registerAFLdict2filePass); + +static RegisterStandardPasses RegisterAFLdict2filePass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLdict2filePass); + diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc new file mode 100644 index 00000000..125db229 --- /dev/null +++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc @@ -0,0 +1,957 @@ +/* + american fuzzy lop++ - LLVM LTO instrumentation pass + ---------------------------------------------------- + + Written by Marc Heuse + + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This library is plugged into LLVM when invoking clang through afl-clang-lto. + + */ + +#define AFL_LLVM_PASS + +#include "config.h" +#include "debug.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "llvm/Config/llvm-config.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/MemorySSAUpdater.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/Pass.h" +#include "llvm/IR/Constants.h" + +#include "afl-llvm-common.h" + +using namespace llvm; + +namespace { + +class AFLLTOPass : public ModulePass { + + public: + static char ID; + + AFLLTOPass() : ModulePass(ID) { + + char *ptr; + + if (getenv("AFL_DEBUG")) debug = 1; + if ((ptr = getenv("AFL_LLVM_LTO_STARTID")) != NULL) + if ((afl_global_id = atoi(ptr)) < 0 || afl_global_id >= MAP_SIZE) + FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is not between 0 and %d\n", + ptr, MAP_SIZE - 1); + + skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); + + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + + ModulePass::getAnalysisUsage(AU); + AU.addRequired(); + AU.addRequired(); + + } + + bool runOnModule(Module &M) override; + + protected: + int afl_global_id = 1, autodictionary = 1; + uint32_t function_minimum_size = 1; + uint32_t inst_blocks = 0, inst_funcs = 0, total_instr = 0; + uint64_t map_addr = 0x10000; + char * skip_nozero = NULL; + +}; + +} // namespace + +bool AFLLTOPass::runOnModule(Module &M) { + + LLVMContext & C = M.getContext(); + std::vector dictionary; + std::vector calls; + DenseMap valueMap; + std::vector BlockList; + char * ptr; + FILE * documentFile = NULL; + + srand((unsigned int)time(NULL)); + + unsigned long long int moduleID = + (((unsigned long long int)(rand() & 0xffffffff)) << 32) | getpid(); + + IntegerType *Int8Ty = IntegerType::getInt8Ty(C); + IntegerType *Int32Ty = IntegerType::getInt32Ty(C); + IntegerType *Int64Ty = IntegerType::getInt64Ty(C); + + /* Show a banner */ + setvbuf(stdout, NULL, _IONBF, 0); + + if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { + + SAYF(cCYA "afl-llvm-lto" VERSION cRST + " by Marc \"vanHauser\" Heuse \n"); + + } else + + be_quiet = 1; + + if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { + + if ((documentFile = fopen(ptr, "a")) == NULL) + WARNF("Cannot access document file %s", ptr); + + } + + // we make this the default as the fixed map has problems with + // defered forkserver, early constructors, ifuncs and maybe more + /*if (getenv("AFL_LLVM_MAP_DYNAMIC"))*/ + map_addr = 0; + + if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { + + uint64_t val; + if (!*ptr || !strcmp(ptr, "0") || !strcmp(ptr, "0x0")) { + + map_addr = 0; + + } else if (getenv("AFL_LLVM_MAP_DYNAMIC")) { + + FATAL( + "AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used together"); + + } else if (strncmp(ptr, "0x", 2) != 0) { + + map_addr = 0x10000; // the default + + } else { + + val = strtoull(ptr, NULL, 16); + if (val < 0x100 || val > 0xffffffff00000000) { + + FATAL( + "AFL_LLVM_MAP_ADDR must be a value between 0x100 and " + "0xffffffff00000000"); + + } + + map_addr = val; + + } + + } + + if (debug) { fprintf(stderr, "map address is 0x%lx\n", map_addr); } + + /* Get/set the globals for the SHM region. */ + + GlobalVariable *AFLMapPtr = NULL; + Value * MapPtrFixed = NULL; + + if (!map_addr) { + + AFLMapPtr = + new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, + GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); + + } else { + + ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr); + MapPtrFixed = + ConstantExpr::getIntToPtr(MapAddr, PointerType::getUnqual(Int8Ty)); + + } + + ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); + ConstantInt *One = ConstantInt::get(Int8Ty, 1); + + // This dumps all inialized global strings - might be useful in the future + /* + for (auto G=M.getGlobalList().begin(); G!=M.getGlobalList().end(); G++) { + + GlobalVariable &GV=*G; + if (!GV.getName().str().empty()) { + + fprintf(stderr, "Global Variable: %s", GV.getName().str().c_str()); + if (GV.hasInitializer()) + if (auto *Val = dyn_cast(GV.getInitializer())) + fprintf(stderr, " Value: \"%s\"", Val->getAsString().str().c_str()); + fprintf(stderr, "\n"); + + } + + } + + */ + + scanForDangerousFunctions(&M); + + /* Instrument all the things! */ + + int inst_blocks = 0; + + for (auto &F : M) { + + /*For debugging + AttributeSet X = F.getAttributes().getFnAttributes(); + fprintf(stderr, "DEBUG: Module %s Function %s attributes %u\n", + M.getName().str().c_str(), F.getName().str().c_str(), + X.getNumAttributes()); + */ + + if (F.size() < function_minimum_size) continue; + if (isIgnoreFunction(&F)) continue; + + // the instrument file list check + AttributeList Attrs = F.getAttributes(); + if (Attrs.hasAttribute(-1, StringRef("skipinstrument"))) { + + if (debug) + fprintf(stderr, + "DEBUG: Function %s is not in a source file that was specified " + "in the instrument file list\n", + F.getName().str().c_str()); + continue; + + } + + std::vector InsBlocks; + + if (autodictionary) { + + /* Some implementation notes. + * + * We try to handle 3 cases: + * - memcmp("foo", arg, 3) <- literal string + * - static char globalvar[] = "foo"; + * memcmp(globalvar, arg, 3) <- global variable + * - char localvar[] = "foo"; + * memcmp(locallvar, arg, 3) <- local variable + * + * The local variable case is the hardest. We can only detect that + * case if there is no reassignment or change in the variable. + * And it might not work across llvm version. + * What we do is hooking the initializer function for local variables + * (llvm.memcpy.p0i8.p0i8.i64) and note the string and the assigned + * variable. And if that variable is then used in a compare function + * we use that noted string. + * This seems not to work for tokens that have a size <= 4 :-( + * + * - if the compared length is smaller than the string length we + * save the full string. This is likely better for fuzzing but + * might be wrong in a few cases depending on optimizers + * + * - not using StringRef because there is a bug in the llvm 11 + * checkout I am using which sometimes points to wrong strings + * + * Over and out. Took me a full day. damn. mh/vh + */ + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast(&IN))) { + + bool isStrcmp = true; + bool isMemcmp = true; + bool isStrncmp = true; + bool isStrcasecmp = true; + bool isStrncasecmp = true; + bool isIntMemcpy = true; + bool addedNull = false; + size_t optLen = 0; + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + std::string FuncName = Callee->getName().str(); + isStrcmp &= !FuncName.compare("strcmp"); + isMemcmp &= !FuncName.compare("memcmp"); + isStrncmp &= !FuncName.compare("strncmp"); + isStrcasecmp &= !FuncName.compare("strcasecmp"); + isStrncasecmp &= !FuncName.compare("strncasecmp"); + isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); + + /* we do something different here, putting this BB and the + successors in a block map */ + if (!FuncName.compare("__afl_persistent_loop")) { + + BlockList.push_back(&BB); + /* + for (succ_iterator SI = succ_begin(&BB), SE = + succ_end(&BB); SI != SE; ++SI) { + + BasicBlock *succ = *SI; + BlockList.push_back(succ); + + } + + */ + + } + + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && + !isStrncasecmp && !isIntMemcpy) + continue; + + /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function + * prototype */ + FunctionType *FT = Callee->getFunctionType(); + + isStrcmp &= FT->getNumParams() == 2 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()); + isStrcasecmp &= FT->getNumParams() == 2 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()); + isMemcmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy() && + FT->getParamType(2)->isIntegerTy(); + isStrncmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()) && + FT->getParamType(2)->isIntegerTy(); + isStrncasecmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()) && + FT->getParamType(2)->isIntegerTy(); + + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && + !isStrncasecmp && !isIntMemcpy) + continue; + + /* is a str{n,}{case,}cmp/memcmp, check if we have + * str{case,}cmp(x, "const") or str{case,}cmp("const", x) + * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, ..) + * memcmp(x, "const", ..) or memcmp("const", x, ..) */ + Value *Str1P = callInst->getArgOperand(0), + *Str2P = callInst->getArgOperand(1); + std::string Str1, Str2; + StringRef TmpStr; + bool HasStr1 = getConstantStringInfo(Str1P, TmpStr); + if (TmpStr.empty()) { + + HasStr1 = false; + + } else { + + HasStr1 = true; + Str1 = TmpStr.str(); + + } + + bool HasStr2 = getConstantStringInfo(Str2P, TmpStr); + if (TmpStr.empty()) { + + HasStr2 = false; + + } else { + + HasStr2 = true; + Str2 = TmpStr.str(); + + } + + if (debug) + fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n", + FuncName.c_str(), Str1P, Str1P->getName().str().c_str(), + Str1.c_str(), HasStr1 == true ? "true" : "false", Str2P, + Str2P->getName().str().c_str(), Str2.c_str(), + HasStr2 == true ? "true" : "false"); + + // we handle the 2nd parameter first because of llvm memcpy + if (!HasStr2) { + + auto *Ptr = dyn_cast(Str2P); + if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { + + if (auto *Var = dyn_cast(Ptr->getOperand(0))) { + + if (Var->hasInitializer()) { + + if (auto *Array = dyn_cast( + Var->getInitializer())) { + + HasStr2 = true; + Str2 = Array->getAsString().str(); + + } + + } + + } + + } + + } + + // for the internal memcpy routine we only care for the second + // parameter and are not reporting anything. + if (isIntMemcpy == true) { + + if (HasStr2 == true) { + + Value * op2 = callInst->getArgOperand(2); + ConstantInt *ilen = dyn_cast(op2); + if (ilen) { + + uint64_t literalLength = Str2.size(); + uint64_t optLength = ilen->getZExtValue(); + if (literalLength + 1 == optLength) { + + Str2.append("\0", 1); // add null byte + addedNull = true; + + } + + } + + valueMap[Str1P] = new std::string(Str2); + + if (debug) + fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), Str1P); + continue; + + } + + continue; + + } + + // Neither a literal nor a global variable? + // maybe it is a local variable that we saved + if (!HasStr2) { + + std::string *strng = valueMap[Str2P]; + if (strng && !strng->empty()) { + + Str2 = *strng; + HasStr2 = true; + if (debug) + fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(), + Str2P); + + } + + } + + if (!HasStr1) { + + auto Ptr = dyn_cast(Str1P); + + if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { + + if (auto *Var = dyn_cast(Ptr->getOperand(0))) { + + if (Var->hasInitializer()) { + + if (auto *Array = dyn_cast( + Var->getInitializer())) { + + HasStr1 = true; + Str1 = Array->getAsString().str(); + + } + + } + + } + + } + + } + + // Neither a literal nor a global variable? + // maybe it is a local variable that we saved + if (!HasStr1) { + + std::string *strng = valueMap[Str1P]; + if (strng && !strng->empty()) { + + Str1 = *strng; + HasStr1 = true; + if (debug) + fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(), + Str1P); + + } + + } + + /* handle cases of one string is const, one string is variable */ + if (!(HasStr1 ^ HasStr2)) continue; + + std::string thestring; + + if (HasStr1) + thestring = Str1; + else + thestring = Str2; + + optLen = thestring.length(); + + if (isMemcmp || isStrncmp || isStrncasecmp) { + + Value * op2 = callInst->getArgOperand(2); + ConstantInt *ilen = dyn_cast(op2); + if (ilen) { + + uint64_t literalLength = optLen; + optLen = ilen->getZExtValue(); + if (literalLength + 1 == optLen) { // add null byte + thestring.append("\0", 1); + addedNull = true; + + } + + } + + } + + // add null byte if this is a string compare function and a null + // was not already added + if (!isMemcmp) { + + if (addedNull == false) { + + thestring.append("\0", 1); // add null byte + optLen++; + + } + + // ensure we do not have garbage + size_t offset = thestring.find('\0', 0); + if (offset + 1 < optLen) optLen = offset + 1; + thestring = thestring.substr(0, optLen); + + } + + if (!be_quiet) { + + std::string outstring; + fprintf(stderr, "%s: length %zu/%zu \"", FuncName.c_str(), optLen, + thestring.length()); + for (uint8_t i = 0; i < thestring.length(); i++) { + + uint8_t c = thestring[i]; + if (c <= 32 || c >= 127) + fprintf(stderr, "\\x%02x", c); + else + fprintf(stderr, "%c", c); + + } + + fprintf(stderr, "\"\n"); + + } + + // we take the longer string, even if the compare was to a + // shorter part. Note that depending on the optimizer of the + // compiler this can be wrong, but it is more likely that this + // is helping the fuzzer + if (optLen != thestring.length()) optLen = thestring.length(); + if (optLen > MAX_AUTO_EXTRA) optLen = MAX_AUTO_EXTRA; + if (optLen < MIN_AUTO_EXTRA) // too short? skip + continue; + + dictionary.push_back(thestring.substr(0, optLen)); + + } + + } + + } + + } + + for (auto &BB : F) { + + if (F.size() == 1) { + + InsBlocks.push_back(&BB); + continue; + + } + + uint32_t succ = 0; + for (succ_iterator SI = succ_begin(&BB), SE = succ_end(&BB); SI != SE; + ++SI) + if ((*SI)->size() > 0) succ++; + if (succ < 2) // no need to instrument + continue; + + if (BlockList.size()) { + + int skip = 0; + for (uint32_t k = 0; k < BlockList.size(); k++) { + + if (&BB == BlockList[k]) { + + if (debug) + fprintf(stderr, + "DEBUG: Function %s skipping BB with/after __afl_loop\n", + F.getName().str().c_str()); + skip = 1; + + } + + } + + if (skip) continue; + + } + + InsBlocks.push_back(&BB); + + } + + if (InsBlocks.size() > 0) { + + uint32_t i = InsBlocks.size(); + + do { + + --i; + BasicBlock * newBB = NULL; + BasicBlock * origBB = &(*InsBlocks[i]); + std::vector Successors; + Instruction * TI = origBB->getTerminator(); + uint32_t fs = origBB->getParent()->size(); + uint32_t countto; + + for (succ_iterator SI = succ_begin(origBB), SE = succ_end(origBB); + SI != SE; ++SI) { + + BasicBlock *succ = *SI; + Successors.push_back(succ); + + } + + if (fs == 1) { + + newBB = origBB; + countto = 1; + + } else { + + if (TI == NULL || TI->getNumSuccessors() < 2) continue; + countto = Successors.size(); + + } + + // if (Successors.size() != TI->getNumSuccessors()) + // FATAL("Different successor numbers %lu <-> %u\n", Successors.size(), + // TI->getNumSuccessors()); + + for (uint32_t j = 0; j < countto; j++) { + + if (fs != 1) newBB = llvm::SplitEdge(origBB, Successors[j]); + + if (!newBB) { + + if (!be_quiet) WARNF("Split failed!"); + continue; + + } + + if (documentFile) { + + fprintf(documentFile, "ModuleID=%llu Function=%s edgeID=%u\n", + moduleID, F.getName().str().c_str(), afl_global_id); + + } + + BasicBlock::iterator IP = newBB->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + + /* Set the ID of the inserted basic block */ + + ConstantInt *CurLoc = ConstantInt::get(Int32Ty, afl_global_id++); + + /* Load SHM pointer */ + + Value *MapPtrIdx; + + if (map_addr) { + + MapPtrIdx = IRB.CreateGEP(MapPtrFixed, CurLoc); + + } else { + + LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); + MapPtr->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + MapPtrIdx = IRB.CreateGEP(MapPtr, CurLoc); + + } + + /* Update bitmap */ + + LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); + Counter->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + Value *Incr = IRB.CreateAdd(Counter, One); + + if (skip_nozero == NULL) { + + auto cf = IRB.CreateICmpEQ(Incr, Zero); + auto carry = IRB.CreateZExt(cf, Int8Ty); + Incr = IRB.CreateAdd(Incr, carry); + + } + + IRB.CreateStore(Incr, MapPtrIdx) + ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + // done :) + + inst_blocks++; + + } + + } while (i > 0); + + } + + } + + if (documentFile) fclose(documentFile); + documentFile = NULL; + + // save highest location ID to global variable + // do this after each function to fail faster + if (!be_quiet && afl_global_id > MAP_SIZE && + afl_global_id > FS_OPT_MAX_MAPSIZE) { + + uint32_t pow2map = 1, map = afl_global_id; + while ((map = map >> 1)) + pow2map++; + WARNF( + "We have %u blocks to instrument but the map size is only %u. Either " + "edit config.h and set MAP_SIZE_POW2 from %u to %u, then recompile " + "afl-fuzz and llvm_mode and then make this target - or set " + "AFL_MAP_SIZE with at least size %u when running afl-fuzz with this " + "target.", + afl_global_id, MAP_SIZE, MAP_SIZE_POW2, pow2map, afl_global_id); + + } + + if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) { + + // yes we could create our own function, insert it into ctors ... + // but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o + + Function *f = M.getFunction("__afl_auto_init_globals"); + + if (!f) { + + fprintf(stderr, + "Error: init function could not be found (this should not " + "happen)\n"); + exit(-1); + + } + + BasicBlock *bb = &f->getEntryBlock(); + if (!bb) { + + fprintf(stderr, + "Error: init function does not have an EntryBlock (this should " + "not happen)\n"); + exit(-1); + + } + + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + + if (map_addr) { + + GlobalVariable *AFLMapAddrFixed = new GlobalVariable( + M, Int64Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_map_addr"); + ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr); + StoreInst * StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed); + StoreMapAddr->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) { + + uint32_t write_loc = afl_global_id; + + if (afl_global_id % 8) write_loc = (((afl_global_id + 8) >> 3) << 3); + + GlobalVariable *AFLFinalLoc = new GlobalVariable( + M, Int32Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_final_loc"); + ConstantInt *const_loc = ConstantInt::get(Int32Ty, write_loc); + StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc); + StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + if (dictionary.size()) { + + size_t memlen = 0, count = 0, offset = 0; + char * ptr; + + for (auto token : dictionary) { + + memlen += token.length(); + count++; + + } + + if (!be_quiet) + printf("AUTODICTIONARY: %lu string%s found\n", count, + count == 1 ? "" : "s"); + + if (count) { + + if ((ptr = (char *)malloc(memlen + count)) == NULL) { + + fprintf(stderr, "Error: malloc for %lu bytes failed!\n", + memlen + count); + exit(-1); + + } + + count = 0; + + for (auto token : dictionary) { + + if (offset + token.length() < 0xfffff0 && count < MAX_AUTO_EXTRAS) { + + ptr[offset++] = (uint8_t)token.length(); + memcpy(ptr + offset, token.c_str(), token.length()); + offset += token.length(); + count++; + + } + + } + + GlobalVariable *AFLDictionaryLen = + new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, + 0, "__afl_dictionary_len"); + ConstantInt *const_len = ConstantInt::get(Int32Ty, offset); + StoreInst *StoreDictLen = IRB.CreateStore(const_len, AFLDictionaryLen); + StoreDictLen->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + ArrayType *ArrayTy = ArrayType::get(IntegerType::get(C, 8), offset); + GlobalVariable *AFLInternalDictionary = new GlobalVariable( + M, ArrayTy, true, GlobalValue::ExternalLinkage, + ConstantDataArray::get(C, + *(new ArrayRef((char *)ptr, offset))), + "__afl_internal_dictionary"); + AFLInternalDictionary->setInitializer(ConstantDataArray::get( + C, *(new ArrayRef((char *)ptr, offset)))); + AFLInternalDictionary->setConstant(true); + + GlobalVariable *AFLDictionary = new GlobalVariable( + M, PointerType::get(Int8Ty, 0), false, GlobalValue::ExternalLinkage, + 0, "__afl_dictionary"); + + Value *AFLDictOff = IRB.CreateGEP(AFLInternalDictionary, Zero); + Value *AFLDictPtr = + IRB.CreatePointerCast(AFLDictOff, PointerType::get(Int8Ty, 0)); + StoreInst *StoreDict = IRB.CreateStore(AFLDictPtr, AFLDictionary); + StoreDict->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + } + + } + + /* Say something nice. */ + + if (!be_quiet) { + + if (!inst_blocks) + WARNF("No instrumentation targets found."); + else { + + char modeline[100]; + snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", + getenv("AFL_HARDEN") ? "hardened" : "non-hardened", + getenv("AFL_USE_ASAN") ? ", ASAN" : "", + getenv("AFL_USE_MSAN") ? ", MSAN" : "", + getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", + getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); + OKF("Instrumented %u locations with no collisions (on average %llu " + "collisions would be in afl-gcc/afl-clang-fast) (%s mode).", + inst_blocks, calculateCollisions(inst_blocks), modeline); + + } + + } + + return true; + +} + +char AFLLTOPass::ID = 0; + +static void registerAFLLTOPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + PM.add(new AFLLTOPass()); + +} + +static RegisterPass X("afl-lto", "afl++ LTO instrumentation pass", + false, false); + +static RegisterStandardPasses RegisterAFLLTOPass( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerAFLLTOPass); + diff --git a/instrumentation/afl-llvm-lto-instrumentlist.so.cc b/instrumentation/afl-llvm-lto-instrumentlist.so.cc new file mode 100644 index 00000000..a7331444 --- /dev/null +++ b/instrumentation/afl-llvm-lto-instrumentlist.so.cc @@ -0,0 +1,147 @@ +/* + american fuzzy lop++ - LLVM-mode instrumentation pass + --------------------------------------------------- + + Written by Laszlo Szekeres and + Michal Zalewski + + LLVM integration design comes from Laszlo Szekeres. C bits copied-and-pasted + from afl-as.c are Michal's fault. + + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This library is plugged into LLVM when invoking clang through afl-clang-fast. + It tells the compiler to add code roughly equivalent to the bits discussed + in ../afl-as.h. + + */ + +#define AFL_LLVM_PASS + +#include "config.h" +#include "debug.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/IR/CFG.h" + +#include "afl-llvm-common.h" + +using namespace llvm; + +namespace { + +class AFLcheckIfInstrument : public ModulePass { + + public: + static char ID; + AFLcheckIfInstrument() : ModulePass(ID) { + + if (getenv("AFL_DEBUG")) debug = 1; + + initInstrumentList(); + + } + + bool runOnModule(Module &M) override; + + // StringRef getPassName() const override { + + // return "American Fuzzy Lop Instrumentation"; + // } + + protected: + std::list myInstrumentList; + +}; + +} // namespace + +char AFLcheckIfInstrument::ID = 0; + +bool AFLcheckIfInstrument::runOnModule(Module &M) { + + /* Show a banner */ + + setvbuf(stdout, NULL, _IONBF, 0); + + if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { + + SAYF(cCYA "afl-llvm-lto-instrumentlist" VERSION cRST + " by Marc \"vanHauser\" Heuse \n"); + + } else if (getenv("AFL_QUIET")) + + be_quiet = 1; + + for (auto &F : M) { + + if (F.size() < 1) continue; + + // fprintf(stderr, "F:%s\n", F.getName().str().c_str()); + + if (isInInstrumentList(&F)) { + + if (debug) + SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n", + F.getName().str().c_str()); + + } else { + + if (debug) + SAYF(cMGN "[D] " cRST + "function %s is NOT in the instrument file list\n", + F.getName().str().c_str()); + + auto & Ctx = F.getContext(); + AttributeList Attrs = F.getAttributes(); + AttrBuilder NewAttrs; + NewAttrs.addAttribute("skipinstrument"); + F.setAttributes( + Attrs.addAttributes(Ctx, AttributeList::FunctionIndex, NewAttrs)); + + } + + } + + return true; + +} + +static void registerAFLcheckIfInstrumentpass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + PM.add(new AFLcheckIfInstrument()); + +} + +static RegisterStandardPasses RegisterAFLcheckIfInstrumentpass( + PassManagerBuilder::EP_ModuleOptimizerEarly, + registerAFLcheckIfInstrumentpass); + +static RegisterStandardPasses RegisterAFLcheckIfInstrumentpass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, + registerAFLcheckIfInstrumentpass); + diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc new file mode 100644 index 00000000..8c8c987a --- /dev/null +++ b/instrumentation/afl-llvm-pass.so.cc @@ -0,0 +1,654 @@ +/* + american fuzzy lop++ - LLVM-mode instrumentation pass + --------------------------------------------------- + + Written by Laszlo Szekeres , + Adrian Herrera , + Michal Zalewski + + LLVM integration design comes from Laszlo Szekeres. C bits copied-and-pasted + from afl-as.c are Michal's fault. + + NGRAM previous location coverage comes from Adrian Herrera. + + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This library is plugged into LLVM when invoking clang through afl-clang-fast. + It tells the compiler to add code roughly equivalent to the bits discussed + in ../afl-as.h. + + */ + +#define AFL_LLVM_PASS + +#include "config.h" +#include "debug.h" +#include +#include +#include + +#include +#include +#include +#include + +#include "llvm/Config/llvm-config.h" +#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5 +typedef long double max_align_t; +#endif + +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" + +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) + #include "llvm/IR/DebugInfo.h" + #include "llvm/IR/CFG.h" +#else + #include "llvm/DebugInfo.h" + #include "llvm/Support/CFG.h" +#endif + +#include "afl-llvm-common.h" +#include "llvm-ngram-coverage.h" + +using namespace llvm; + +namespace { + +class AFLCoverage : public ModulePass { + + public: + static char ID; + AFLCoverage() : ModulePass(ID) { + + initInstrumentList(); + + } + + bool runOnModule(Module &M) override; + + protected: + uint32_t ngram_size = 0; + uint32_t map_size = MAP_SIZE; + uint32_t function_minimum_size = 1; + char * ctx_str = NULL, *skip_nozero = NULL; + +}; + +} // namespace + +char AFLCoverage::ID = 0; + +/* needed up to 3.9.0 */ +#if LLVM_VERSION_MAJOR == 3 && \ + (LLVM_VERSION_MINOR < 9 || \ + (LLVM_VERSION_MINOR == 9 && LLVM_VERSION_PATCH < 1)) +uint64_t PowerOf2Ceil(unsigned in) { + + uint64_t in64 = in - 1; + in64 |= (in64 >> 1); + in64 |= (in64 >> 2); + in64 |= (in64 >> 4); + in64 |= (in64 >> 8); + in64 |= (in64 >> 16); + in64 |= (in64 >> 32); + return in64 + 1; + +} + +#endif + +/* #if LLVM_VERSION_STRING >= "4.0.1" */ +#if LLVM_VERSION_MAJOR > 4 || \ + (LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1) + #define AFL_HAVE_VECTOR_INTRINSICS 1 +#endif +bool AFLCoverage::runOnModule(Module &M) { + + LLVMContext &C = M.getContext(); + + IntegerType *Int8Ty = IntegerType::getInt8Ty(C); + IntegerType *Int32Ty = IntegerType::getInt32Ty(C); +#ifdef AFL_HAVE_VECTOR_INTRINSICS + IntegerType *IntLocTy = + IntegerType::getIntNTy(C, sizeof(PREV_LOC_T) * CHAR_BIT); +#endif + struct timeval tv; + struct timezone tz; + u32 rand_seed; + unsigned int cur_loc = 0; + + /* Setup random() so we get Actually Random(TM) outputs from AFL_R() */ + gettimeofday(&tv, &tz); + rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); + AFL_SR(rand_seed); + + /* Show a banner */ + + setvbuf(stdout, NULL, _IONBF, 0); + + if (getenv("AFL_DEBUG")) debug = 1; + + if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { + + SAYF(cCYA "afl-llvm-pass" VERSION cRST + " by and \n"); + + } else + + be_quiet = 1; + + /* + char *ptr; + if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) { + + map_size = atoi(ptr); + if (map_size < 8 || map_size > (1 << 29)) + FATAL("illegal AFL_MAP_SIZE %u, must be between 2^3 and 2^30", + map_size); if (map_size % 8) map_size = (((map_size >> 3) + 1) << 3); + + } + + */ + + /* Decide instrumentation ratio */ + + char * inst_ratio_str = getenv("AFL_INST_RATIO"); + unsigned int inst_ratio = 100; + + if (inst_ratio_str) { + + if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || + inst_ratio > 100) + FATAL("Bad value of AFL_INST_RATIO (must be between 1 and 100)"); + + } + +#if LLVM_VERSION_MAJOR < 9 + char *neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO"); +#endif + skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); + + unsigned PrevLocSize = 0; + + char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); + if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); + ctx_str = getenv("AFL_LLVM_CTX"); + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + /* Decide previous location vector size (must be a power of two) */ + VectorType *PrevLocTy = NULL; + + if (ngram_size_str) + if (sscanf(ngram_size_str, "%u", &ngram_size) != 1 || ngram_size < 2 || + ngram_size > NGRAM_SIZE_MAX) + FATAL( + "Bad value of AFL_NGRAM_SIZE (must be between 2 and NGRAM_SIZE_MAX " + "(%u))", + NGRAM_SIZE_MAX); + + if (ngram_size == 1) ngram_size = 0; + if (ngram_size) + PrevLocSize = ngram_size - 1; + else +#else + if (ngram_size_str) + #ifndef LLVM_VERSION_PATCH + FATAL( + "Sorry, NGRAM branch coverage is not supported with llvm version " + "%d.%d.%d!", + LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0); + #else + FATAL( + "Sorry, NGRAM branch coverage is not supported with llvm version " + "%d.%d.%d!", + LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH); + #endif +#endif + PrevLocSize = 1; + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + int PrevLocVecSize = PowerOf2Ceil(PrevLocSize); + if (ngram_size) + PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize + #if LLVM_VERSION_MAJOR >= 12 + , + false + #endif + ); +#endif + + /* Get globals for the SHM region and the previous location. Note that + __afl_prev_loc is thread-local. */ + + GlobalVariable *AFLMapPtr = + new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, + GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); + GlobalVariable *AFLPrevLoc; + GlobalVariable *AFLContext = NULL; + + if (ctx_str) +#ifdef __ANDROID__ + AFLContext = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx"); +#else + AFLContext = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx", 0, + GlobalVariable::GeneralDynamicTLSModel, 0, false); +#endif + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ngram_size) + #ifdef __ANDROID__ + AFLPrevLoc = new GlobalVariable( + M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage, + /* Initializer */ nullptr, "__afl_prev_loc"); + #else + AFLPrevLoc = new GlobalVariable( + M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage, + /* Initializer */ nullptr, "__afl_prev_loc", + /* InsertBefore */ nullptr, GlobalVariable::GeneralDynamicTLSModel, + /* AddressSpace */ 0, /* IsExternallyInitialized */ false); + #endif + else +#endif +#ifdef __ANDROID__ + AFLPrevLoc = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc"); +#else + AFLPrevLoc = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0, + GlobalVariable::GeneralDynamicTLSModel, 0, false); +#endif + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + /* Create the vector shuffle mask for updating the previous block history. + Note that the first element of the vector will store cur_loc, so just set + it to undef to allow the optimizer to do its thing. */ + + SmallVector PrevLocShuffle = {UndefValue::get(Int32Ty)}; + + for (unsigned I = 0; I < PrevLocSize - 1; ++I) + PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, I)); + + for (int I = PrevLocSize; I < PrevLocVecSize; ++I) + PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize)); + + Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle); +#endif + + // other constants we need + ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); + ConstantInt *One = ConstantInt::get(Int8Ty, 1); + + LoadInst *PrevCtx = NULL; // CTX sensitive coverage + + /* Instrument all the things! */ + + int inst_blocks = 0; + scanForDangerousFunctions(&M); + + for (auto &F : M) { + + int has_calls = 0; + if (debug) + fprintf(stderr, "FUNCTION: %s (%zu)\n", F.getName().str().c_str(), + F.size()); + + if (!isInInstrumentList(&F)) continue; + + if (F.size() < function_minimum_size) continue; + + for (auto &BB : F) { + + BasicBlock::iterator IP = BB.getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + + // Context sensitive coverage + if (ctx_str && &BB == &F.getEntryBlock()) { + + // load the context ID of the previous function and write to to a local + // variable on the stack + PrevCtx = IRB.CreateLoad(AFLContext); + PrevCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + // does the function have calls? and is any of the calls larger than one + // basic block? + for (auto &BB : F) { + + if (has_calls) break; + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + if ((callInst = dyn_cast(&IN))) { + + Function *Callee = callInst->getCalledFunction(); + if (!Callee || Callee->size() < function_minimum_size) + continue; + else { + + has_calls = 1; + break; + + } + + } + + } + + } + + // if yes we store a context ID for this function in the global var + if (has_calls) { + + ConstantInt *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size)); + StoreInst * StoreCtx = IRB.CreateStore(NewCtx, AFLContext); + StoreCtx->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + } + + if (AFL_R(100) >= inst_ratio) continue; + + /* Make up cur_loc */ + + // cur_loc++; + cur_loc = AFL_R(map_size); + +/* There is a problem with Ubuntu 18.04 and llvm 6.0 (see issue #63). + The inline function successors() is not inlined and also not found at runtime + :-( As I am unable to detect Ubuntu18.04 heree, the next best thing is to + disable this optional optimization for LLVM 6.0.0 and Linux */ +#if !(LLVM_VERSION_MAJOR == 6 && LLVM_VERSION_MINOR == 0) || !defined __linux__ + // only instrument if this basic block is the destination of a previous + // basic block that has multiple successors + // this gets rid of ~5-10% of instrumentations that are unnecessary + // result: a little more speed and less map pollution + int more_than_one = -1; + // fprintf(stderr, "BB %u: ", cur_loc); + for (pred_iterator PI = pred_begin(&BB), E = pred_end(&BB); PI != E; + ++PI) { + + BasicBlock *Pred = *PI; + + int count = 0; + if (more_than_one == -1) more_than_one = 0; + // fprintf(stderr, " %p=>", Pred); + + for (succ_iterator SI = succ_begin(Pred), E = succ_end(Pred); SI != E; + ++SI) { + + BasicBlock *Succ = *SI; + + // if (count > 0) + // fprintf(stderr, "|"); + if (Succ != NULL) count++; + // fprintf(stderr, "%p", Succ); + + } + + if (count > 1) more_than_one = 1; + + } + + // fprintf(stderr, " == %d\n", more_than_one); + if (F.size() > 1 && more_than_one != 1) { + + // in CTX mode we have to restore the original context for the caller - + // she might be calling other functions which need the correct CTX + if (ctx_str && has_calls) { + + Instruction *Inst = BB.getTerminator(); + if (isa(Inst) || isa(Inst)) { + + IRBuilder<> Post_IRB(Inst); + StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + } + + continue; + + } + +#endif + + ConstantInt *CurLoc; + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ngram_size) + CurLoc = ConstantInt::get(IntLocTy, cur_loc); + else +#endif + CurLoc = ConstantInt::get(Int32Ty, cur_loc); + + /* Load prev_loc */ + + LoadInst *PrevLoc = IRB.CreateLoad(AFLPrevLoc); + PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + Value *PrevLocTrans; + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + /* "For efficiency, we propose to hash the tuple as a key into the + hit_count map as (prev_block_trans << 1) ^ curr_block_trans, where + prev_block_trans = (block_trans_1 ^ ... ^ block_trans_(n-1)" */ + + if (ngram_size) + PrevLocTrans = + IRB.CreateZExt(IRB.CreateXorReduce(PrevLoc), IRB.getInt32Ty()); + else +#endif + PrevLocTrans = PrevLoc; + + if (ctx_str) + PrevLocTrans = + IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty); + else + PrevLocTrans = IRB.CreateZExt(PrevLocTrans, IRB.getInt32Ty()); + + /* Load SHM pointer */ + + LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); + MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + Value *MapPtrIdx; +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ngram_size) + MapPtrIdx = IRB.CreateGEP( + MapPtr, + IRB.CreateZExt( + IRB.CreateXor(PrevLocTrans, IRB.CreateZExt(CurLoc, Int32Ty)), + Int32Ty)); + else +#endif + MapPtrIdx = IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocTrans, CurLoc)); + + /* Update bitmap */ + + LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); + Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + Value *Incr = IRB.CreateAdd(Counter, One); + +#if LLVM_VERSION_MAJOR < 9 + if (neverZero_counters_str != + NULL) { // with llvm 9 we make this the default as the bug in llvm is + // then fixed +#else + if (!skip_nozero) { + +#endif + /* hexcoder: Realize a counter that skips zero during overflow. + * Once this counter reaches its maximum value, it next increments to 1 + * + * Instead of + * Counter + 1 -> Counter + * we inject now this + * Counter + 1 -> {Counter, OverflowFlag} + * Counter + OverflowFlag -> Counter + */ + + auto cf = IRB.CreateICmpEQ(Incr, Zero); + auto carry = IRB.CreateZExt(cf, Int8Ty); + Incr = IRB.CreateAdd(Incr, carry); + + } + + IRB.CreateStore(Incr, MapPtrIdx) + ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + /* Update prev_loc history vector (by placing cur_loc at the head of the + vector and shuffle the other elements back by one) */ + + StoreInst *Store; + +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ngram_size) { + + Value *ShuffledPrevLoc = IRB.CreateShuffleVector( + PrevLoc, UndefValue::get(PrevLocTy), PrevLocShuffleMask); + Value *UpdatedPrevLoc = IRB.CreateInsertElement( + ShuffledPrevLoc, IRB.CreateLShr(CurLoc, (uint64_t)1), (uint64_t)0); + + Store = IRB.CreateStore(UpdatedPrevLoc, AFLPrevLoc); + Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + } else + +#endif + { + + Store = IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), + AFLPrevLoc); + + } + + // in CTX mode we have to restore the original context for the caller - + // she might be calling other functions which need the correct CTX. + // Currently this is only needed for the Ubuntu clang-6.0 bug + if (ctx_str && has_calls) { + + Instruction *Inst = BB.getTerminator(); + if (isa(Inst) || isa(Inst)) { + + IRBuilder<> Post_IRB(Inst); + StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + } + + inst_blocks++; + + } + + } + + /* + // This is currently disabled because we not only need to create/insert a + // function (easy), but also add it as a constructor with an ID < 5 + + if (getenv("AFL_LLVM_DONTWRITEID") == NULL) { + + // yes we could create our own function, insert it into ctors ... + // but this would be a pain in the butt ... so we use afl-llvm-rt.o + + Function *f = ... + + if (!f) { + + fprintf(stderr, + "Error: init function could not be created (this should not + happen)\n"); exit(-1); + + } + + ... constructor for f = 4 + + BasicBlock *bb = &f->getEntryBlock(); + if (!bb) { + + fprintf(stderr, + "Error: init function does not have an EntryBlock (this should + not happen)\n"); exit(-1); + + } + + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + + if (map_size <= 0x800000) { + + GlobalVariable *AFLFinalLoc = new GlobalVariable( + M, Int32Ty, true, GlobalValue::ExternalLinkage, 0, + "__afl_final_loc"); + ConstantInt *const_loc = ConstantInt::get(Int32Ty, map_size); + StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc); + StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + } + + */ + + /* Say something nice. */ + + if (!be_quiet) { + + if (!inst_blocks) + WARNF("No instrumentation targets found."); + else { + + char modeline[100]; + snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", + getenv("AFL_HARDEN") ? "hardened" : "non-hardened", + getenv("AFL_USE_ASAN") ? ", ASAN" : "", + getenv("AFL_USE_MSAN") ? ", MSAN" : "", + getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", + getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); + OKF("Instrumented %u locations (%s mode, ratio %u%%).", inst_blocks, + modeline, inst_ratio); + + } + + } + + return true; + +} + +static void registerAFLPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + PM.add(new AFLCoverage()); + +} + +static RegisterStandardPasses RegisterAFLPass( + PassManagerBuilder::EP_OptimizerLast, registerAFLPass); + +static RegisterStandardPasses RegisterAFLPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass); + diff --git a/instrumentation/afl-llvm-rt-lto.o.c b/instrumentation/afl-llvm-rt-lto.o.c new file mode 100644 index 00000000..e53785ff --- /dev/null +++ b/instrumentation/afl-llvm-rt-lto.o.c @@ -0,0 +1,27 @@ +/* + american fuzzy lop++ - LLVM instrumentation bootstrap + ----------------------------------------------------- + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +*/ + +#include +#include + +// to prevent the function from being removed +unsigned char __afl_lto_mode = 0; + +/* Proper initialization routine. */ + +__attribute__((constructor(0))) void __afl_auto_init_globals(void) { + + if (getenv("AFL_DEBUG")) fprintf(stderr, "[__afl_auto_init_globals]\n"); + __afl_lto_mode = 1; + +} + diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc new file mode 100644 index 00000000..d5de3dbb --- /dev/null +++ b/instrumentation/cmplog-instructions-pass.cc @@ -0,0 +1,292 @@ +/* + american fuzzy lop++ - LLVM CmpLog instrumentation + -------------------------------------------------- + + Written by Andrea Fioraldi + + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include "llvm/Config/llvm-config.h" + +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Pass.h" +#include "llvm/Analysis/ValueTracking.h" + +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) + #include "llvm/IR/Verifier.h" + #include "llvm/IR/DebugInfo.h" +#else + #include "llvm/Analysis/Verifier.h" + #include "llvm/DebugInfo.h" + #define nullptr 0 +#endif + +#include +#include "afl-llvm-common.h" + +using namespace llvm; + +namespace { + +class CmpLogInstructions : public ModulePass { + + public: + static char ID; + CmpLogInstructions() : ModulePass(ID) { + + initInstrumentList(); + + } + + bool runOnModule(Module &M) override; + +#if LLVM_VERSION_MAJOR < 4 + const char *getPassName() const override { + +#else + StringRef getPassName() const override { + +#endif + return "cmplog instructions"; + + } + + private: + bool hookInstrs(Module &M); + +}; + +} // namespace + +char CmpLogInstructions::ID = 0; + +bool CmpLogInstructions::hookInstrs(Module &M) { + + std::vector icomps; + LLVMContext & C = M.getContext(); + + Type * VoidTy = Type::getVoidTy(C); + IntegerType *Int8Ty = IntegerType::getInt8Ty(C); + IntegerType *Int16Ty = IntegerType::getInt16Ty(C); + IntegerType *Int32Ty = IntegerType::getInt32Ty(C); + IntegerType *Int64Ty = IntegerType::getInt64Ty(C); + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c1 = M.getOrInsertFunction("__cmplog_ins_hook1", VoidTy, Int8Ty, Int8Ty +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogHookIns1 = cast(c1); +#else + FunctionCallee cmplogHookIns1 = c1; +#endif + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c2 = M.getOrInsertFunction("__cmplog_ins_hook2", VoidTy, Int16Ty, Int16Ty +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogHookIns2 = cast(c2); +#else + FunctionCallee cmplogHookIns2 = c2; +#endif + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c4 = M.getOrInsertFunction("__cmplog_ins_hook4", VoidTy, Int32Ty, Int32Ty +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogHookIns4 = cast(c4); +#else + FunctionCallee cmplogHookIns4 = c4; +#endif + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c8 = M.getOrInsertFunction("__cmplog_ins_hook8", VoidTy, Int64Ty, Int64Ty +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogHookIns8 = cast(c8); +#else + FunctionCallee cmplogHookIns8 = c8; +#endif + + /* iterate over all functions, bbs and instruction and add suitable calls */ + for (auto &F : M) { + + if (!isInInstrumentList(&F)) continue; + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CmpInst *selectcmpInst = nullptr; + + if ((selectcmpInst = dyn_cast(&IN))) { + + if (selectcmpInst->getPredicate() == CmpInst::ICMP_EQ || + selectcmpInst->getPredicate() == CmpInst::ICMP_NE || + selectcmpInst->getPredicate() == CmpInst::ICMP_UGT || + selectcmpInst->getPredicate() == CmpInst::ICMP_SGT || + selectcmpInst->getPredicate() == CmpInst::ICMP_ULT || + selectcmpInst->getPredicate() == CmpInst::ICMP_SLT || + selectcmpInst->getPredicate() == CmpInst::ICMP_UGE || + selectcmpInst->getPredicate() == CmpInst::ICMP_SGE || + selectcmpInst->getPredicate() == CmpInst::ICMP_ULE || + selectcmpInst->getPredicate() == CmpInst::ICMP_SLE) { + + auto op0 = selectcmpInst->getOperand(0); + auto op1 = selectcmpInst->getOperand(1); + + IntegerType *intTyOp0 = dyn_cast(op0->getType()); + IntegerType *intTyOp1 = dyn_cast(op1->getType()); + + /* this is probably not needed but we do it anyway */ + if (!intTyOp0 || !intTyOp1) { continue; } + + icomps.push_back(selectcmpInst); + + } + + } + + } + + } + + } + + if (!icomps.size()) return false; + if (!be_quiet) errs() << "Hooking " << icomps.size() << " cmp instructions\n"; + + for (auto &selectcmpInst : icomps) { + + IRBuilder<> IRB(selectcmpInst->getParent()); + IRB.SetInsertPoint(selectcmpInst); + + auto op0 = selectcmpInst->getOperand(0); + auto op1 = selectcmpInst->getOperand(1); + + IntegerType *intTyOp0 = dyn_cast(op0->getType()); + IntegerType *intTyOp1 = dyn_cast(op1->getType()); + + unsigned max_size = intTyOp0->getBitWidth() > intTyOp1->getBitWidth() + ? intTyOp0->getBitWidth() + : intTyOp1->getBitWidth(); + + std::vector args; + args.push_back(op0); + args.push_back(op1); + + switch (max_size) { + + case 8: + IRB.CreateCall(cmplogHookIns1, args); + break; + case 16: + IRB.CreateCall(cmplogHookIns2, args); + break; + case 32: + IRB.CreateCall(cmplogHookIns4, args); + break; + case 64: + IRB.CreateCall(cmplogHookIns8, args); + break; + default: + break; + + } + + } + + return true; + +} + +bool CmpLogInstructions::runOnModule(Module &M) { + + if (getenv("AFL_QUIET") == NULL) + llvm::errs() + << "Running cmplog-instructions-pass by andreafioraldi@gmail.com\n"; + else + be_quiet = 1; + hookInstrs(M); + verifyModule(M); + + return true; + +} + +static void registerCmpLogInstructionsPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + auto p = new CmpLogInstructions(); + PM.add(p); + +} + +static RegisterStandardPasses RegisterCmpLogInstructionsPass( + PassManagerBuilder::EP_OptimizerLast, registerCmpLogInstructionsPass); + +static RegisterStandardPasses RegisterCmpLogInstructionsPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogInstructionsPass); + +#if LLVM_VERSION_MAJOR >= 11 +static RegisterStandardPasses RegisterCmpLogInstructionsPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerCmpLogInstructionsPass); +#endif + diff --git a/instrumentation/cmplog-routines-pass.cc b/instrumentation/cmplog-routines-pass.cc new file mode 100644 index 00000000..c44f38c4 --- /dev/null +++ b/instrumentation/cmplog-routines-pass.cc @@ -0,0 +1,212 @@ +/* + american fuzzy lop++ - LLVM CmpLog instrumentation + -------------------------------------------------- + + Written by Andrea Fioraldi + + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include "llvm/Config/llvm-config.h" + +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Pass.h" +#include "llvm/Analysis/ValueTracking.h" + +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) + #include "llvm/IR/Verifier.h" + #include "llvm/IR/DebugInfo.h" +#else + #include "llvm/Analysis/Verifier.h" + #include "llvm/DebugInfo.h" + #define nullptr 0 +#endif + +#include +#include "afl-llvm-common.h" + +using namespace llvm; + +namespace { + +class CmpLogRoutines : public ModulePass { + + public: + static char ID; + CmpLogRoutines() : ModulePass(ID) { + + initInstrumentList(); + + } + + bool runOnModule(Module &M) override; + +#if LLVM_VERSION_MAJOR < 4 + const char *getPassName() const override { + +#else + StringRef getPassName() const override { + +#endif + return "cmplog routines"; + + } + + private: + bool hookRtns(Module &M); + +}; + +} // namespace + +char CmpLogRoutines::ID = 0; + +bool CmpLogRoutines::hookRtns(Module &M) { + + std::vector calls; + LLVMContext & C = M.getContext(); + + Type *VoidTy = Type::getVoidTy(C); + // PointerType *VoidPtrTy = PointerType::get(VoidTy, 0); + IntegerType *Int8Ty = IntegerType::getInt8Ty(C); + PointerType *i8PtrTy = PointerType::get(Int8Ty, 0); + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c = M.getOrInsertFunction("__cmplog_rtn_hook", VoidTy, i8PtrTy, i8PtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogHookFn = cast(c); +#else + FunctionCallee cmplogHookFn = c; +#endif + + /* iterate over all functions, bbs and instruction and add suitable calls */ + for (auto &F : M) { + + if (!isInInstrumentList(&F)) continue; + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast(&IN))) { + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + + FunctionType *FT = Callee->getFunctionType(); + + bool isPtrRtn = FT->getNumParams() >= 2 && + !FT->getReturnType()->isVoidTy() && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0)->isPointerTy(); + + if (!isPtrRtn) continue; + + calls.push_back(callInst); + + } + + } + + } + + } + + if (!calls.size()) return false; + if (!be_quiet) + errs() << "Hooking " << calls.size() + << " calls with pointers as arguments\n"; + + for (auto &callInst : calls) { + + Value *v1P = callInst->getArgOperand(0), *v2P = callInst->getArgOperand(1); + + IRBuilder<> IRB(callInst->getParent()); + IRB.SetInsertPoint(callInst); + + std::vector args; + Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); + Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); + args.push_back(v1Pcasted); + args.push_back(v2Pcasted); + + IRB.CreateCall(cmplogHookFn, args); + + // errs() << callInst->getCalledFunction()->getName() << "\n"; + + } + + return true; + +} + +bool CmpLogRoutines::runOnModule(Module &M) { + + if (getenv("AFL_QUIET") == NULL) + llvm::errs() + << "Running cmplog-routines-pass by andreafioraldi@gmail.com\n"; + else + be_quiet = 1; + hookRtns(M); + verifyModule(M); + + return true; + +} + +static void registerCmpLogRoutinesPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + auto p = new CmpLogRoutines(); + PM.add(p); + +} + +static RegisterStandardPasses RegisterCmpLogRoutinesPass( + PassManagerBuilder::EP_OptimizerLast, registerCmpLogRoutinesPass); + +static RegisterStandardPasses RegisterCmpLogRoutinesPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogRoutinesPass); + +#if LLVM_VERSION_MAJOR >= 11 +static RegisterStandardPasses RegisterCmpLogRoutinesPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerCmpLogRoutinesPass); +#endif + diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc new file mode 100644 index 00000000..acdd0f3b --- /dev/null +++ b/instrumentation/compare-transform-pass.so.cc @@ -0,0 +1,587 @@ +/* + * Copyright 2016 laf-intel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include "llvm/Config/llvm-config.h" + +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Pass.h" +#include "llvm/Analysis/ValueTracking.h" + +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) + #include "llvm/IR/Verifier.h" + #include "llvm/IR/DebugInfo.h" +#else + #include "llvm/Analysis/Verifier.h" + #include "llvm/DebugInfo.h" + #define nullptr 0 +#endif + +#include +#include "afl-llvm-common.h" + +using namespace llvm; + +namespace { + +class CompareTransform : public ModulePass { + + public: + static char ID; + CompareTransform() : ModulePass(ID) { + + initInstrumentList(); + + } + + bool runOnModule(Module &M) override; + +#if LLVM_VERSION_MAJOR < 4 + const char *getPassName() const override { + +#else + StringRef getPassName() const override { + +#endif + return "transforms compare functions"; + + } + + private: + bool transformCmps(Module &M, const bool processStrcmp, + const bool processMemcmp, const bool processStrncmp, + const bool processStrcasecmp, + const bool processStrncasecmp); + +}; + +} // namespace + +char CompareTransform::ID = 0; + +bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, + const bool processMemcmp, + const bool processStrncmp, + const bool processStrcasecmp, + const bool processStrncasecmp) { + + DenseMap valueMap; + std::vector calls; + LLVMContext & C = M.getContext(); + IntegerType * Int8Ty = IntegerType::getInt8Ty(C); + IntegerType * Int32Ty = IntegerType::getInt32Ty(C); + IntegerType * Int64Ty = IntegerType::getInt64Ty(C); + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c = M.getOrInsertFunction("tolower", Int32Ty, Int32Ty +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *tolowerFn = cast(c); +#else + FunctionCallee tolowerFn = c; +#endif + + /* iterate over all functions, bbs and instruction and add suitable calls to + * strcmp/memcmp/strncmp/strcasecmp/strncasecmp */ + for (auto &F : M) { + + if (!isInInstrumentList(&F)) continue; + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast(&IN))) { + + bool isStrcmp = processStrcmp; + bool isMemcmp = processMemcmp; + bool isStrncmp = processStrncmp; + bool isStrcasecmp = processStrcasecmp; + bool isStrncasecmp = processStrncasecmp; + bool isIntMemcpy = true; + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + StringRef FuncName = Callee->getName(); + isStrcmp &= !FuncName.compare(StringRef("strcmp")); + isMemcmp &= !FuncName.compare(StringRef("memcmp")); + isStrncmp &= !FuncName.compare(StringRef("strncmp")); + isStrcasecmp &= !FuncName.compare(StringRef("strcasecmp")); + isStrncasecmp &= !FuncName.compare(StringRef("strncasecmp")); + isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); + + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && + !isStrncasecmp && !isIntMemcpy) + continue; + + /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function + * prototype */ + FunctionType *FT = Callee->getFunctionType(); + + isStrcmp &= + FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); + isStrcasecmp &= + FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); + isMemcmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy() && + FT->getParamType(2)->isIntegerTy(); + isStrncmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()) && + FT->getParamType(2)->isIntegerTy(); + isStrncasecmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()) && + FT->getParamType(2)->isIntegerTy(); + + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && + !isStrncasecmp && !isIntMemcpy) + continue; + + /* is a str{n,}{case,}cmp/memcmp, check if we have + * str{case,}cmp(x, "const") or str{case,}cmp("const", x) + * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, ..) + * memcmp(x, "const", ..) or memcmp("const", x, ..) */ + Value *Str1P = callInst->getArgOperand(0), + *Str2P = callInst->getArgOperand(1); + StringRef Str1, Str2; + bool HasStr1 = getConstantStringInfo(Str1P, Str1); + bool HasStr2 = getConstantStringInfo(Str2P, Str2); + + if (isIntMemcpy && HasStr2) { + + valueMap[Str1P] = new std::string(Str2.str()); + // fprintf(stderr, "saved %s for %p\n", Str2.str().c_str(), Str1P); + continue; + + } + + // not literal? maybe global or local variable + if (!(HasStr1 || HasStr2)) { + + auto *Ptr = dyn_cast(Str2P); + if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { + + if (auto *Var = dyn_cast(Ptr->getOperand(0))) { + + if (Var->hasInitializer()) { + + if (auto *Array = + dyn_cast(Var->getInitializer())) { + + HasStr2 = true; + Str2 = Array->getAsString(); + valueMap[Str2P] = new std::string(Str2.str()); + fprintf(stderr, "glo2 %s\n", Str2.str().c_str()); + + } + + } + + } + + } + + if (!HasStr2) { + + auto *Ptr = dyn_cast(Str1P); + if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { + + if (auto *Var = dyn_cast(Ptr->getOperand(0))) { + + if (Var->hasInitializer()) { + + if (auto *Array = dyn_cast( + Var->getInitializer())) { + + HasStr1 = true; + Str1 = Array->getAsString(); + valueMap[Str1P] = new std::string(Str1.str()); + // fprintf(stderr, "glo1 %s\n", Str1.str().c_str()); + + } + + } + + } + + } + + } else if (isIntMemcpy) { + + valueMap[Str1P] = new std::string(Str2.str()); + // fprintf(stderr, "saved\n"); + + } + + } + + if (isIntMemcpy) continue; + + if (!(HasStr1 || HasStr2)) { + + // do we have a saved local variable initialization? + std::string *val = valueMap[Str1P]; + if (val && !val->empty()) { + + Str1 = StringRef(*val); + HasStr1 = true; + // fprintf(stderr, "loaded1 %s\n", Str1.str().c_str()); + + } else { + + val = valueMap[Str2P]; + if (val && !val->empty()) { + + Str2 = StringRef(*val); + HasStr2 = true; + // fprintf(stderr, "loaded2 %s\n", Str2.str().c_str()); + + } + + } + + } + + /* handle cases of one string is const, one string is variable */ + if (!(HasStr1 || HasStr2)) continue; + + if (isMemcmp || isStrncmp || isStrncasecmp) { + + /* check if third operand is a constant integer + * strlen("constStr") and sizeof() are treated as constant */ + Value * op2 = callInst->getArgOperand(2); + ConstantInt *ilen = dyn_cast(op2); + if (ilen) { + + uint64_t len = ilen->getZExtValue(); + // if len is zero this is a pointless call but allow real + // implementation to worry about that + if (!len) continue; + + if (isMemcmp) { + + // if size of compare is larger than constant string this is + // likely a bug but allow real implementation to worry about + // that + uint64_t literalLength = HasStr1 ? Str1.size() : Str2.size(); + if (literalLength + 1 < ilen->getZExtValue()) continue; + + } + + } else if (isMemcmp) + + // this *may* supply a len greater than the constant string at + // runtime so similarly we don't want to have to handle that + continue; + + } + + calls.push_back(callInst); + + } + + } + + } + + } + + if (!calls.size()) return false; + if (!be_quiet) + errs() << "Replacing " << calls.size() + << " calls to strcmp/memcmp/strncmp/strcasecmp/strncasecmp\n"; + + for (auto &callInst : calls) { + + Value *Str1P = callInst->getArgOperand(0), + *Str2P = callInst->getArgOperand(1); + StringRef Str1, Str2, ConstStr; + std::string TmpConstStr; + Value * VarStr; + bool HasStr1 = getConstantStringInfo(Str1P, Str1); + bool HasStr2 = getConstantStringInfo(Str2P, Str2); + uint64_t constStrLen, unrollLen, constSizedLen = 0; + bool isMemcmp = + !callInst->getCalledFunction()->getName().compare(StringRef("memcmp")); + bool isSizedcmp = isMemcmp || + !callInst->getCalledFunction()->getName().compare( + StringRef("strncmp")) || + !callInst->getCalledFunction()->getName().compare( + StringRef("strncasecmp")); + Value *sizedValue = isSizedcmp ? callInst->getArgOperand(2) : NULL; + bool isConstSized = sizedValue && isa(sizedValue); + bool isCaseInsensitive = !callInst->getCalledFunction()->getName().compare( + StringRef("strcasecmp")) || + !callInst->getCalledFunction()->getName().compare( + StringRef("strncasecmp")); + + if (!(HasStr1 || HasStr2)) { + + // do we have a saved local or global variable initialization? + std::string *val = valueMap[Str1P]; + if (val && !val->empty()) { + + Str1 = StringRef(*val); + HasStr1 = true; + + } else { + + val = valueMap[Str2P]; + if (val && !val->empty()) { + + Str2 = StringRef(*val); + HasStr2 = true; + + } + + } + + } + + if (isConstSized) { + + constSizedLen = dyn_cast(sizedValue)->getZExtValue(); + + } + + if (HasStr1) { + + TmpConstStr = Str1.str(); + VarStr = Str2P; + + } else { + + TmpConstStr = Str2.str(); + VarStr = Str1P; + + } + + // add null termination character implicit in c strings + TmpConstStr.append("\0", 1); + + // in the unusual case the const str has embedded null + // characters, the string comparison functions should terminate + // at the first null + if (!isMemcmp) + TmpConstStr.assign(TmpConstStr, 0, TmpConstStr.find('\0') + 1); + + constStrLen = TmpConstStr.length(); + // prefer use of StringRef (in comparison to std::string a StringRef has + // built-in runtime bounds checking, which makes debugging easier) + ConstStr = StringRef(TmpConstStr); + + if (isConstSized) + unrollLen = constSizedLen < constStrLen ? constSizedLen : constStrLen; + else + unrollLen = constStrLen; + + if (!be_quiet) + errs() << callInst->getCalledFunction()->getName() << ": unroll len " + << unrollLen + << ((isSizedcmp && !isConstSized) ? ", variable n" : "") << ": " + << ConstStr << "\n"; + + /* split before the call instruction */ + BasicBlock *bb = callInst->getParent(); + BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(callInst)); + + BasicBlock *next_lenchk_bb = NULL; + if (isSizedcmp && !isConstSized) { + + next_lenchk_bb = + BasicBlock::Create(C, "len_check", end_bb->getParent(), end_bb); + BranchInst::Create(end_bb, next_lenchk_bb); + + } + + BasicBlock *next_cmp_bb = + BasicBlock::Create(C, "cmp_added", end_bb->getParent(), end_bb); + BranchInst::Create(end_bb, next_cmp_bb); + PHINode *PN = PHINode::Create( + Int32Ty, (next_lenchk_bb ? 2 : 1) * unrollLen + 1, "cmp_phi"); + +#if LLVM_VERSION_MAJOR < 8 + TerminatorInst *term = bb->getTerminator(); +#else + Instruction *term = bb->getTerminator(); +#endif + BranchInst::Create(next_lenchk_bb ? next_lenchk_bb : next_cmp_bb, bb); + term->eraseFromParent(); + + for (uint64_t i = 0; i < unrollLen; i++) { + + BasicBlock * cur_cmp_bb = next_cmp_bb, *cur_lenchk_bb = next_lenchk_bb; + unsigned char c; + + if (cur_lenchk_bb) { + + IRBuilder<> cur_lenchk_IRB(&*(cur_lenchk_bb->getFirstInsertionPt())); + Value * icmp = cur_lenchk_IRB.CreateICmpEQ( + sizedValue, ConstantInt::get(sizedValue->getType(), i)); + cur_lenchk_IRB.CreateCondBr(icmp, end_bb, cur_cmp_bb); + cur_lenchk_bb->getTerminator()->eraseFromParent(); + + PN->addIncoming(ConstantInt::get(Int32Ty, 0), cur_lenchk_bb); + + } + + if (isCaseInsensitive) + c = (unsigned char)(tolower((int)ConstStr[i]) & 0xff); + else + c = (unsigned char)ConstStr[i]; + + IRBuilder<> cur_cmp_IRB(&*(cur_cmp_bb->getFirstInsertionPt())); + + Value *v = ConstantInt::get(Int64Ty, i); + Value *ele = cur_cmp_IRB.CreateInBoundsGEP(VarStr, v, "empty"); + Value *load = cur_cmp_IRB.CreateLoad(ele); + + if (isCaseInsensitive) { + + // load >= 'A' && load <= 'Z' ? load | 0x020 : load + load = cur_cmp_IRB.CreateZExt(load, Int32Ty); + std::vector args; + args.push_back(load); + load = cur_cmp_IRB.CreateCall(tolowerFn, args); + load = cur_cmp_IRB.CreateTrunc(load, Int8Ty); + + } + + Value *isub; + if (HasStr1) + isub = cur_cmp_IRB.CreateSub(ConstantInt::get(Int8Ty, c), load); + else + isub = cur_cmp_IRB.CreateSub(load, ConstantInt::get(Int8Ty, c)); + + Value *sext = cur_cmp_IRB.CreateSExt(isub, Int32Ty); + PN->addIncoming(sext, cur_cmp_bb); + + if (i < unrollLen - 1) { + + if (cur_lenchk_bb) { + + next_lenchk_bb = + BasicBlock::Create(C, "len_check", end_bb->getParent(), end_bb); + BranchInst::Create(end_bb, next_lenchk_bb); + + } + + next_cmp_bb = + BasicBlock::Create(C, "cmp_added", end_bb->getParent(), end_bb); + BranchInst::Create(end_bb, next_cmp_bb); + + Value *icmp = + cur_cmp_IRB.CreateICmpEQ(isub, ConstantInt::get(Int8Ty, 0)); + cur_cmp_IRB.CreateCondBr( + icmp, next_lenchk_bb ? next_lenchk_bb : next_cmp_bb, end_bb); + cur_cmp_bb->getTerminator()->eraseFromParent(); + + } else { + + // IRB.CreateBr(end_bb); + + } + + // add offset to varstr + // create load + // create signed isub + // create icmp + // create jcc + // create next_bb + + } + + /* since the call is the first instruction of the bb it is safe to + * replace it with a phi instruction */ + BasicBlock::iterator ii(callInst); + ReplaceInstWithInst(callInst->getParent()->getInstList(), ii, PN); + + } + + return true; + +} + +bool CompareTransform::runOnModule(Module &M) { + + if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) + llvm::errs() << "Running compare-transform-pass by laf.intel@gmail.com, " + "extended by heiko@hexco.de\n"; + else + be_quiet = 1; + transformCmps(M, true, true, true, true, true); + verifyModule(M); + + return true; + +} + +static void registerCompTransPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + auto p = new CompareTransform(); + PM.add(p); + +} + +static RegisterStandardPasses RegisterCompTransPass( + PassManagerBuilder::EP_OptimizerLast, registerCompTransPass); + +static RegisterStandardPasses RegisterCompTransPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerCompTransPass); + +#if LLVM_VERSION_MAJOR >= 11 +static RegisterStandardPasses RegisterCompTransPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerCompTransPass); +#endif + diff --git a/instrumentation/llvm-ngram-coverage.h b/instrumentation/llvm-ngram-coverage.h new file mode 100644 index 00000000..12b666e9 --- /dev/null +++ b/instrumentation/llvm-ngram-coverage.h @@ -0,0 +1,18 @@ +#ifndef AFL_NGRAM_CONFIG_H +#define AFL_NGRAM_CONFIG_H + +#include "../config.h" + +#if (MAP_SIZE_POW2 <= 16) +typedef u16 PREV_LOC_T; +#elif (MAP_SIZE_POW2 <= 32) +typedef u32 PREV_LOC_T; +#else +typedef u64 PREV_LOC_T; +#endif + +/* Maximum ngram size */ +#define NGRAM_SIZE_MAX 16U + +#endif + diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc new file mode 100644 index 00000000..2fb90e5e --- /dev/null +++ b/instrumentation/split-compares-pass.so.cc @@ -0,0 +1,1356 @@ +/* + * Copyright 2016 laf-intel + * extended for floating point by Heiko Eißfeldt + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "llvm/Config/llvm-config.h" + +#include "llvm/Pass.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/IR/Module.h" + +#include "llvm/IR/IRBuilder.h" +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) + #include "llvm/IR/Verifier.h" + #include "llvm/IR/DebugInfo.h" +#else + #include "llvm/Analysis/Verifier.h" + #include "llvm/DebugInfo.h" + #define nullptr 0 +#endif + +using namespace llvm; +#include "afl-llvm-common.h" + +namespace { + +class SplitComparesTransform : public ModulePass { + + public: + static char ID; + SplitComparesTransform() : ModulePass(ID) { + + initInstrumentList(); + + } + + bool runOnModule(Module &M) override; +#if LLVM_VERSION_MAJOR >= 4 + StringRef getPassName() const override { + +#else + const char *getPassName() const override { + +#endif + return "simplifies and splits ICMP instructions"; + + } + + private: + int enableFPSplit; + + size_t splitIntCompares(Module &M, unsigned bitw); + size_t splitFPCompares(Module &M); + bool simplifyCompares(Module &M); + bool simplifyFPCompares(Module &M); + bool simplifyIntSignedness(Module &M); + size_t nextPowerOfTwo(size_t in); + +}; + +} // namespace + +char SplitComparesTransform::ID = 0; + +/* This function splits FCMP instructions with xGE or xLE predicates into two + * FCMP instructions with predicate xGT or xLT and EQ */ +bool SplitComparesTransform::simplifyFPCompares(Module &M) { + + LLVMContext & C = M.getContext(); + std::vector fcomps; + IntegerType * Int1Ty = IntegerType::getInt1Ty(C); + + /* iterate over all functions, bbs and instruction and add + * all integer comparisons with >= and <= predicates to the icomps vector */ + for (auto &F : M) { + + if (!isInInstrumentList(&F)) continue; + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CmpInst *selectcmpInst = nullptr; + + if ((selectcmpInst = dyn_cast(&IN))) { + + if (enableFPSplit && + (selectcmpInst->getPredicate() == CmpInst::FCMP_OGE || + selectcmpInst->getPredicate() == CmpInst::FCMP_UGE || + selectcmpInst->getPredicate() == CmpInst::FCMP_OLE || + selectcmpInst->getPredicate() == CmpInst::FCMP_ULE)) { + + auto op0 = selectcmpInst->getOperand(0); + auto op1 = selectcmpInst->getOperand(1); + + Type *TyOp0 = op0->getType(); + Type *TyOp1 = op1->getType(); + + /* this is probably not needed but we do it anyway */ + if (TyOp0 != TyOp1) { continue; } + + if (TyOp0->isArrayTy() || TyOp0->isVectorTy()) { continue; } + + fcomps.push_back(selectcmpInst); + + } + + } + + } + + } + + } + + if (!fcomps.size()) { return false; } + + /* transform for floating point */ + for (auto &FcmpInst : fcomps) { + + BasicBlock *bb = FcmpInst->getParent(); + + auto op0 = FcmpInst->getOperand(0); + auto op1 = FcmpInst->getOperand(1); + + /* find out what the new predicate is going to be */ + auto pred = dyn_cast(FcmpInst)->getPredicate(); + CmpInst::Predicate new_pred; + switch (pred) { + + case CmpInst::FCMP_UGE: + new_pred = CmpInst::FCMP_UGT; + break; + case CmpInst::FCMP_OGE: + new_pred = CmpInst::FCMP_OGT; + break; + case CmpInst::FCMP_ULE: + new_pred = CmpInst::FCMP_ULT; + break; + case CmpInst::FCMP_OLE: + new_pred = CmpInst::FCMP_OLT; + break; + default: // keep the compiler happy + continue; + + } + + /* split before the fcmp instruction */ + BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(FcmpInst)); + + /* the old bb now contains a unconditional jump to the new one (end_bb) + * we need to delete it later */ + + /* create the FCMP instruction with new_pred and add it to the old basic + * block bb it is now at the position where the old FcmpInst was */ + Instruction *fcmp_np; + fcmp_np = CmpInst::Create(Instruction::FCmp, new_pred, op0, op1); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), + fcmp_np); + + /* create a new basic block which holds the new EQ fcmp */ + Instruction *fcmp_eq; + /* insert middle_bb before end_bb */ + BasicBlock *middle_bb = + BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); + fcmp_eq = CmpInst::Create(Instruction::FCmp, CmpInst::FCMP_OEQ, op0, op1); + middle_bb->getInstList().push_back(fcmp_eq); + /* add an unconditional branch to the end of middle_bb with destination + * end_bb */ + BranchInst::Create(end_bb, middle_bb); + + /* replace the uncond branch with a conditional one, which depends on the + * new_pred fcmp. True goes to end, false to the middle (injected) bb */ + auto term = bb->getTerminator(); + BranchInst::Create(end_bb, middle_bb, fcmp_np, bb); + term->eraseFromParent(); + + /* replace the old FcmpInst (which is the first inst in end_bb) with a PHI + * inst to wire up the loose ends */ + PHINode *PN = PHINode::Create(Int1Ty, 2, ""); + /* the first result depends on the outcome of fcmp_eq */ + PN->addIncoming(fcmp_eq, middle_bb); + /* if the source was the original bb we know that the fcmp_np yielded true + * hence we can hardcode this value */ + PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); + /* replace the old FcmpInst with our new and shiny PHI inst */ + BasicBlock::iterator ii(FcmpInst); + ReplaceInstWithInst(FcmpInst->getParent()->getInstList(), ii, PN); + + } + + return true; + +} + +/* This function splits ICMP instructions with xGE or xLE predicates into two + * ICMP instructions with predicate xGT or xLT and EQ */ +bool SplitComparesTransform::simplifyCompares(Module &M) { + + LLVMContext & C = M.getContext(); + std::vector icomps; + IntegerType * Int1Ty = IntegerType::getInt1Ty(C); + + /* iterate over all functions, bbs and instruction and add + * all integer comparisons with >= and <= predicates to the icomps vector */ + for (auto &F : M) { + + if (!isInInstrumentList(&F)) continue; + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CmpInst *selectcmpInst = nullptr; + + if ((selectcmpInst = dyn_cast(&IN))) { + + if (selectcmpInst->getPredicate() == CmpInst::ICMP_UGE || + selectcmpInst->getPredicate() == CmpInst::ICMP_SGE || + selectcmpInst->getPredicate() == CmpInst::ICMP_ULE || + selectcmpInst->getPredicate() == CmpInst::ICMP_SLE) { + + auto op0 = selectcmpInst->getOperand(0); + auto op1 = selectcmpInst->getOperand(1); + + IntegerType *intTyOp0 = dyn_cast(op0->getType()); + IntegerType *intTyOp1 = dyn_cast(op1->getType()); + + /* this is probably not needed but we do it anyway */ + if (!intTyOp0 || !intTyOp1) { continue; } + + icomps.push_back(selectcmpInst); + + } + + } + + } + + } + + } + + if (!icomps.size()) { return false; } + + for (auto &IcmpInst : icomps) { + + BasicBlock *bb = IcmpInst->getParent(); + + auto op0 = IcmpInst->getOperand(0); + auto op1 = IcmpInst->getOperand(1); + + /* find out what the new predicate is going to be */ + auto pred = dyn_cast(IcmpInst)->getPredicate(); + CmpInst::Predicate new_pred; + switch (pred) { + + case CmpInst::ICMP_UGE: + new_pred = CmpInst::ICMP_UGT; + break; + case CmpInst::ICMP_SGE: + new_pred = CmpInst::ICMP_SGT; + break; + case CmpInst::ICMP_ULE: + new_pred = CmpInst::ICMP_ULT; + break; + case CmpInst::ICMP_SLE: + new_pred = CmpInst::ICMP_SLT; + break; + default: // keep the compiler happy + continue; + + } + + /* split before the icmp instruction */ + BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); + + /* the old bb now contains a unconditional jump to the new one (end_bb) + * we need to delete it later */ + + /* create the ICMP instruction with new_pred and add it to the old basic + * block bb it is now at the position where the old IcmpInst was */ + Instruction *icmp_np; + icmp_np = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), + icmp_np); + + /* create a new basic block which holds the new EQ icmp */ + Instruction *icmp_eq; + /* insert middle_bb before end_bb */ + BasicBlock *middle_bb = + BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); + icmp_eq = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, op0, op1); + middle_bb->getInstList().push_back(icmp_eq); + /* add an unconditional branch to the end of middle_bb with destination + * end_bb */ + BranchInst::Create(end_bb, middle_bb); + + /* replace the uncond branch with a conditional one, which depends on the + * new_pred icmp. True goes to end, false to the middle (injected) bb */ + auto term = bb->getTerminator(); + BranchInst::Create(end_bb, middle_bb, icmp_np, bb); + term->eraseFromParent(); + + /* replace the old IcmpInst (which is the first inst in end_bb) with a PHI + * inst to wire up the loose ends */ + PHINode *PN = PHINode::Create(Int1Ty, 2, ""); + /* the first result depends on the outcome of icmp_eq */ + PN->addIncoming(icmp_eq, middle_bb); + /* if the source was the original bb we know that the icmp_np yielded true + * hence we can hardcode this value */ + PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); + /* replace the old IcmpInst with our new and shiny PHI inst */ + BasicBlock::iterator ii(IcmpInst); + ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); + + } + + return true; + +} + +/* this function transforms signed compares to equivalent unsigned compares */ +bool SplitComparesTransform::simplifyIntSignedness(Module &M) { + + LLVMContext & C = M.getContext(); + std::vector icomps; + IntegerType * Int1Ty = IntegerType::getInt1Ty(C); + + /* iterate over all functions, bbs and instructions and add + * all signed compares to icomps vector */ + for (auto &F : M) { + + if (!isInInstrumentList(&F)) continue; + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CmpInst *selectcmpInst = nullptr; + + if ((selectcmpInst = dyn_cast(&IN))) { + + if (selectcmpInst->getPredicate() == CmpInst::ICMP_SGT || + selectcmpInst->getPredicate() == CmpInst::ICMP_SLT) { + + auto op0 = selectcmpInst->getOperand(0); + auto op1 = selectcmpInst->getOperand(1); + + IntegerType *intTyOp0 = dyn_cast(op0->getType()); + IntegerType *intTyOp1 = dyn_cast(op1->getType()); + + /* see above */ + if (!intTyOp0 || !intTyOp1) { continue; } + + /* i think this is not possible but to lazy to look it up */ + if (intTyOp0->getBitWidth() != intTyOp1->getBitWidth()) { + + continue; + + } + + icomps.push_back(selectcmpInst); + + } + + } + + } + + } + + } + + if (!icomps.size()) { return false; } + + for (auto &IcmpInst : icomps) { + + BasicBlock *bb = IcmpInst->getParent(); + + auto op0 = IcmpInst->getOperand(0); + auto op1 = IcmpInst->getOperand(1); + + IntegerType *intTyOp0 = dyn_cast(op0->getType()); + unsigned bitw = intTyOp0->getBitWidth(); + IntegerType *IntType = IntegerType::get(C, bitw); + + /* get the new predicate */ + auto pred = dyn_cast(IcmpInst)->getPredicate(); + CmpInst::Predicate new_pred; + if (pred == CmpInst::ICMP_SGT) { + + new_pred = CmpInst::ICMP_UGT; + + } else { + + new_pred = CmpInst::ICMP_ULT; + + } + + BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); + + /* create a 1 bit compare for the sign bit. to do this shift and trunc + * the original operands so only the first bit remains.*/ + Instruction *s_op0, *t_op0, *s_op1, *t_op1, *icmp_sign_bit; + + s_op0 = BinaryOperator::Create(Instruction::LShr, op0, + ConstantInt::get(IntType, bitw - 1)); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op0); + t_op0 = new TruncInst(s_op0, Int1Ty); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_op0); + + s_op1 = BinaryOperator::Create(Instruction::LShr, op1, + ConstantInt::get(IntType, bitw - 1)); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op1); + t_op1 = new TruncInst(s_op1, Int1Ty); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_op1); + + /* compare of the sign bits */ + icmp_sign_bit = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_op0, t_op1); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), + icmp_sign_bit); + + /* create a new basic block which is executed if the signedness bit is + * different */ + Instruction *icmp_inv_sig_cmp; + BasicBlock * sign_bb = + BasicBlock::Create(C, "sign", end_bb->getParent(), end_bb); + if (pred == CmpInst::ICMP_SGT) { + + /* if we check for > and the op0 positive and op1 negative then the final + * result is true. if op0 negative and op1 pos, the cmp must result + * in false + */ + icmp_inv_sig_cmp = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_op0, t_op1); + + } else { + + /* just the inverse of the above statement */ + icmp_inv_sig_cmp = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_op0, t_op1); + + } + + sign_bb->getInstList().push_back(icmp_inv_sig_cmp); + BranchInst::Create(end_bb, sign_bb); + + /* create a new bb which is executed if signedness is equal */ + Instruction *icmp_usign_cmp; + BasicBlock * middle_bb = + BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); + /* we can do a normal unsigned compare now */ + icmp_usign_cmp = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); + middle_bb->getInstList().push_back(icmp_usign_cmp); + BranchInst::Create(end_bb, middle_bb); + + auto term = bb->getTerminator(); + /* if the sign is eq do a normal unsigned cmp, else we have to check the + * signedness bit */ + BranchInst::Create(middle_bb, sign_bb, icmp_sign_bit, bb); + term->eraseFromParent(); + + PHINode *PN = PHINode::Create(Int1Ty, 2, ""); + + PN->addIncoming(icmp_usign_cmp, middle_bb); + PN->addIncoming(icmp_inv_sig_cmp, sign_bb); + + BasicBlock::iterator ii(IcmpInst); + ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); + + } + + return true; + +} + +size_t SplitComparesTransform::nextPowerOfTwo(size_t in) { + + --in; + in |= in >> 1; + in |= in >> 2; + in |= in >> 4; + // in |= in >> 8; + // in |= in >> 16; + return in + 1; + +} + +/* splits fcmps into two nested fcmps with sign compare and the rest */ +size_t SplitComparesTransform::splitFPCompares(Module &M) { + + size_t count = 0; + + LLVMContext &C = M.getContext(); + +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) + const DataLayout &dl = M.getDataLayout(); + + /* define unions with floating point and (sign, exponent, mantissa) triples + */ + if (dl.isLittleEndian()) { + + } else if (dl.isBigEndian()) { + + } else { + + return count; + + } + +#endif + + std::vector fcomps; + + /* get all EQ, NE, GT, and LT fcmps. if the other two + * functions were executed only these four predicates should exist */ + for (auto &F : M) { + + if (!isInInstrumentList(&F)) continue; + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CmpInst *selectcmpInst = nullptr; + + if ((selectcmpInst = dyn_cast(&IN))) { + + if (selectcmpInst->getPredicate() == CmpInst::FCMP_OEQ || + selectcmpInst->getPredicate() == CmpInst::FCMP_ONE || + selectcmpInst->getPredicate() == CmpInst::FCMP_UNE || + selectcmpInst->getPredicate() == CmpInst::FCMP_UGT || + selectcmpInst->getPredicate() == CmpInst::FCMP_OGT || + selectcmpInst->getPredicate() == CmpInst::FCMP_ULT || + selectcmpInst->getPredicate() == CmpInst::FCMP_OLT) { + + auto op0 = selectcmpInst->getOperand(0); + auto op1 = selectcmpInst->getOperand(1); + + Type *TyOp0 = op0->getType(); + Type *TyOp1 = op1->getType(); + + if (TyOp0 != TyOp1) { continue; } + + if (TyOp0->isArrayTy() || TyOp0->isVectorTy()) { continue; } + + fcomps.push_back(selectcmpInst); + + } + + } + + } + + } + + } + + if (!fcomps.size()) { return count; } + + IntegerType *Int1Ty = IntegerType::getInt1Ty(C); + + for (auto &FcmpInst : fcomps) { + + BasicBlock *bb = FcmpInst->getParent(); + + auto op0 = FcmpInst->getOperand(0); + auto op1 = FcmpInst->getOperand(1); + + unsigned op_size; + op_size = op0->getType()->getPrimitiveSizeInBits(); + + if (op_size != op1->getType()->getPrimitiveSizeInBits()) { continue; } + + const unsigned int sizeInBits = op0->getType()->getPrimitiveSizeInBits(); + const unsigned int precision = + sizeInBits == 32 + ? 24 + : sizeInBits == 64 + ? 53 + : sizeInBits == 128 ? 113 + : sizeInBits == 16 ? 11 + /* sizeInBits == 80 */ + : 65; + + const unsigned shiftR_exponent = precision - 1; + const unsigned long long mask_fraction = + (1ULL << (shiftR_exponent - 1)) | ((1ULL << (shiftR_exponent - 1)) - 1); + const unsigned long long mask_exponent = + (1ULL << (sizeInBits - precision)) - 1; + + // round up sizes to the next power of two + // this should help with integer compare splitting + size_t exTySizeBytes = ((sizeInBits - precision + 7) >> 3); + size_t frTySizeBytes = ((precision - 1ULL + 7) >> 3); + + IntegerType *IntExponentTy = + IntegerType::get(C, nextPowerOfTwo(exTySizeBytes) << 3); + IntegerType *IntFractionTy = + IntegerType::get(C, nextPowerOfTwo(frTySizeBytes) << 3); + + // errs() << "Fractions: IntFractionTy size " << + // IntFractionTy->getPrimitiveSizeInBits() << ", op_size " << op_size << + // ", mask " << mask_fraction << + // ", precision " << precision << "\n"; + + BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(FcmpInst)); + + /* create the integers from floats directly */ + Instruction *b_op0, *b_op1; + b_op0 = CastInst::Create(Instruction::BitCast, op0, + IntegerType::get(C, op_size)); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), b_op0); + + b_op1 = CastInst::Create(Instruction::BitCast, op1, + IntegerType::get(C, op_size)); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), b_op1); + + /* isolate signs of value of floating point type */ + + /* create a 1 bit compare for the sign bit. to do this shift and trunc + * the original operands so only the first bit remains.*/ + Instruction *s_s0, *t_s0, *s_s1, *t_s1, *icmp_sign_bit; + + s_s0 = + BinaryOperator::Create(Instruction::LShr, b_op0, + ConstantInt::get(b_op0->getType(), op_size - 1)); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_s0); + t_s0 = new TruncInst(s_s0, Int1Ty); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_s0); + + s_s1 = + BinaryOperator::Create(Instruction::LShr, b_op1, + ConstantInt::get(b_op1->getType(), op_size - 1)); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_s1); + t_s1 = new TruncInst(s_s1, Int1Ty); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_s1); + + /* compare of the sign bits */ + icmp_sign_bit = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_s0, t_s1); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), + icmp_sign_bit); + + /* create a new basic block which is executed if the signedness bits are + * equal */ + BasicBlock *signequal_bb = + BasicBlock::Create(C, "signequal", end_bb->getParent(), end_bb); + + BranchInst::Create(end_bb, signequal_bb); + + /* create a new bb which is executed if exponents are satisfying the compare + */ + BasicBlock *middle_bb = + BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); + + BranchInst::Create(end_bb, middle_bb); + + auto term = bb->getTerminator(); + /* if the signs are different goto end_bb else to signequal_bb */ + BranchInst::Create(signequal_bb, end_bb, icmp_sign_bit, bb); + term->eraseFromParent(); + + /* insert code for equal signs */ + + /* isolate the exponents */ + Instruction *s_e0, *m_e0, *t_e0, *s_e1, *m_e1, *t_e1; + + s_e0 = BinaryOperator::Create( + Instruction::LShr, b_op0, + ConstantInt::get(b_op0->getType(), shiftR_exponent)); + s_e1 = BinaryOperator::Create( + Instruction::LShr, b_op1, + ConstantInt::get(b_op1->getType(), shiftR_exponent)); + signequal_bb->getInstList().insert( + BasicBlock::iterator(signequal_bb->getTerminator()), s_e0); + signequal_bb->getInstList().insert( + BasicBlock::iterator(signequal_bb->getTerminator()), s_e1); + + t_e0 = new TruncInst(s_e0, IntExponentTy); + t_e1 = new TruncInst(s_e1, IntExponentTy); + signequal_bb->getInstList().insert( + BasicBlock::iterator(signequal_bb->getTerminator()), t_e0); + signequal_bb->getInstList().insert( + BasicBlock::iterator(signequal_bb->getTerminator()), t_e1); + + if (sizeInBits - precision < exTySizeBytes * 8) { + + m_e0 = BinaryOperator::Create( + Instruction::And, t_e0, + ConstantInt::get(t_e0->getType(), mask_exponent)); + m_e1 = BinaryOperator::Create( + Instruction::And, t_e1, + ConstantInt::get(t_e1->getType(), mask_exponent)); + signequal_bb->getInstList().insert( + BasicBlock::iterator(signequal_bb->getTerminator()), m_e0); + signequal_bb->getInstList().insert( + BasicBlock::iterator(signequal_bb->getTerminator()), m_e1); + + } else { + + m_e0 = t_e0; + m_e1 = t_e1; + + } + + /* compare the exponents of the operands */ + Instruction *icmp_exponents_equal; + Instruction *icmp_exponent_result; + BasicBlock * signequal2_bb = signequal_bb; + switch (FcmpInst->getPredicate()) { + + case CmpInst::FCMP_OEQ: + icmp_exponent_result = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, m_e0, m_e1); + break; + case CmpInst::FCMP_ONE: + case CmpInst::FCMP_UNE: + icmp_exponent_result = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_NE, m_e0, m_e1); + break; + /* compare the exponents of the operands (signs are equal) + * if exponents are equal -> proceed to mantissa comparison + * else get result depending on sign + */ + case CmpInst::FCMP_OGT: + case CmpInst::FCMP_UGT: + Instruction *icmp_exponent; + icmp_exponents_equal = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, m_e0, m_e1); + signequal_bb->getInstList().insert( + BasicBlock::iterator(signequal_bb->getTerminator()), + icmp_exponents_equal); + + // shortcut for unequal exponents + signequal2_bb = signequal_bb->splitBasicBlock( + BasicBlock::iterator(signequal_bb->getTerminator())); + + /* if the exponents are equal goto middle_bb else to signequal2_bb */ + term = signequal_bb->getTerminator(); + BranchInst::Create(middle_bb, signequal2_bb, icmp_exponents_equal, + signequal_bb); + term->eraseFromParent(); + + icmp_exponent = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, m_e0, m_e1); + signequal2_bb->getInstList().insert( + BasicBlock::iterator(signequal2_bb->getTerminator()), + icmp_exponent); + icmp_exponent_result = + BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0); + break; + case CmpInst::FCMP_OLT: + case CmpInst::FCMP_ULT: + icmp_exponents_equal = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, m_e0, m_e1); + signequal_bb->getInstList().insert( + BasicBlock::iterator(signequal_bb->getTerminator()), + icmp_exponents_equal); + + // shortcut for unequal exponents + signequal2_bb = signequal_bb->splitBasicBlock( + BasicBlock::iterator(signequal_bb->getTerminator())); + + /* if the exponents are equal goto middle_bb else to signequal2_bb */ + term = signequal_bb->getTerminator(); + BranchInst::Create(middle_bb, signequal2_bb, icmp_exponents_equal, + signequal_bb); + term->eraseFromParent(); + + icmp_exponent = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, m_e0, m_e1); + signequal2_bb->getInstList().insert( + BasicBlock::iterator(signequal2_bb->getTerminator()), + icmp_exponent); + icmp_exponent_result = + BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0); + break; + default: + continue; + + } + + signequal2_bb->getInstList().insert( + BasicBlock::iterator(signequal2_bb->getTerminator()), + icmp_exponent_result); + + { + + term = signequal2_bb->getTerminator(); + + switch (FcmpInst->getPredicate()) { + + case CmpInst::FCMP_OEQ: + /* if the exponents are satifying the compare do a fraction cmp in + * middle_bb */ + BranchInst::Create(middle_bb, end_bb, icmp_exponent_result, + signequal2_bb); + break; + case CmpInst::FCMP_ONE: + case CmpInst::FCMP_UNE: + /* if the exponents are satifying the compare do a fraction cmp in + * middle_bb */ + BranchInst::Create(end_bb, middle_bb, icmp_exponent_result, + signequal2_bb); + break; + case CmpInst::FCMP_OGT: + case CmpInst::FCMP_UGT: + case CmpInst::FCMP_OLT: + case CmpInst::FCMP_ULT: + BranchInst::Create(end_bb, signequal2_bb); + break; + default: + continue; + + } + + term->eraseFromParent(); + + } + + /* isolate the mantissa aka fraction */ + Instruction *t_f0, *t_f1; + bool needTrunc = IntFractionTy->getPrimitiveSizeInBits() < op_size; + + if (precision - 1 < frTySizeBytes * 8) { + + Instruction *m_f0, *m_f1; + m_f0 = BinaryOperator::Create( + Instruction::And, b_op0, + ConstantInt::get(b_op0->getType(), mask_fraction)); + m_f1 = BinaryOperator::Create( + Instruction::And, b_op1, + ConstantInt::get(b_op1->getType(), mask_fraction)); + middle_bb->getInstList().insert( + BasicBlock::iterator(middle_bb->getTerminator()), m_f0); + middle_bb->getInstList().insert( + BasicBlock::iterator(middle_bb->getTerminator()), m_f1); + + if (needTrunc) { + + t_f0 = new TruncInst(m_f0, IntFractionTy); + t_f1 = new TruncInst(m_f1, IntFractionTy); + middle_bb->getInstList().insert( + BasicBlock::iterator(middle_bb->getTerminator()), t_f0); + middle_bb->getInstList().insert( + BasicBlock::iterator(middle_bb->getTerminator()), t_f1); + + } else { + + t_f0 = m_f0; + t_f1 = m_f1; + + } + + } else { + + if (needTrunc) { + + t_f0 = new TruncInst(b_op0, IntFractionTy); + t_f1 = new TruncInst(b_op1, IntFractionTy); + middle_bb->getInstList().insert( + BasicBlock::iterator(middle_bb->getTerminator()), t_f0); + middle_bb->getInstList().insert( + BasicBlock::iterator(middle_bb->getTerminator()), t_f1); + + } else { + + t_f0 = b_op0; + t_f1 = b_op1; + + } + + } + + /* compare the fractions of the operands */ + Instruction *icmp_fraction_result; + Instruction *icmp_fraction_result2; + BasicBlock * middle2_bb = middle_bb; + PHINode * PN2 = nullptr; + switch (FcmpInst->getPredicate()) { + + case CmpInst::FCMP_OEQ: + icmp_fraction_result = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_f0, t_f1); + middle2_bb->getInstList().insert( + BasicBlock::iterator(middle2_bb->getTerminator()), + icmp_fraction_result); + + break; + case CmpInst::FCMP_UNE: + case CmpInst::FCMP_ONE: + icmp_fraction_result = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_NE, t_f0, t_f1); + middle2_bb->getInstList().insert( + BasicBlock::iterator(middle2_bb->getTerminator()), + icmp_fraction_result); + + break; + case CmpInst::FCMP_OGT: + case CmpInst::FCMP_UGT: + case CmpInst::FCMP_OLT: + case CmpInst::FCMP_ULT: { + + middle2_bb = middle_bb->splitBasicBlock( + BasicBlock::iterator(middle_bb->getTerminator())); + + BasicBlock *negative_bb = BasicBlock::Create( + C, "negative_value", middle2_bb->getParent(), middle2_bb); + BasicBlock *positive_bb = BasicBlock::Create( + C, "positive_value", negative_bb->getParent(), negative_bb); + + if (FcmpInst->getPredicate() == CmpInst::FCMP_OGT || + FcmpInst->getPredicate() == CmpInst::FCMP_UGT) { + + negative_bb->getInstList().push_back( + icmp_fraction_result = CmpInst::Create( + Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1)); + positive_bb->getInstList().push_back( + icmp_fraction_result2 = CmpInst::Create( + Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1)); + + } else { + + negative_bb->getInstList().push_back( + icmp_fraction_result = CmpInst::Create( + Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1)); + positive_bb->getInstList().push_back( + icmp_fraction_result2 = CmpInst::Create( + Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1)); + + } + + BranchInst::Create(middle2_bb, negative_bb); + BranchInst::Create(middle2_bb, positive_bb); + + term = middle_bb->getTerminator(); + BranchInst::Create(negative_bb, positive_bb, t_s0, middle_bb); + term->eraseFromParent(); + + PN2 = PHINode::Create(Int1Ty, 2, ""); + PN2->addIncoming(icmp_fraction_result, negative_bb); + PN2->addIncoming(icmp_fraction_result2, positive_bb); + middle2_bb->getInstList().insert( + BasicBlock::iterator(middle2_bb->getTerminator()), PN2); + + } break; + + default: + continue; + + } + + PHINode *PN = PHINode::Create(Int1Ty, 3, ""); + + switch (FcmpInst->getPredicate()) { + + case CmpInst::FCMP_OEQ: + /* unequal signs cannot be equal values */ + /* goto false branch */ + PN->addIncoming(ConstantInt::get(Int1Ty, 0), bb); + /* unequal exponents cannot be equal values, too */ + PN->addIncoming(ConstantInt::get(Int1Ty, 0), signequal_bb); + /* fractions comparison */ + PN->addIncoming(icmp_fraction_result, middle2_bb); + break; + case CmpInst::FCMP_ONE: + case CmpInst::FCMP_UNE: + /* unequal signs are unequal values */ + /* goto true branch */ + PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); + /* unequal exponents are unequal values, too */ + PN->addIncoming(icmp_exponent_result, signequal_bb); + /* fractions comparison */ + PN->addIncoming(icmp_fraction_result, middle2_bb); + break; + case CmpInst::FCMP_OGT: + case CmpInst::FCMP_UGT: + /* if op1 is negative goto true branch, + else go on comparing */ + PN->addIncoming(t_s1, bb); + PN->addIncoming(icmp_exponent_result, signequal2_bb); + PN->addIncoming(PN2, middle2_bb); + break; + case CmpInst::FCMP_OLT: + case CmpInst::FCMP_ULT: + /* if op0 is negative goto true branch, + else go on comparing */ + PN->addIncoming(t_s0, bb); + PN->addIncoming(icmp_exponent_result, signequal2_bb); + PN->addIncoming(PN2, middle2_bb); + break; + default: + continue; + + } + + BasicBlock::iterator ii(FcmpInst); + ReplaceInstWithInst(FcmpInst->getParent()->getInstList(), ii, PN); + ++count; + + } + + return count; + +} + +/* splits icmps of size bitw into two nested icmps with bitw/2 size each */ +size_t SplitComparesTransform::splitIntCompares(Module &M, unsigned bitw) { + + size_t count = 0; + + LLVMContext &C = M.getContext(); + + IntegerType *Int1Ty = IntegerType::getInt1Ty(C); + IntegerType *OldIntType = IntegerType::get(C, bitw); + IntegerType *NewIntType = IntegerType::get(C, bitw / 2); + + std::vector icomps; + + if (bitw % 2) { return 0; } + + /* not supported yet */ + if (bitw > 64) { return 0; } + + /* get all EQ, NE, UGT, and ULT icmps of width bitw. if the + * functions simplifyCompares() and simplifyIntSignedness() + * were executed only these four predicates should exist */ + for (auto &F : M) { + + if (!isInInstrumentList(&F)) continue; + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CmpInst *selectcmpInst = nullptr; + + if ((selectcmpInst = dyn_cast(&IN))) { + + if (selectcmpInst->getPredicate() == CmpInst::ICMP_EQ || + selectcmpInst->getPredicate() == CmpInst::ICMP_NE || + selectcmpInst->getPredicate() == CmpInst::ICMP_UGT || + selectcmpInst->getPredicate() == CmpInst::ICMP_ULT) { + + auto op0 = selectcmpInst->getOperand(0); + auto op1 = selectcmpInst->getOperand(1); + + IntegerType *intTyOp0 = dyn_cast(op0->getType()); + IntegerType *intTyOp1 = dyn_cast(op1->getType()); + + if (!intTyOp0 || !intTyOp1) { continue; } + + /* check if the bitwidths are the one we are looking for */ + if (intTyOp0->getBitWidth() != bitw || + intTyOp1->getBitWidth() != bitw) { + + continue; + + } + + icomps.push_back(selectcmpInst); + + } + + } + + } + + } + + } + + if (!icomps.size()) { return 0; } + + for (auto &IcmpInst : icomps) { + + BasicBlock *bb = IcmpInst->getParent(); + + auto op0 = IcmpInst->getOperand(0); + auto op1 = IcmpInst->getOperand(1); + + auto pred = dyn_cast(IcmpInst)->getPredicate(); + + BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); + + /* create the comparison of the top halves of the original operands */ + Instruction *s_op0, *op0_high, *s_op1, *op1_high, *icmp_high; + + s_op0 = BinaryOperator::Create(Instruction::LShr, op0, + ConstantInt::get(OldIntType, bitw / 2)); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op0); + op0_high = new TruncInst(s_op0, NewIntType); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), + op0_high); + + s_op1 = BinaryOperator::Create(Instruction::LShr, op1, + ConstantInt::get(OldIntType, bitw / 2)); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op1); + op1_high = new TruncInst(s_op1, NewIntType); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), + op1_high); + + icmp_high = CmpInst::Create(Instruction::ICmp, pred, op0_high, op1_high); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), + icmp_high); + + /* now we have to destinguish between == != and > < */ + if (pred == CmpInst::ICMP_EQ || pred == CmpInst::ICMP_NE) { + + /* transformation for == and != icmps */ + + /* create a compare for the lower half of the original operands */ + Instruction *op0_low, *op1_low, *icmp_low; + BasicBlock * cmp_low_bb = + BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); + + op0_low = new TruncInst(op0, NewIntType); + cmp_low_bb->getInstList().push_back(op0_low); + + op1_low = new TruncInst(op1, NewIntType); + cmp_low_bb->getInstList().push_back(op1_low); + + icmp_low = CmpInst::Create(Instruction::ICmp, pred, op0_low, op1_low); + cmp_low_bb->getInstList().push_back(icmp_low); + BranchInst::Create(end_bb, cmp_low_bb); + + /* dependent on the cmp of the high parts go to the end or go on with + * the comparison */ + auto term = bb->getTerminator(); + if (pred == CmpInst::ICMP_EQ) { + + BranchInst::Create(cmp_low_bb, end_bb, icmp_high, bb); + + } else { + + /* CmpInst::ICMP_NE */ + BranchInst::Create(end_bb, cmp_low_bb, icmp_high, bb); + + } + + term->eraseFromParent(); + + /* create the PHI and connect the edges accordingly */ + PHINode *PN = PHINode::Create(Int1Ty, 2, ""); + PN->addIncoming(icmp_low, cmp_low_bb); + if (pred == CmpInst::ICMP_EQ) { + + PN->addIncoming(ConstantInt::get(Int1Ty, 0), bb); + + } else { + + /* CmpInst::ICMP_NE */ + PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); + + } + + /* replace the old icmp with the new PHI */ + BasicBlock::iterator ii(IcmpInst); + ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); + + } else { + + /* CmpInst::ICMP_UGT and CmpInst::ICMP_ULT */ + /* transformations for < and > */ + + /* create a basic block which checks for the inverse predicate. + * if this is true we can go to the end if not we have to go to the + * bb which checks the lower half of the operands */ + Instruction *icmp_inv_cmp, *op0_low, *op1_low, *icmp_low; + BasicBlock * inv_cmp_bb = + BasicBlock::Create(C, "inv_cmp", end_bb->getParent(), end_bb); + if (pred == CmpInst::ICMP_UGT) { + + icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, + op0_high, op1_high); + + } else { + + icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, + op0_high, op1_high); + + } + + inv_cmp_bb->getInstList().push_back(icmp_inv_cmp); + + auto term = bb->getTerminator(); + term->eraseFromParent(); + BranchInst::Create(end_bb, inv_cmp_bb, icmp_high, bb); + + /* create a bb which handles the cmp of the lower halves */ + BasicBlock *cmp_low_bb = + BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); + op0_low = new TruncInst(op0, NewIntType); + cmp_low_bb->getInstList().push_back(op0_low); + op1_low = new TruncInst(op1, NewIntType); + cmp_low_bb->getInstList().push_back(op1_low); + + icmp_low = CmpInst::Create(Instruction::ICmp, pred, op0_low, op1_low); + cmp_low_bb->getInstList().push_back(icmp_low); + BranchInst::Create(end_bb, cmp_low_bb); + + BranchInst::Create(end_bb, cmp_low_bb, icmp_inv_cmp, inv_cmp_bb); + + PHINode *PN = PHINode::Create(Int1Ty, 3); + PN->addIncoming(icmp_low, cmp_low_bb); + PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); + PN->addIncoming(ConstantInt::get(Int1Ty, 0), inv_cmp_bb); + + BasicBlock::iterator ii(IcmpInst); + ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); + + } + + ++count; + + } + + return count; + +} + +bool SplitComparesTransform::runOnModule(Module &M) { + + int bitw = 64; + size_t count; + + char *bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW"); + if (!bitw_env) bitw_env = getenv("LAF_SPLIT_COMPARES_BITW"); + if (bitw_env) { bitw = atoi(bitw_env); } + + enableFPSplit = getenv("AFL_LLVM_LAF_SPLIT_FLOATS") != NULL; + + if ((isatty(2) && getenv("AFL_QUIET") == NULL) || + getenv("AFL_DEBUG") != NULL) { + + errs() << "Split-compare-pass by laf.intel@gmail.com, extended by " + "heiko@hexco.de\n"; + + } else { + + be_quiet = 1; + + } + + if (enableFPSplit) { + + count = splitFPCompares(M); + + if (!be_quiet) { + + errs() << "Split-floatingpoint-compare-pass: " << count + << " FP comparisons split\n"; + + } + + simplifyFPCompares(M); + + } + + simplifyCompares(M); + + simplifyIntSignedness(M); + + switch (bitw) { + + case 64: + count = splitIntCompares(M, bitw); + if (!be_quiet) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << count + << " split\n"; + + bitw >>= 1; +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) + [[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */ +#endif + case 32: + count = splitIntCompares(M, bitw); + if (!be_quiet) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << count + << " split\n"; + + bitw >>= 1; +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) + [[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */ +#endif + case 16: + count = splitIntCompares(M, bitw); + if (!be_quiet) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << count + << " split\n"; + + bitw >>= 1; + break; + + default: + if (!be_quiet) errs() << "NOT Running split-compare-pass \n"; + return false; + break; + + } + + verifyModule(M); + return true; + +} + +static void registerSplitComparesPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + PM.add(new SplitComparesTransform()); + +} + +static RegisterStandardPasses RegisterSplitComparesPass( + PassManagerBuilder::EP_OptimizerLast, registerSplitComparesPass); + +static RegisterStandardPasses RegisterSplitComparesTransPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitComparesPass); + +#if LLVM_VERSION_MAJOR >= 11 +static RegisterStandardPasses RegisterSplitComparesTransPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerSplitComparesPass); +#endif + diff --git a/instrumentation/split-switches-pass.so.cc b/instrumentation/split-switches-pass.so.cc new file mode 100644 index 00000000..a79d4114 --- /dev/null +++ b/instrumentation/split-switches-pass.so.cc @@ -0,0 +1,447 @@ +/* + * Copyright 2016 laf-intel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "llvm/Config/llvm-config.h" + +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Pass.h" +#include "llvm/Analysis/ValueTracking.h" + +#include "llvm/IR/IRBuilder.h" +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) + #include "llvm/IR/Verifier.h" + #include "llvm/IR/DebugInfo.h" +#else + #include "llvm/Analysis/Verifier.h" + #include "llvm/DebugInfo.h" + #define nullptr 0 +#endif + +#include +#include "afl-llvm-common.h" + +using namespace llvm; + +namespace { + +class SplitSwitchesTransform : public ModulePass { + + public: + static char ID; + SplitSwitchesTransform() : ModulePass(ID) { + + initInstrumentList(); + + } + + bool runOnModule(Module &M) override; + +#if LLVM_VERSION_MAJOR >= 4 + StringRef getPassName() const override { + +#else + const char *getPassName() const override { + +#endif + return "splits switch constructs"; + + } + + struct CaseExpr { + + ConstantInt *Val; + BasicBlock * BB; + + CaseExpr(ConstantInt *val = nullptr, BasicBlock *bb = nullptr) + : Val(val), BB(bb) { + + } + + }; + + typedef std::vector CaseVector; + + private: + bool splitSwitches(Module &M); + bool transformCmps(Module &M, const bool processStrcmp, + const bool processMemcmp); + BasicBlock *switchConvert(CaseVector Cases, std::vector bytesChecked, + BasicBlock *OrigBlock, BasicBlock *NewDefault, + Value *Val, unsigned level); + +}; + +} // namespace + +char SplitSwitchesTransform::ID = 0; + +/* switchConvert - Transform simple list of Cases into list of CaseRange's */ +BasicBlock *SplitSwitchesTransform::switchConvert( + CaseVector Cases, std::vector bytesChecked, BasicBlock *OrigBlock, + BasicBlock *NewDefault, Value *Val, unsigned level) { + + unsigned ValTypeBitWidth = Cases[0].Val->getBitWidth(); + IntegerType *ValType = + IntegerType::get(OrigBlock->getContext(), ValTypeBitWidth); + IntegerType * ByteType = IntegerType::get(OrigBlock->getContext(), 8); + unsigned BytesInValue = bytesChecked.size(); + std::vector setSizes; + std::vector > byteSets(BytesInValue, std::set()); + + assert(ValTypeBitWidth >= 8 && ValTypeBitWidth <= 64); + + /* for each of the possible cases we iterate over all bytes of the values + * build a set of possible values at each byte position in byteSets */ + for (CaseExpr &Case : Cases) { + + for (unsigned i = 0; i < BytesInValue; i++) { + + uint8_t byte = (Case.Val->getZExtValue() >> (i * 8)) & 0xFF; + byteSets[i].insert(byte); + + } + + } + + /* find the index of the first byte position that was not yet checked. then + * save the number of possible values at that byte position */ + unsigned smallestIndex = 0; + unsigned smallestSize = 257; + for (unsigned i = 0; i < byteSets.size(); i++) { + + if (bytesChecked[i]) continue; + if (byteSets[i].size() < smallestSize) { + + smallestIndex = i; + smallestSize = byteSets[i].size(); + + } + + } + + assert(bytesChecked[smallestIndex] == false); + + /* there are only smallestSize different bytes at index smallestIndex */ + + Instruction *Shift, *Trunc; + Function * F = OrigBlock->getParent(); + BasicBlock * NewNode = BasicBlock::Create(Val->getContext(), "NodeBlock", F); + Shift = BinaryOperator::Create(Instruction::LShr, Val, + ConstantInt::get(ValType, smallestIndex * 8)); + NewNode->getInstList().push_back(Shift); + + if (ValTypeBitWidth > 8) { + + Trunc = new TruncInst(Shift, ByteType); + NewNode->getInstList().push_back(Trunc); + + } else { + + /* not necessary to trunc */ + Trunc = Shift; + + } + + /* this is a trivial case, we can directly check for the byte, + * if the byte is not found go to default. if the byte was found + * mark the byte as checked. if this was the last byte to check + * we can finally execute the block belonging to this case */ + + if (smallestSize == 1) { + + uint8_t byte = *(byteSets[smallestIndex].begin()); + + /* insert instructions to check whether the value we are switching on is + * equal to byte */ + ICmpInst *Comp = + new ICmpInst(ICmpInst::ICMP_EQ, Trunc, ConstantInt::get(ByteType, byte), + "byteMatch"); + NewNode->getInstList().push_back(Comp); + + bytesChecked[smallestIndex] = true; + bool allBytesAreChecked = true; + + for (std::vector::iterator BCI = bytesChecked.begin(), + E = bytesChecked.end(); + BCI != E; ++BCI) { + + if (!*BCI) { + + allBytesAreChecked = false; + break; + + } + + } + + // if (std::all_of(bytesChecked.begin(), bytesChecked.end(), + // [](bool b) { return b; })) { + + if (allBytesAreChecked) { + + assert(Cases.size() == 1); + BranchInst::Create(Cases[0].BB, NewDefault, Comp, NewNode); + + /* we have to update the phi nodes! */ + for (BasicBlock::iterator I = Cases[0].BB->begin(); + I != Cases[0].BB->end(); ++I) { + + if (!isa(&*I)) { continue; } + PHINode *PN = cast(I); + + /* Only update the first occurrence. */ + unsigned Idx = 0, E = PN->getNumIncomingValues(); + for (; Idx != E; ++Idx) { + + if (PN->getIncomingBlock(Idx) == OrigBlock) { + + PN->setIncomingBlock(Idx, NewNode); + break; + + } + + } + + } + + } else { + + BasicBlock *BB = switchConvert(Cases, bytesChecked, OrigBlock, NewDefault, + Val, level + 1); + BranchInst::Create(BB, NewDefault, Comp, NewNode); + + } + + } + + /* there is no byte which we can directly check on, split the tree */ + else { + + std::vector byteVector; + std::copy(byteSets[smallestIndex].begin(), byteSets[smallestIndex].end(), + std::back_inserter(byteVector)); + std::sort(byteVector.begin(), byteVector.end()); + uint8_t pivot = byteVector[byteVector.size() / 2]; + + /* we already chose to divide the cases based on the value of byte at index + * smallestIndex the pivot value determines the threshold for the decicion; + * if a case value + * is smaller at this byte index move it to the LHS vector, otherwise to the + * RHS vector */ + + CaseVector LHSCases, RHSCases; + + for (CaseExpr &Case : Cases) { + + uint8_t byte = (Case.Val->getZExtValue() >> (smallestIndex * 8)) & 0xFF; + + if (byte < pivot) { + + LHSCases.push_back(Case); + + } else { + + RHSCases.push_back(Case); + + } + + } + + BasicBlock *LBB, *RBB; + LBB = switchConvert(LHSCases, bytesChecked, OrigBlock, NewDefault, Val, + level + 1); + RBB = switchConvert(RHSCases, bytesChecked, OrigBlock, NewDefault, Val, + level + 1); + + /* insert instructions to check whether the value we are switching on is + * equal to byte */ + ICmpInst *Comp = + new ICmpInst(ICmpInst::ICMP_ULT, Trunc, + ConstantInt::get(ByteType, pivot), "byteMatch"); + NewNode->getInstList().push_back(Comp); + BranchInst::Create(LBB, RBB, Comp, NewNode); + + } + + return NewNode; + +} + +bool SplitSwitchesTransform::splitSwitches(Module &M) { + +#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 7) + LLVMContext &C = M.getContext(); +#endif + + std::vector switches; + + /* iterate over all functions, bbs and instruction and add + * all switches to switches vector for later processing */ + for (auto &F : M) { + + if (!isInInstrumentList(&F)) continue; + + for (auto &BB : F) { + + SwitchInst *switchInst = nullptr; + + if ((switchInst = dyn_cast(BB.getTerminator()))) { + + if (switchInst->getNumCases() < 1) continue; + switches.push_back(switchInst); + + } + + } + + } + + if (!switches.size()) return false; + if (!be_quiet) + errs() << "Rewriting " << switches.size() << " switch statements " + << "\n"; + + for (auto &SI : switches) { + + BasicBlock *CurBlock = SI->getParent(); + BasicBlock *OrigBlock = CurBlock; + Function * F = CurBlock->getParent(); + /* this is the value we are switching on */ + Value * Val = SI->getCondition(); + BasicBlock *Default = SI->getDefaultDest(); + unsigned bitw = Val->getType()->getIntegerBitWidth(); + + if (!be_quiet) + errs() << "switch: " << SI->getNumCases() << " cases " << bitw + << " bit\n"; + + /* If there is only the default destination or the condition checks 8 bit or + * less, don't bother with the code below. */ + if (!SI->getNumCases() || bitw <= 8) { + + if (!be_quiet) errs() << "skip trivial switch..\n"; + continue; + + } + + /* Create a new, empty default block so that the new hierarchy of + * if-then statements go to this and the PHI nodes are happy. + * if the default block is set as an unreachable we avoid creating one + * because will never be a valid target.*/ + BasicBlock *NewDefault = nullptr; + NewDefault = BasicBlock::Create(SI->getContext(), "NewDefault", F, Default); + BranchInst::Create(Default, NewDefault); + + /* Prepare cases vector. */ + CaseVector Cases; + for (SwitchInst::CaseIt i = SI->case_begin(), e = SI->case_end(); i != e; + ++i) +#if LLVM_VERSION_MAJOR < 5 + Cases.push_back(CaseExpr(i.getCaseValue(), i.getCaseSuccessor())); +#else + Cases.push_back(CaseExpr(i->getCaseValue(), i->getCaseSuccessor())); +#endif + /* bugfix thanks to pbst + * round up bytesChecked (in case getBitWidth() % 8 != 0) */ + std::vector bytesChecked((7 + Cases[0].Val->getBitWidth()) / 8, + false); + BasicBlock * SwitchBlock = + switchConvert(Cases, bytesChecked, OrigBlock, NewDefault, Val, 0); + + /* Branch to our shiny new if-then stuff... */ + BranchInst::Create(SwitchBlock, OrigBlock); + + /* We are now done with the switch instruction, delete it. */ + CurBlock->getInstList().erase(SI); + + /* we have to update the phi nodes! */ + for (BasicBlock::iterator I = Default->begin(); I != Default->end(); ++I) { + + if (!isa(&*I)) { continue; } + PHINode *PN = cast(I); + + /* Only update the first occurrence. */ + unsigned Idx = 0, E = PN->getNumIncomingValues(); + for (; Idx != E; ++Idx) { + + if (PN->getIncomingBlock(Idx) == OrigBlock) { + + PN->setIncomingBlock(Idx, NewDefault); + break; + + } + + } + + } + + } + + verifyModule(M); + return true; + +} + +bool SplitSwitchesTransform::runOnModule(Module &M) { + + if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) + llvm::errs() << "Running split-switches-pass by laf.intel@gmail.com\n"; + else + be_quiet = 1; + splitSwitches(M); + verifyModule(M); + + return true; + +} + +static void registerSplitSwitchesTransPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + auto p = new SplitSwitchesTransform(); + PM.add(p); + +} + +static RegisterStandardPasses RegisterSplitSwitchesTransPass( + PassManagerBuilder::EP_OptimizerLast, registerSplitSwitchesTransPass); + +static RegisterStandardPasses RegisterSplitSwitchesTransPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitSwitchesTransPass); + +#if LLVM_VERSION_MAJOR >= 11 +static RegisterStandardPasses RegisterSplitSwitchesTransPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerSplitSwitchesTransPass); +#endif + diff --git a/llvm_mode/GNUmakefile b/llvm_mode/GNUmakefile deleted file mode 100644 index c14e8b4e..00000000 --- a/llvm_mode/GNUmakefile +++ /dev/null @@ -1,480 +0,0 @@ -# american fuzzy lop++ - LLVM instrumentation -# ----------------------------------------- -# -# Written by Laszlo Szekeres and -# Michal Zalewski -# -# LLVM integration design comes from Laszlo Szekeres. -# -# Copyright 2015, 2016 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -# For Heiko: -#TEST_MMAP=1 -HASH=\# - -PREFIX ?= /usr/local -HELPER_PATH ?= $(PREFIX)/lib/afl -BIN_PATH ?= $(PREFIX)/bin -DOC_PATH ?= $(PREFIX)/share/doc/afl -MISC_PATH ?= $(PREFIX)/share/afl -MAN_PATH ?= $(PREFIX)/share/man/man8 - -VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2) - -BUILD_DATE ?= $(shell date -u -d "@$(SOURCE_DATE_EPOCH)" "+%Y-%m-%d" 2>/dev/null || date -u -r "$(SOURCE_DATE_EPOCH)" "+%Y-%m-%d" 2>/dev/null || date -u "+%Y-%m-%d") - -ifeq "$(shell uname)" "OpenBSD" - LLVM_CONFIG ?= $(BIN_PATH)/llvm-config - HAS_OPT = $(shell test -x $(BIN_PATH)/opt && echo 0 || echo 1) - ifeq "$(HAS_OPT)" "1" - $(error llvm_mode needs a complete llvm installation (versions 3.4 up to 12) -> e.g. "pkg_add llvm-7.0.1p9") - endif -else - LLVM_CONFIG ?= llvm-config -endif - -LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/git//' ) -LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\.[0-3]|^19' && echo 1 || echo 0 ) -LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[0-9]' && echo 1 || echo 0 ) -LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-9]' && echo 1 || echo 0 ) -LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//') -LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null) -LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null) -LLVM_STDCXX = gnu++11 -LLVM_APPLE_XCODE = $(shell clang -v 2>&1 | grep -q Apple && echo 1 || echo 0) -LLVM_LTO = 0 - -ifeq "$(LLVMVER)" "" - $(warning [!] llvm_mode needs llvm-config, which was not found) -endif - -ifeq "$(LLVM_UNSUPPORTED)" "1" - $(warning llvm_mode only supports llvm versions 3.4 up to 12) -endif - -LLVM_TOO_OLD=1 - -ifeq "$(LLVM_MAJOR)" "9" - $(info [+] llvm_mode detected llvm 9, enabling neverZero implementation) - LLVM_TOO_OLD=0 -endif - -ifeq "$(LLVM_NEW_API)" "1" - $(info [+] llvm_mode detected llvm 10+, enabling neverZero implementation and c++14) - LLVM_STDCXX = c++14 - LLVM_TOO_OLD=0 -endif - -ifeq "$(LLVM_TOO_OLD)" "1" - $(info [!] llvm_mode detected an old version of llvm, upgrade to at least 9 or preferable 11!) - $(shell sleep 1) -endif - -ifeq "$(LLVM_HAVE_LTO)" "1" - $(info [+] llvm_mode detected llvm 11+, enabling afl-clang-lto LTO implementation) - LLVM_LTO = 1 - #TEST_MMAP = 1 -endif - -ifeq "$(LLVM_LTO)" "0" - $(info [+] llvm_mode detected llvm < 11, afl-clang-lto LTO will not be build.) -endif - -ifeq "$(LLVM_APPLE_XCODE)" "1" - $(warning llvm_mode will not compile with Xcode clang...) -endif - -# We were using llvm-config --bindir to get the location of clang, but -# this seems to be busted on some distros, so using the one in $PATH is -# probably better. - -CC = $(LLVM_BINDIR)/clang -CXX = $(LLVM_BINDIR)/clang++ - -# llvm-config --bindir may not providing a valid path, so ... -ifeq "$(shell test -e $(CC) || echo 1 )" "1" - # however we must ensure that this is not a "CC=gcc make" - ifeq "$(shell command -v $(CC) 2> /dev/null)" "" - # we do not have a valid CC variable so we try alternatives - ifeq "$(shell test -e '$(BIN_DIR)/clang' && echo 1)" "1" - # we found one in the local install directory, lets use these - CC = $(BIN_DIR)/clang - else - # hope for the best - $(warning we have trouble finding clang - llvm-config is not helping us) - CC = clang - endif - endif -endif -# llvm-config --bindir may not providing a valid path, so ... -ifeq "$(shell test -e $(CXX) || echo 1 )" "1" - # however we must ensure that this is not a "CC=gcc make" - ifeq "$(shell command -v $(CXX) 2> /dev/null)" "" - # we do not have a valid CC variable so we try alternatives - ifeq "$(shell test -e '$(BIN_DIR)/clang++' && echo 1)" "1" - # we found one in the local install directory, lets use these - CXX = $(BIN_DIR)/clang++ - else - # hope for the best - $(warning we have trouble finding clang++ - llvm-config is not helping us) - CXX = clang++ - endif - endif -endif - -# sanity check. -# Are versions of clang --version and llvm-config --version equal? -CLANGVER = $(shell $(CC) --version | sed -E -ne '/^.*version\ (1?[0-9]\.[0-9]\.[0-9]).*/s//\1/p') - -# I disable this because it does not make sense with what we did before (marc) -# We did exactly set these 26 lines above with these values, and it would break -# "CC=gcc make" etc. usages -ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" - CC_SAVE := $(LLVM_BINDIR)/clang -else - CC_SAVE := $(CC) -endif -ifeq "$(findstring clang, $(shell $(CXX) --version 2>/dev/null))" "" - CXX_SAVE := $(LLVM_BINDIR)/clang++ -else - CXX_SAVE := $(CXX) -endif - -CLANG_BIN := $(CC_SAVE) -CLANGPP_BIN := $(CXX_SAVE) - -ifeq "$(CC_SAVE)" "$(LLVM_BINDIR)/clang" - USE_BINDIR = 1 -else - ifeq "$(CXX_SAVE)" "$(LLVM_BINDIR)/clang++" - USE_BINDIR = 1 - else - USE_BINDIR = 0 - endif -endif - -# On old platform we cannot compile with clang because std++ libraries are too -# old. For these we need to use gcc/g++, so if we find REAL_CC and REAL_CXX -# variable we override the compiler variables here -ifneq "$(REAL_CC)" "" -CC = $(REAL_CC) -endif -ifneq "$(REAL_CXX)" "" -CXX = $(REAL_CXX) -endif - -# After we set CC/CXX we can start makefile magic tests - -#ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" -# CFLAGS_OPT = -march=native -#endif - -ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" - AFL_CLANG_FLTO ?= -flto=full -else - ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" - AFL_CLANG_FLTO ?= -flto=thin - else - ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" - AFL_CLANG_FLTO ?= -flto - endif - endif -endif - -ifeq "$(LLVM_LTO)" "1" - ifneq "$(AFL_CLANG_FLTO)" "" - ifeq "$(AFL_REAL_LD)" "" - ifneq "$(shell readlink $(LLVM_BINDIR)/ld.lld 2>&1)" "" - AFL_REAL_LD = $(LLVM_BINDIR)/ld.lld - else - $(warn ld.lld not found, cannot enable LTO mode) - LLVM_LTO = 0 - endif - endif - else - $(warn clang option -flto is not working - maybe LLVMgold.so not found - cannot enable LTO mode) - LLVM_LTO = 0 - endif -endif - -AFL_CLANG_FUSELD= -ifeq "$(LLVM_LTO)" "1" - ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=`command -v ld` -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" - AFL_CLANG_FUSELD=1 - ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=ld.lld --ld-path=$(LLVM_BINDIR)/ld.lld -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" - AFL_CLANG_LDPATH=1 - endif - else - $(warn -fuse-ld is not working, cannot enable LTO mode) - LLVM_LTO = 0 - endif -endif - -ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fdebug-prefix-map=$(CURDIR)=llvm_mode -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" - AFL_CLANG_DEBUG_PREFIX = -fdebug-prefix-map="$(CURDIR)=llvm_mode" -else - AFL_CLANG_DEBUG_PREFIX = "" -endif - -CFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2 -CFLAGS_SAFE := -Wall -g -Wno-pointer-sign -I ../include/ \ - -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ - -DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \ - -DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \ - -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \ - -DAFL_REAL_LD=\"$(AFL_REAL_LD)\" \ - -DAFL_CLANG_LDPATH=\"$(AFL_CLANG_LDPATH)\" \ - -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \ - -DCLANG_BIN=\"$(CLANG_BIN)\" -DCLANGPP_BIN=\"$(CLANGPP_BIN)\" -DUSE_BINDIR=$(USE_BINDIR) -Wno-unused-function \ - $(AFL_CLANG_DEBUG_PREFIX) -override CFLAGS += $(CFLAGS_SAFE) - -ifdef AFL_TRACE_PC - $(info Compile option AFL_TRACE_PC is deprecated, just set AFL_LLVM_INSTRUMENT=PCGUARD to activate when compiling targets ) -endif - -CXXFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2 -override CXXFLAGS += -Wall -g -I ../include/ \ - -DVERSION=\"$(VERSION)\" -Wno-variadic-macros - -ifneq "$(shell $(LLVM_CONFIG) --includedir) 2> /dev/null" "" - CLANG_CFL = -I$(shell $(LLVM_CONFIG) --includedir) -endif -ifneq "$(LLVM_CONFIG)" "" - CLANG_CFL += -I$(shell dirname $(LLVM_CONFIG))/../include -endif -CLANG_CPPFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC $(CXXFLAGS) -Wno-deprecated-declarations -CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS) - - -# User teor2345 reports that this is required to make things work on MacOS X. -ifeq "$(shell uname)" "Darwin" - CLANG_LFL += -Wl,-flat_namespace -Wl,-undefined,suppress -else - CLANG_CPPFL += -Wl,-znodelete -endif - -ifeq "$(shell uname)" "OpenBSD" - CLANG_LFL += `$(LLVM_CONFIG) --libdir`/libLLVM.so - CLANG_CPPFL += -mno-retpoline - CFLAGS += -mno-retpoline - # Needed for unwind symbols - LDFLAGS += -lc++abi -endif - -ifeq "$(shell echo '$(HASH)include @$(HASH)include @int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" - SHMAT_OK=1 -else - SHMAT_OK=0 - #CFLAGS+=-DUSEMMAP=1 - LDFLAGS += -Wno-deprecated-declarations -endif - -ifeq "$(TEST_MMAP)" "1" - SHMAT_OK=0 - CFLAGS+=-DUSEMMAP=1 - LDFLAGS += -Wno-deprecated-declarations -endif - -PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-ld-lto ../afl-llvm-lto-instrumentlist.so ../afl-llvm-lto-instrumentation.so ../libLLVMInsTrim.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so ../cmplog-routines-pass.so ../cmplog-instructions-pass.so ../SanitizerCoverageLTO.so - -# If prerequisites are not given, warn, do not build anything, and exit with code 0 -ifeq "$(LLVMVER)" "" - NO_BUILD = 1 -endif - -ifneq "$(LLVM_UNSUPPORTED)$(LLVM_APPLE_XCODE)" "00" - NO_BUILD = 1 -endif - -ifeq "$(NO_BUILD)" "1" - TARGETS = no_build -else - TARGETS = test_shm test_deps $(PROGS) afl-clang-fast.8 test_build all_done -endif - -LLVM_MIN_4_0_1 = $(shell awk 'function tonum(ver, a) {split(ver,a,"."); return a[1]*1000000+a[2]*1000+a[3]} BEGIN { exit tonum(ARGV[1]) >= tonum(ARGV[2]) }' $(LLVMVER) 4.0.1; echo $$?) - -all: $(TARGETS) - -ifeq "$(SHMAT_OK)" "1" - -test_shm: - @echo "[+] shmat seems to be working." - @rm -f .test2 - -else - -test_shm: - @echo "[-] shmat seems not to be working, switching to mmap implementation" - -endif - -no_build: - @printf "%b\\n" "\\033[0;31mPrerequisites are not met, skipping build llvm_mode\\033[0m" - -test_deps: - @echo "[*] Checking for working 'llvm-config'..." - ifneq "$(LLVM_APPLE_XCODE)" "1" - @type $(LLVM_CONFIG) >/dev/null 2>&1 || ( echo "[-] Oops, can't find 'llvm-config'. Install clang or set \$$LLVM_CONFIG or \$$PATH beforehand."; echo " (Sometimes, the binary will be named llvm-config-3.5 or something like that.)"; exit 1 ) - endif - @echo "[*] Checking for working '$(CC)'..." - @type $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 ) - @echo "[*] Checking for matching versions of '$(CC)' and '$(LLVM_CONFIG)'" -ifneq "$(CLANGVER)" "$(LLVMVER)" - @echo "[!] WARNING: we have llvm-config version $(LLVMVER) and a clang version $(CLANGVER)" -else - @echo "[*] We have llvm-config version $(LLVMVER) with a clang version $(CLANGVER), good." -endif - @echo "[*] Checking for '../afl-showmap'..." - @test -f ../afl-showmap || ( echo "[-] Oops, can't find '../afl-showmap'. Be sure to compile AFL first."; exit 1 ) - @echo "[+] All set and ready to build." - -afl-common.o: ../src/afl-common.c - $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(LDFLAGS) - -../afl-clang-fast: afl-clang-fast.c afl-common.o | test_deps - $(CC) $(CLANG_CFL) $(CFLAGS) $(CPPFLAGS) $< afl-common.o -o $@ $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\" - ln -sf afl-clang-fast ../afl-clang-fast++ -ifneq "$(AFL_CLANG_FLTO)" "" -ifeq "$(LLVM_LTO)" "1" - ln -sf afl-clang-fast ../afl-clang-lto - ln -sf afl-clang-fast ../afl-clang-lto++ -endif -endif - -afl-llvm-common.o: afl-llvm-common.cc afl-llvm-common.h - $(CXX) $(CFLAGS) $(CPPFLAGS) `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC -std=$(LLVM_STDCXX) -c $< -o $@ - -../libLLVMInsTrim.so: LLVMInsTrim.so.cc MarkNodes.cc afl-llvm-common.o | test_deps - -$(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL) afl-llvm-common.o - -../afl-llvm-pass.so: afl-llvm-pass.so.cc afl-llvm-common.o | test_deps -ifeq "$(LLVM_MIN_4_0_1)" "0" - $(info [!] N-gram branch coverage instrumentation is not available for llvm version $(LLVMVER)) -endif - $(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o - -../afl-llvm-lto-instrumentlist.so: afl-llvm-lto-instrumentlist.so.cc afl-llvm-common.o -ifeq "$(LLVM_LTO)" "1" - $(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o -endif - -../afl-ld-lto: afl-ld-lto.c -ifeq "$(LLVM_LTO)" "1" - $(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@ -endif - -../SanitizerCoverageLTO.so: SanitizerCoverageLTO.so.cc -ifeq "$(LLVM_LTO)" "1" - $(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o -endif - -../afl-llvm-lto-instrumentation.so: afl-llvm-lto-instrumentation.so.cc afl-llvm-common.o -ifeq "$(LLVM_LTO)" "1" - $(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o - $(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto.o - @$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m64 -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto-64.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi - @$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m32 -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi -endif - -# laf -../split-switches-pass.so: split-switches-pass.so.cc afl-llvm-common.o | test_deps - $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o -../compare-transform-pass.so: compare-transform-pass.so.cc afl-llvm-common.o | test_deps - $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o -../split-compares-pass.so: split-compares-pass.so.cc afl-llvm-common.o | test_deps - $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o -# /laf - -../cmplog-routines-pass.so: cmplog-routines-pass.cc afl-llvm-common.o | test_deps - $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o - -../cmplog-instructions-pass.so: cmplog-instructions-pass.cc afl-llvm-common.o | test_deps - $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o - -document: - $(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt.o - @$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -m32 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-32.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi - @$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -m64 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-64.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi - -../afl-llvm-rt.o: afl-llvm-rt.o.c | test_deps - $(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -fPIC -c $< -o $@ - -../afl-llvm-rt-32.o: afl-llvm-rt.o.c | test_deps - @printf "[*] Building 32-bit variant of the runtime (-m32)... " - @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi - -../afl-llvm-rt-64.o: afl-llvm-rt.o.c | test_deps - @printf "[*] Building 64-bit variant of the runtime (-m64)... " - @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi - -test_build: $(PROGS) - @echo "[*] Testing the CC wrapper and instrumentation output..." - unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_LLVM_LAF_SPLIT_SWITCHES=1 AFL_LLVM_LAF_TRANSFORM_COMPARES=1 AFL_LLVM_LAF_SPLIT_COMPARES=1 ../afl-clang-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS) - ASAN_OPTIONS=detect_leaks=0 ../afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null - echo 1 | ASAN_OPTIONS=detect_leaks=0 ../afl-showmap -m none -q -o .test-instr1 ./test-instr - @rm -f test-instr - @cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi - @echo "[+] All right, the instrumentation seems to be working!" - -all_done: test_build - @echo "[+] All done! You can now use '../afl-clang-fast' to compile programs." - -.NOTPARALLEL: clean - -install: all - install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH) - if [ -f ../afl-clang-fast -a -f ../libLLVMInsTrim.so -a -f ../afl-llvm-rt.o ]; then set -e; install -m 755 ../afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 ../libLLVMInsTrim.so ../afl-llvm-pass.so ../afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi - if [ -f ../afl-clang-lto ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-llvm-lto-instrumentation.so ../afl-llvm-rt-lto*.o ../afl-llvm-lto-instrumentlist.so $${DESTDIR}$(HELPER_PATH); fi - if [ -f ../afl-ld-lto ]; then set -e; install -m 755 ../afl-ld-lto $${DESTDIR}$(BIN_PATH); fi - if [ -f ../afl-llvm-rt-32.o ]; then set -e; install -m 755 ../afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH); fi - if [ -f ../afl-llvm-rt-64.o ]; then set -e; install -m 755 ../afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH); fi - if [ -f ../compare-transform-pass.so ]; then set -e; install -m 755 ../compare-transform-pass.so $${DESTDIR}$(HELPER_PATH); fi - if [ -f ../split-compares-pass.so ]; then set -e; install -m 755 ../split-compares-pass.so $${DESTDIR}$(HELPER_PATH); fi - if [ -f ../split-switches-pass.so ]; then set -e; install -m 755 ../split-switches-pass.so $${DESTDIR}$(HELPER_PATH); fi - if [ -f ../cmplog-instructions-pass.so ]; then set -e; install -m 755 ../cmplog-*-pass.so $${DESTDIR}$(HELPER_PATH); fi - if [ -f ../SanitizerCoverageLTO.so ]; then set -e; install -m 755 ../SanitizerCoverageLTO.so $${DESTDIR}$(HELPER_PATH); fi - set -e; install -m 644 ../dynamic_list.txt $${DESTDIR}$(HELPER_PATH) - set -e; if [ -f ../afl-clang-fast ] ; then ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang++ ; else ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang++; fi - install -m 644 README.*.md $${DESTDIR}$(DOC_PATH)/ - install -m 644 README.md $${DESTDIR}$(DOC_PATH)/README.llvm_mode.md - -vpath % .. -%.8: % - @echo .TH $* 8 $(BUILD_DATE) "afl++" > ../$@ - @echo .SH NAME >> ../$@ - @echo -n ".B $* \- " >> ../$@ - @../$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> ../$@ - @echo >> ../$@ - @echo .SH SYNOPSIS >> ../$@ - @../$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ../$@ - @echo >> ../$@ - @echo .SH OPTIONS >> ../$@ - @echo .nf >> ../$@ - @../$* -h 2>&1 | tail -n +4 >> ../$@ - @echo >> ../$@ - @echo .SH AUTHOR >> ../$@ - @echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse , Heiko \"hexcoder-\" Eissfeldt , Andrea Fioraldi and Dominik Maier " >> ../$@ - @echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ../$@ - @echo >> ../$@ - @echo .SH LICENSE >> ../$@ - @echo Apache License Version 2.0, January 2004 >> ../$@ - ln -sf afl-clang-fast.8 ../afl-clang-fast++.8 -ifneq "$(AFL_CLANG_FLTO)" "" -ifeq "$(LLVM_LTO)" "1" - ln -sf afl-clang-fast.8 ../afl-clang-lto.8 - ln -sf afl-clang-fast.8 ../afl-clang-lto++.8 -endif -endif - -clean: - rm -f *.o *.so *~ a.out core core.[1-9][0-9]* .test2 test-instr .test-instr0 .test-instr1 *.dwo - rm -f $(PROGS) afl-common.o ../afl-clang-fast++ ../afl-clang-lto ../afl-clang-lto++ ../afl-clang*.8 ../ld ../afl-ld ../afl-llvm-rt*.o diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc deleted file mode 100644 index 61a420ba..00000000 --- a/llvm_mode/LLVMInsTrim.so.cc +++ /dev/null @@ -1,598 +0,0 @@ -#include -#include -#include -#include - -#include "llvm/Config/llvm-config.h" -#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5 -typedef long double max_align_t; -#endif - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/DenseSet.h" -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) - #include "llvm/IR/CFG.h" - #include "llvm/IR/Dominators.h" - #include "llvm/IR/DebugInfo.h" -#else - #include "llvm/Support/CFG.h" - #include "llvm/Analysis/Dominators.h" - #include "llvm/DebugInfo.h" -#endif -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/IR/BasicBlock.h" -#include -#include -#include -#include -#include - -#include "MarkNodes.h" -#include "afl-llvm-common.h" -#include "llvm-ngram-coverage.h" - -#include "config.h" -#include "debug.h" - -using namespace llvm; - -static cl::opt MarkSetOpt("markset", cl::desc("MarkSet"), - cl::init(false)); -static cl::opt LoopHeadOpt("loophead", cl::desc("LoopHead"), - cl::init(false)); - -namespace { - -struct InsTrim : public ModulePass { - - protected: - uint32_t function_minimum_size = 1; - char * skip_nozero = NULL; - - private: - std::mt19937 generator; - int total_instr = 0; - - unsigned int genLabel() { - - return generator() & (MAP_SIZE - 1); - - } - - public: - static char ID; - - InsTrim() : ModulePass(ID), generator(0) { - - initInstrumentList(); - - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - - AU.addRequired(); - - } - -#if LLVM_VERSION_MAJOR < 4 - const char * -#else - StringRef -#endif - getPassName() const override { - - return "InstTrim Instrumentation"; - - } - -#if LLVM_VERSION_MAJOR > 4 || \ - (LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1) - #define AFL_HAVE_VECTOR_INTRINSICS 1 -#endif - - bool runOnModule(Module &M) override { - - setvbuf(stdout, NULL, _IONBF, 0); - - if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { - - SAYF(cCYA "LLVMInsTrim" VERSION cRST " by csienslab\n"); - - } else - - be_quiet = 1; - - if (getenv("AFL_DEBUG") != NULL) debug = 1; - - LLVMContext &C = M.getContext(); - - IntegerType *Int8Ty = IntegerType::getInt8Ty(C); - IntegerType *Int32Ty = IntegerType::getInt32Ty(C); - -#if LLVM_VERSION_MAJOR < 9 - char *neverZero_counters_str; - if ((neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO")) != NULL) - if (!be_quiet) OKF("LLVM neverZero activated (by hexcoder)\n"); -#endif - skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); - - if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL || - getenv("LOOPHEAD") != NULL) { - - LoopHeadOpt = true; - - } - - unsigned int PrevLocSize = 0; - char * ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); - if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); - char *ctx_str = getenv("AFL_LLVM_CTX"); - -#ifdef AFL_HAVE_VECTOR_INTRINSICS - unsigned int ngram_size = 0; - /* Decide previous location vector size (must be a power of two) */ - VectorType *PrevLocTy = NULL; - - if (ngram_size_str) - if (sscanf(ngram_size_str, "%u", &ngram_size) != 1 || ngram_size < 2 || - ngram_size > NGRAM_SIZE_MAX) - FATAL( - "Bad value of AFL_NGRAM_SIZE (must be between 2 and NGRAM_SIZE_MAX " - "(%u))", - NGRAM_SIZE_MAX); - - if (ngram_size) - PrevLocSize = ngram_size - 1; - else -#else - if (ngram_size_str) - #ifdef LLVM_VERSION_STRING - FATAL( - "Sorry, NGRAM branch coverage is not supported with llvm version %s!", - LLVM_VERSION_STRING); - #else - #ifndef LLVM_VERSION_PATCH - FATAL( - "Sorry, NGRAM branch coverage is not supported with llvm version " - "%d.%d.%d!", - LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0); - #else - FATAL( - "Sorry, NGRAM branch coverage is not supported with llvm version " - "%d.%d.%d!", - LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERISON_PATCH); - #endif - #endif -#endif - PrevLocSize = 1; - -#ifdef AFL_HAVE_VECTOR_INTRINSICS - // IntegerType *Int64Ty = IntegerType::getInt64Ty(C); - int PrevLocVecSize = PowerOf2Ceil(PrevLocSize); - IntegerType *IntLocTy = - IntegerType::getIntNTy(C, sizeof(PREV_LOC_T) * CHAR_BIT); - if (ngram_size) - PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize - #if LLVM_VERSION_MAJOR >= 12 - , - false - #endif - ); -#endif - - /* Get globals for the SHM region and the previous location. Note that - __afl_prev_loc is thread-local. */ - - GlobalVariable *AFLMapPtr = - new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, - GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); - GlobalVariable *AFLPrevLoc; - GlobalVariable *AFLContext = NULL; - LoadInst * PrevCtx = NULL; // for CTX sensitive coverage - - if (ctx_str) -#ifdef __ANDROID__ - AFLContext = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx"); -#else - AFLContext = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx", - 0, GlobalVariable::GeneralDynamicTLSModel, 0, false); -#endif - -#ifdef AFL_HAVE_VECTOR_INTRINSICS - if (ngram_size) - #ifdef __ANDROID__ - AFLPrevLoc = new GlobalVariable( - M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage, - /* Initializer */ nullptr, "__afl_prev_loc"); - #else - AFLPrevLoc = new GlobalVariable( - M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage, - /* Initializer */ nullptr, "__afl_prev_loc", - /* InsertBefore */ nullptr, GlobalVariable::GeneralDynamicTLSModel, - /* AddressSpace */ 0, /* IsExternallyInitialized */ false); - #endif - else -#endif -#ifdef __ANDROID__ - AFLPrevLoc = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc"); -#else - AFLPrevLoc = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0, - GlobalVariable::GeneralDynamicTLSModel, 0, false); -#endif - -#ifdef AFL_HAVE_VECTOR_INTRINSICS - /* Create the vector shuffle mask for updating the previous block history. - Note that the first element of the vector will store cur_loc, so just set - it to undef to allow the optimizer to do its thing. */ - - SmallVector PrevLocShuffle = {UndefValue::get(Int32Ty)}; - - for (unsigned I = 0; I < PrevLocSize - 1; ++I) - PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, I)); - - for (int I = PrevLocSize; I < PrevLocVecSize; ++I) - PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize)); - - Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle); -#endif - - // this is our default - MarkSetOpt = true; - - ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); - ConstantInt *One = ConstantInt::get(Int8Ty, 1); - - u64 total_rs = 0; - u64 total_hs = 0; - - scanForDangerousFunctions(&M); - - for (Function &F : M) { - - if (debug) { - - uint32_t bb_cnt = 0; - - for (auto &BB : F) - if (BB.size() > 0) ++bb_cnt; - SAYF(cMGN "[D] " cRST "Function %s size %zu %u\n", - F.getName().str().c_str(), F.size(), bb_cnt); - - } - - if (!isInInstrumentList(&F)) continue; - - // if the function below our minimum size skip it (1 or 2) - if (F.size() < function_minimum_size) { continue; } - - std::unordered_set MS; - if (!MarkSetOpt) { - - for (auto &BB : F) { - - MS.insert(&BB); - - } - - total_rs += F.size(); - - } else { - - auto Result = markNodes(&F); - auto RS = Result.first; - auto HS = Result.second; - - MS.insert(RS.begin(), RS.end()); - if (!LoopHeadOpt) { - - MS.insert(HS.begin(), HS.end()); - total_rs += MS.size(); - - } else { - - DenseSet> EdgeSet; - DominatorTreeWrapperPass * DTWP = - &getAnalysis(F); - auto DT = &DTWP->getDomTree(); - - total_rs += RS.size(); - total_hs += HS.size(); - - for (BasicBlock *BB : HS) { - - bool Inserted = false; - for (auto BI = pred_begin(BB), BE = pred_end(BB); BI != BE; ++BI) { - - auto Edge = BasicBlockEdge(*BI, BB); - if (Edge.isSingleEdge() && DT->dominates(Edge, BB)) { - - EdgeSet.insert({*BI, BB}); - Inserted = true; - break; - - } - - } - - if (!Inserted) { - - MS.insert(BB); - total_rs += 1; - total_hs -= 1; - - } - - } - - for (auto I = EdgeSet.begin(), E = EdgeSet.end(); I != E; ++I) { - - auto PredBB = I->first; - auto SuccBB = I->second; - auto NewBB = - SplitBlockPredecessors(SuccBB, {PredBB}, ".split", DT, nullptr, -#if LLVM_VERSION_MAJOR >= 8 - nullptr, -#endif - false); - MS.insert(NewBB); - - } - - } - - for (BasicBlock &BB : F) { - - if (MS.find(&BB) == MS.end()) { continue; } - IRBuilder<> IRB(&*BB.getFirstInsertionPt()); - -#ifdef AFL_HAVE_VECTOR_INTRINSICS - if (ngram_size) { - - LoadInst *PrevLoc = IRB.CreateLoad(AFLPrevLoc); - PrevLoc->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - Value *ShuffledPrevLoc = IRB.CreateShuffleVector( - PrevLoc, UndefValue::get(PrevLocTy), PrevLocShuffleMask); - Value *UpdatedPrevLoc = IRB.CreateInsertElement( - ShuffledPrevLoc, ConstantInt::get(Int32Ty, genLabel()), - (uint64_t)0); - - IRB.CreateStore(UpdatedPrevLoc, AFLPrevLoc) - ->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } else - -#endif - { - - IRB.CreateStore(ConstantInt::get(Int32Ty, genLabel()), AFLPrevLoc); - - } - - } - - } - - int has_calls = 0; - for (BasicBlock &BB : F) { - - auto PI = pred_begin(&BB); - auto PE = pred_end(&BB); - IRBuilder<> IRB(&*BB.getFirstInsertionPt()); - Value * L = NULL; - unsigned int cur_loc; - - // Context sensitive coverage - if (ctx_str && &BB == &F.getEntryBlock()) { - - PrevCtx = IRB.CreateLoad(AFLContext); - PrevCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - // does the function have calls? and is any of the calls larger than - // one basic block? - has_calls = 0; - for (auto &BB : F) { - - if (has_calls) break; - for (auto &IN : BB) { - - CallInst *callInst = nullptr; - if ((callInst = dyn_cast(&IN))) { - - Function *Callee = callInst->getCalledFunction(); - if (!Callee || Callee->size() < function_minimum_size) - continue; - else { - - has_calls = 1; - break; - - } - - } - - } - - } - - // if yes we store a context ID for this function in the global var - if (has_calls) { - - ConstantInt *NewCtx = ConstantInt::get(Int32Ty, genLabel()); - StoreInst * StoreCtx = IRB.CreateStore(NewCtx, AFLContext); - StoreCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } - - } // END of ctx_str - - if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; } - - if (PI == PE) { - - cur_loc = genLabel(); - L = ConstantInt::get(Int32Ty, cur_loc); - - } else { - - auto *PN = PHINode::Create(Int32Ty, 0, "", &*BB.begin()); - DenseMap PredMap; - for (auto PI = pred_begin(&BB), PE = pred_end(&BB); PI != PE; ++PI) { - - BasicBlock *PBB = *PI; - auto It = PredMap.insert({PBB, genLabel()}); - unsigned Label = It.first->second; - cur_loc = Label; - PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB); - - } - - L = PN; - - } - - /* Load prev_loc */ - LoadInst *PrevLoc = IRB.CreateLoad(AFLPrevLoc); - PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - Value *PrevLocTrans; - -#ifdef AFL_HAVE_VECTOR_INTRINSICS - /* "For efficiency, we propose to hash the tuple as a key into the - hit_count map as (prev_block_trans << 1) ^ curr_block_trans, where - prev_block_trans = (block_trans_1 ^ ... ^ block_trans_(n-1)" */ - - if (ngram_size) - PrevLocTrans = - IRB.CreateZExt(IRB.CreateXorReduce(PrevLoc), IRB.getInt32Ty()); - else -#endif - PrevLocTrans = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty()); - - if (ctx_str) - PrevLocTrans = - IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty); - - /* Load SHM pointer */ - LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); - MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - Value *MapPtrIdx; -#ifdef AFL_HAVE_VECTOR_INTRINSICS - if (ngram_size) - MapPtrIdx = IRB.CreateGEP( - MapPtr, IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, L), Int32Ty)); - else -#endif - MapPtrIdx = IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocTrans, L)); - - /* Update bitmap */ - LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); - Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - Value *Incr = IRB.CreateAdd(Counter, One); - -#if LLVM_VERSION_MAJOR < 9 - if (neverZero_counters_str != - NULL) // with llvm 9 we make this the default as the bug in llvm is - // then fixed -#else - if (!skip_nozero) -#endif - { - - /* hexcoder: Realize a counter that skips zero during overflow. - * Once this counter reaches its maximum value, it next increments to - * 1 - * - * Instead of - * Counter + 1 -> Counter - * we inject now this - * Counter + 1 -> {Counter, OverflowFlag} - * Counter + OverflowFlag -> Counter - */ - auto cf = IRB.CreateICmpEQ(Incr, Zero); - auto carry = IRB.CreateZExt(cf, Int8Ty); - Incr = IRB.CreateAdd(Incr, carry); - - } - - IRB.CreateStore(Incr, MapPtrIdx) - ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - if (ctx_str && has_calls) { - - // in CTX mode we have to restore the original context for the - // caller - she might be calling other functions which need the - // correct CTX - Instruction *Inst = BB.getTerminator(); - if (isa(Inst) || isa(Inst)) { - - IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); - RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } - - } - - total_instr++; - - } - - } - - if (!be_quiet) { - - char modeline[100]; - snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", - getenv("AFL_HARDEN") ? "hardened" : "non-hardened", - getenv("AFL_USE_ASAN") ? ", ASAN" : "", - getenv("AFL_USE_MSAN") ? ", MSAN" : "", - getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", - getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); - - OKF("Instrumented %u locations (%llu, %llu) (%s mode)\n", total_instr, - total_rs, total_hs, modeline); - - } - - return false; - - } - -}; // end of struct InsTrim - -} // end of anonymous namespace - -char InsTrim::ID = 0; - -static void registerAFLPass(const PassManagerBuilder &, - legacy::PassManagerBase &PM) { - - PM.add(new InsTrim()); - -} - -static RegisterStandardPasses RegisterAFLPass( - PassManagerBuilder::EP_OptimizerLast, registerAFLPass); - -static RegisterStandardPasses RegisterAFLPass0( - PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass); - diff --git a/llvm_mode/Makefile b/llvm_mode/Makefile deleted file mode 100644 index 3666a74d..00000000 --- a/llvm_mode/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - @gmake all || echo please install GNUmake diff --git a/llvm_mode/MarkNodes.cc b/llvm_mode/MarkNodes.cc deleted file mode 100644 index 20a7df35..00000000 --- a/llvm_mode/MarkNodes.cc +++ /dev/null @@ -1,481 +0,0 @@ -#include -#include -#include -#include -#include - -#include "llvm/Config/llvm-config.h" -#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5 -typedef long double max_align_t; -#endif - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/IR/BasicBlock.h" -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) - #include "llvm/IR/CFG.h" -#else - #include "llvm/Support/CFG.h" -#endif -#include "llvm/IR/Constants.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" - -using namespace llvm; - -DenseMap LMap; -std::vector Blocks; -std::set Marked, Markabove; -std::vector > Succs, Preds; - -void reset() { - - LMap.clear(); - Blocks.clear(); - Marked.clear(); - Markabove.clear(); - -} - -uint32_t start_point; - -void labelEachBlock(Function *F) { - - // Fake single endpoint; - LMap[NULL] = Blocks.size(); - Blocks.push_back(NULL); - - // Assign the unique LabelID to each block; - for (auto I = F->begin(), E = F->end(); I != E; ++I) { - - BasicBlock *BB = &*I; - LMap[BB] = Blocks.size(); - Blocks.push_back(BB); - - } - - start_point = LMap[&F->getEntryBlock()]; - -} - -void buildCFG(Function *F) { - - Succs.resize(Blocks.size()); - Preds.resize(Blocks.size()); - for (size_t i = 0; i < Succs.size(); i++) { - - Succs[i].clear(); - Preds[i].clear(); - - } - - for (auto S = F->begin(), E = F->end(); S != E; ++S) { - - BasicBlock *BB = &*S; - uint32_t MyID = LMap[BB]; - - for (auto I = succ_begin(BB), E = succ_end(BB); I != E; ++I) { - - Succs[MyID].push_back(LMap[*I]); - - } - - } - -} - -std::vector > tSuccs; -std::vector tag, indfs; - -void DFStree(size_t now_id) { - - if (tag[now_id]) return; - tag[now_id] = true; - indfs[now_id] = true; - for (auto succ : tSuccs[now_id]) { - - if (tag[succ] and indfs[succ]) { - - Marked.insert(succ); - Markabove.insert(succ); - continue; - - } - - Succs[now_id].push_back(succ); - Preds[succ].push_back(now_id); - DFStree(succ); - - } - - indfs[now_id] = false; - -} - -void turnCFGintoDAG() { - - tSuccs = Succs; - tag.resize(Blocks.size()); - indfs.resize(Blocks.size()); - for (size_t i = 0; i < Blocks.size(); ++i) { - - Succs[i].clear(); - tag[i] = false; - indfs[i] = false; - - } - - DFStree(start_point); - for (size_t i = 0; i < Blocks.size(); ++i) - if (Succs[i].empty()) { - - Succs[i].push_back(0); - Preds[0].push_back(i); - - } - -} - -uint32_t timeStamp; -namespace DominatorTree { - -std::vector > cov; -std::vector dfn, nfd, par, sdom, idom, mom, mn; - -bool Compare(uint32_t u, uint32_t v) { - - return dfn[u] < dfn[v]; - -} - -uint32_t eval(uint32_t u) { - - if (mom[u] == u) return u; - uint32_t res = eval(mom[u]); - if (Compare(sdom[mn[mom[u]]], sdom[mn[u]])) { mn[u] = mn[mom[u]]; } - return mom[u] = res; - -} - -void DFS(uint32_t now) { - - timeStamp += 1; - dfn[now] = timeStamp; - nfd[timeStamp - 1] = now; - for (auto succ : Succs[now]) { - - if (dfn[succ] == 0) { - - par[succ] = now; - DFS(succ); - - } - - } - -} - -void DominatorTree() { - - if (Blocks.empty()) return; - uint32_t s = start_point; - - // Initialization - mn.resize(Blocks.size()); - cov.resize(Blocks.size()); - dfn.resize(Blocks.size()); - nfd.resize(Blocks.size()); - par.resize(Blocks.size()); - mom.resize(Blocks.size()); - sdom.resize(Blocks.size()); - idom.resize(Blocks.size()); - - for (uint32_t i = 0; i < Blocks.size(); i++) { - - dfn[i] = 0; - nfd[i] = Blocks.size(); - cov[i].clear(); - idom[i] = mom[i] = mn[i] = sdom[i] = i; - - } - - timeStamp = 0; - DFS(s); - - for (uint32_t i = Blocks.size() - 1; i >= 1u; i--) { - - uint32_t now = nfd[i]; - if (now == Blocks.size()) { continue; } - for (uint32_t pre : Preds[now]) { - - if (dfn[pre]) { - - eval(pre); - if (Compare(sdom[mn[pre]], sdom[now])) { sdom[now] = sdom[mn[pre]]; } - - } - - } - - cov[sdom[now]].push_back(now); - mom[now] = par[now]; - for (uint32_t x : cov[par[now]]) { - - eval(x); - if (Compare(sdom[mn[x]], par[now])) { - - idom[x] = mn[x]; - - } else { - - idom[x] = par[now]; - - } - - } - - } - - for (uint32_t i = 1; i < Blocks.size(); i += 1) { - - uint32_t now = nfd[i]; - if (now == Blocks.size()) { continue; } - if (idom[now] != sdom[now]) idom[now] = idom[idom[now]]; - - } - -} - -} // namespace DominatorTree - -std::vector Visited, InStack; -std::vector TopoOrder, InDeg; -std::vector > t_Succ, t_Pred; - -void Go(uint32_t now, uint32_t tt) { - - if (now == tt) return; - Visited[now] = InStack[now] = timeStamp; - - for (uint32_t nxt : Succs[now]) { - - if (Visited[nxt] == timeStamp and InStack[nxt] == timeStamp) { - - Marked.insert(nxt); - - } - - t_Succ[now].push_back(nxt); - t_Pred[nxt].push_back(now); - InDeg[nxt] += 1; - if (Visited[nxt] == timeStamp) { continue; } - Go(nxt, tt); - - } - - InStack[now] = 0; - -} - -void TopologicalSort(uint32_t ss, uint32_t tt) { - - timeStamp += 1; - - Go(ss, tt); - - TopoOrder.clear(); - std::queue wait; - wait.push(ss); - while (not wait.empty()) { - - uint32_t now = wait.front(); - wait.pop(); - TopoOrder.push_back(now); - for (uint32_t nxt : t_Succ[now]) { - - InDeg[nxt] -= 1; - if (InDeg[nxt] == 0u) { wait.push(nxt); } - - } - - } - -} - -std::vector > NextMarked; -bool Indistinguish(uint32_t node1, uint32_t node2) { - - if (NextMarked[node1].size() > NextMarked[node2].size()) { - - uint32_t _swap = node1; - node1 = node2; - node2 = _swap; - - } - - for (uint32_t x : NextMarked[node1]) { - - if (NextMarked[node2].find(x) != NextMarked[node2].end()) { return true; } - - } - - return false; - -} - -void MakeUniq(uint32_t now) { - - bool StopFlag = false; - if (Marked.find(now) == Marked.end()) { - - for (uint32_t pred1 : t_Pred[now]) { - - for (uint32_t pred2 : t_Pred[now]) { - - if (pred1 == pred2) continue; - if (Indistinguish(pred1, pred2)) { - - Marked.insert(now); - StopFlag = true; - break; - - } - - } - - if (StopFlag) { break; } - - } - - } - - if (Marked.find(now) != Marked.end()) { - - NextMarked[now].insert(now); - - } else { - - for (uint32_t pred : t_Pred[now]) { - - for (uint32_t x : NextMarked[pred]) { - - NextMarked[now].insert(x); - - } - - } - - } - -} - -bool MarkSubGraph(uint32_t ss, uint32_t tt) { - - TopologicalSort(ss, tt); - if (TopoOrder.empty()) return false; - - for (uint32_t i : TopoOrder) { - - NextMarked[i].clear(); - - } - - NextMarked[TopoOrder[0]].insert(TopoOrder[0]); - for (uint32_t i = 1; i < TopoOrder.size(); i += 1) { - - MakeUniq(TopoOrder[i]); - - } - - // Check if there is an empty path. - if (NextMarked[tt].count(TopoOrder[0]) > 0) return true; - return false; - -} - -void MarkVertice() { - - uint32_t s = start_point; - - InDeg.resize(Blocks.size()); - Visited.resize(Blocks.size()); - InStack.resize(Blocks.size()); - t_Succ.resize(Blocks.size()); - t_Pred.resize(Blocks.size()); - NextMarked.resize(Blocks.size()); - - for (uint32_t i = 0; i < Blocks.size(); i += 1) { - - Visited[i] = InStack[i] = InDeg[i] = 0; - t_Succ[i].clear(); - t_Pred[i].clear(); - - } - - timeStamp = 0; - uint32_t t = 0; - bool emptyPathExists = true; - - while (s != t) { - - emptyPathExists &= MarkSubGraph(DominatorTree::idom[t], t); - t = DominatorTree::idom[t]; - - } - - if (emptyPathExists) { - - // Mark all exit blocks to catch the empty path. - Marked.insert(t_Pred[0].begin(), t_Pred[0].end()); - - } - -} - -// return {marked nodes} -std::pair, std::vector > markNodes( - Function *F) { - - assert(F->size() > 0 && "Function can not be empty"); - - reset(); - labelEachBlock(F); - buildCFG(F); - turnCFGintoDAG(); - DominatorTree::DominatorTree(); - MarkVertice(); - - std::vector Result, ResultAbove; - for (uint32_t x : Markabove) { - - auto it = Marked.find(x); - if (it != Marked.end()) Marked.erase(it); - if (x) ResultAbove.push_back(Blocks[x]); - - } - - for (uint32_t x : Marked) { - - if (x == 0) { - - continue; - - } else { - - Result.push_back(Blocks[x]); - - } - - } - - return {Result, ResultAbove}; - -} - diff --git a/llvm_mode/MarkNodes.h b/llvm_mode/MarkNodes.h deleted file mode 100644 index 8ddc978d..00000000 --- a/llvm_mode/MarkNodes.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __MARK_NODES__ -#define __MARK_NODES__ - -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Function.h" -#include - -std::pair, std::vector> -markNodes(llvm::Function *F); - -#endif - diff --git a/llvm_mode/README.cmplog.md b/llvm_mode/README.cmplog.md deleted file mode 100644 index 7f426ec8..00000000 --- a/llvm_mode/README.cmplog.md +++ /dev/null @@ -1,42 +0,0 @@ -# CmpLog instrumentation - -The CmpLog instrumentation enables the logging of the comparisons operands in a -shared memory. - -These values can be used by various mutators built on top of it. -At the moment we support the RedQueen mutator (input-2-state instructions only). - -## Build - -To use CmpLog, you have to build two versions of the instrumented target -program. - -The first version is built using the regular AFL++ instrumentation. - -The second one, the CmpLog binary, with setting AFL_LLVM_CMPLOG during the compilation. - -For example: - -``` -./configure --cc=~/path/to/afl-clang-fast -make -cp ./program ./program.afl -make clean -export AFL_LLVM_CMPLOG=1 -./configure --cc=~/path/to/afl-clang-fast -make -cp ./program ./program.cmplog -``` - -## Use - -AFL++ has the new -c option that can be used to specify a CmpLog binary (the second -build). - -For example: - -``` -afl-fuzz -i input -o output -c ./program.cmplog -m none -- ./program.afl @@ -``` - -Be careful to use -m none because CmpLog maps a lot of pages. diff --git a/llvm_mode/README.ctx.md b/llvm_mode/README.ctx.md deleted file mode 100644 index 14255313..00000000 --- a/llvm_mode/README.ctx.md +++ /dev/null @@ -1,22 +0,0 @@ -# AFL Context Sensitive Branch Coverage - -## What is this? - -This is an LLVM-based implementation of the context sensitive branch coverage. - -Basically every function gets it's own ID and that ID is combined with the -edges of the called functions. - -So if both function A and function B call a function C, the coverage -collected in C will be different. - -In math the coverage is collected as follows: -`map[current_location_ID ^ previous_location_ID >> 1 ^ previous_callee_ID] += 1` - -## Usage - -Set the `AFL_LLVM_INSTRUMENT=CTX` or `AFL_LLVM_CTX=1` environment variable. - -It is highly recommended to increase the MAP_SIZE_POW2 definition in -config.h to at least 18 and maybe up to 20 for this as otherwise too -many map collisions occur. diff --git a/llvm_mode/README.instrim.md b/llvm_mode/README.instrim.md deleted file mode 100644 index 7758091b..00000000 --- a/llvm_mode/README.instrim.md +++ /dev/null @@ -1,25 +0,0 @@ -# InsTrim - -InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing - -## Introduction - -InsTrim uses CFG and markers to instrument just what is necessary in the -binary in llvm_mode. It is about 10-15% faster without disadvantages. -It requires at least llvm version 3.8.0. - -## Usage - -Set the environment variable `AFL_LLVM_INSTRUMENT=CFG` or `AFL_LLVM_INSTRIM=1` -during compilation of the target. - -There is also an advanced mode which instruments loops in a way so that -afl-fuzz can see which loop path has been selected but not being able to -see how often the loop has been rerun. -This again is a tradeoff for speed for less path information. -To enable this mode set `AFL_LLVM_INSTRIM_LOOPHEAD=1`. - -## Background - -The paper: [InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing] -(https://www.ndss-symposium.org/wp-content/uploads/2018/07/bar2018_14_Hsu_paper.pdf) diff --git a/llvm_mode/README.instrument_list.md b/llvm_mode/README.instrument_list.md deleted file mode 100644 index 1fc06414..00000000 --- a/llvm_mode/README.instrument_list.md +++ /dev/null @@ -1,86 +0,0 @@ -# Using afl++ with partial instrumentation - - This file describes how you can selectively instrument only the source files - or functions that are interesting to you using the LLVM instrumentation - provided by afl++ - -## 1) Description and purpose - -When building and testing complex programs where only a part of the program is -the fuzzing target, it often helps to only instrument the necessary parts of -the program, leaving the rest uninstrumented. This helps to focus the fuzzer -on the important parts of the program, avoiding undesired noise and -disturbance by uninteresting code being exercised. - -For this purpose, a "partial instrumentation" support en par with llvm sancov -is provided by afl++ that allows you to specify on a source file and function -level which function should be compiled with or without instrumentation. - -Note: When using PCGUARD mode - and have llvm 12+ - you can use this instead: -https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation - -The llvm sancov list format is fully supported by afl++, however afl++ has -more flexibility. - -## 2) Building the LLVM module - -The new code is part of the existing afl++ LLVM module in the llvm_mode/ -subdirectory. There is nothing specifically to do :) - -## 3) How to use the partial instrumentation mode - -In order to build with partial instrumentation, you need to build with -afl-clang-fast/afl-clang-fast++ or afl-clang-lto/afl-clang-lto++. -The only required change is that you need to set either the environment variable -AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST set with a filename. - -That file then contains the filenames or functions that should be instrumented -(AFL_LLVM_ALLOWLIST) or should specifically NOT be instrumented (AFL_LLVM_DENYLIST). - -For matching, the function/filename that is being compiled must end in the -function/filename entry contained in this instrument file list (to avoid -breaking the matching when absolute paths are used during compilation). - -**NOTE:** In builds with optimization enabled functions might be inlined and would not match! - -For example if your source tree looks like this: -``` -project/ -project/feature_a/a1.cpp -project/feature_a/a2.cpp -project/feature_b/b1.cpp -project/feature_b/b2.cpp -``` - -and you only want to test feature_a, then create a instrument file list file containing: -``` -feature_a/a1.cpp -feature_a/a2.cpp -``` - -However if the instrument file list file contains only this, it works as well: -``` -a1.cpp -a2.cpp -``` -but it might lead to files being unwantedly instrumented if the same filename -exists somewhere else in the project directories. - -You can also specify function names. Note that for C++ the function names -must be mangled to match! - -afl++ is able to identify if an entry is a filename or a function. -However if you want to be sure (and compliant to the sancov allow/blocklist -format), you can specify source file entries like this: -``` -src: *malloc.c -``` -and function entries like this: -``` -fun: MallocFoo -``` -Note that whitespace is ignored and comments (`# foo`) are supported. - -## 4) UNIX-style pattern matching -You can add UNIX-style pattern matching in the the instrument file list entries. -See `man fnmatch` for the syntax. We do not set any of the `fnmatch` flags. diff --git a/llvm_mode/README.laf-intel.md b/llvm_mode/README.laf-intel.md deleted file mode 100644 index f63ab2bb..00000000 --- a/llvm_mode/README.laf-intel.md +++ /dev/null @@ -1,42 +0,0 @@ -# laf-intel instrumentation - -## Usage - -By default these passes will not run when you compile programs using -afl-clang-fast. Hence, you can use AFL as usual. -To enable the passes you must set environment variables before you -compile the target project. - -The following options exist: - -`export AFL_LLVM_LAF_SPLIT_SWITCHES=1` - -Enables the split-switches pass. - -`export AFL_LLVM_LAF_TRANSFORM_COMPARES=1` - -Enables the transform-compares pass (strcmp, memcmp, strncmp, -strcasecmp, strncasecmp). - -`export AFL_LLVM_LAF_SPLIT_COMPARES=1` - -Enables the split-compares pass. -By default it will -1. simplify operators >= (and <=) into chains of > (<) and == comparisons -2. change signed integer comparisons to a chain of sign-only comparison -and unsigned comparisons -3. split all unsigned integer comparisons with bit widths of -64, 32 or 16 bits to chains of 8 bits comparisons. - -You can change the behaviour of the last step by setting -`export AFL_LLVM_LAF_SPLIT_COMPARES_BITW=`, where -bit_width may be 64, 32 or 16. - -A new experimental feature is splitting floating point comparisons into a -series of sign, exponent and mantissa comparisons followed by splitting each -of them into 8 bit comparisons when necessary. -It is activated with the `AFL_LLVM_LAF_SPLIT_FLOATS` setting. -Note that setting this automatically activates `AFL_LLVM_LAF_SPLIT_COMPARES` - -You can also set `AFL_LLVM_LAF_ALL` and have all of the above enabled :-) - diff --git a/llvm_mode/README.lto.md b/llvm_mode/README.lto.md deleted file mode 100644 index 9046c5a8..00000000 --- a/llvm_mode/README.lto.md +++ /dev/null @@ -1,293 +0,0 @@ -# afl-clang-lto - collision free instrumentation at link time - -## TLDR; - -This version requires a current llvm 11+ compiled from the github master. - -1. Use afl-clang-lto/afl-clang-lto++ because it is faster and gives better - coverage than anything else that is out there in the AFL world - -2. You can use it together with llvm_mode: laf-intel and the instrument file listing - features and can be combined with cmplog/Redqueen - -3. It only works with llvm 11+ - -4. AUTODICTIONARY feature! see below - -5. If any problems arise be sure to set `AR=llvm-ar RANLIB=llvm-ranlib`. - Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`. - -## Introduction and problem description - -A big issue with how afl/afl++ works is that the basic block IDs that are -set during compilation are random - and hence naturally the larger the number -of instrumented locations, the higher the number of edge collisions are in the -map. This can result in not discovering new paths and therefore degrade the -efficiency of the fuzzing process. - -*This issue is underestimated in the fuzzing community!* -With a 2^16 = 64kb standard map at already 256 instrumented blocks there is -on average one collision. On average a target has 10.000 to 50.000 -instrumented blocks hence the real collisions are between 750-18.000! - -To reach a solution that prevents any collisions took several approaches -and many dead ends until we got to this: - - * We instrument at link time when we have all files pre-compiled - * To instrument at link time we compile in LTO (link time optimization) mode - * Our compiler (afl-clang-lto/afl-clang-lto++) takes care of setting the - correct LTO options and runs our own afl-ld linker instead of the system - linker - * The LLVM linker collects all LTO files to link and instruments them so that - we have non-colliding edge overage - * We use a new (for afl) edge coverage - which is the same as in llvm - -fsanitize=coverage edge coverage mode :) - -The result: - * 10-25% speed gain compared to llvm_mode - * guaranteed non-colliding edge coverage :-) - * The compile time especially for binaries to an instrumented library can be - much longer - -Example build output from a libtiff build: -``` -libtool: link: afl-clang-lto -g -O2 -Wall -W -o thumbnail thumbnail.o ../libtiff/.libs/libtiff.a ../port/.libs/libport.a -llzma -ljbig -ljpeg -lz -lm -afl-clang-lto++2.63d by Marc "vanHauser" Heuse in mode LTO -afl-llvm-lto++2.63d by Marc "vanHauser" Heuse -AUTODICTIONARY: 11 strings found -[+] Instrumented 12071 locations with no collisions (on average 1046 collisions would be in afl-gcc/afl-clang-fast) (non-hardened mode). -``` - -## Getting llvm 11+ - -### Installing llvm from the llvm repository (version 11) - -Installing the llvm snapshot builds is easy and mostly painless: - -In the follow line change `NAME` for your Debian or Ubuntu release name -(e.g. buster, focal, eon, etc.): -``` -echo deb http://apt.llvm.org/NAME/ llvm-toolchain-NAME NAME >> /etc/apt/sources.list -``` -then add the pgp key of llvm and install the packages: -``` -wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - -apt-get update && apt-get upgrade -y -apt-get install -y clang-11 clang-tools-11 libc++1-11 libc++-11-dev \ - libc++abi1-11 libc++abi-11-dev libclang1-11 libclang-11-dev \ - libclang-common-11-dev libclang-cpp11 libclang-cpp11-dev liblld-11 \ - liblld-11-dev liblldb-11 liblldb-11-dev libllvm11 libomp-11-dev \ - libomp5-11 lld-11 lldb-11 llvm-11 llvm-11-dev llvm-11-runtime llvm-11-tools -``` - -### Building llvm yourself (version 12) - -Building llvm from github takes quite some long time and is not painless: -``` -sudo apt install binutils-dev # this is *essential*! -git clone https://github.com/llvm/llvm-project -cd llvm-project -mkdir build -cd build -cmake -DLLVM_ENABLE_PROJECTS='clang;clang-tools-extra;compiler-rt;libclc;libcxx;libcxxabi;libunwind;lld' -DCMAKE_BUILD_TYPE=Release -DLLVM_BINUTILS_INCDIR=/usr/include/ ../llvm/ -make -j $(nproc) -export PATH=`pwd`/bin:$PATH -export LLVM_CONFIG=`pwd`/bin/llvm-config -cd /path/to/AFLplusplus/ -make -cd llvm_mode -make -cd .. -make install -``` - -## How to use afl-clang-lto - -Just use afl-clang-lto like you did with afl-clang-fast or afl-gcc. - -Also the instrument file listing (AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST -> [README.instrument_list.md](README.instrument_list.md)) and -laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work. - -Example: -``` -CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure -make -``` - -NOTE: some targets also need to set the linker, try both `afl-clang-lto` and -`afl-ld-lto` for this for `LD=` for `configure`. - -## AUTODICTIONARY feature - -While compiling, automatically a dictionary based on string comparisons is -generated put into the target binary. This dictionary is transfered to afl-fuzz -on start. This improves coverage statistically by 5-10% :) - -## Fixed memory map - -To speed up fuzzing, it is possible to set a fixed shared memory map. -Recommened is the value 0x10000. -In most cases this will work without any problems. However if a target uses -early constructors, ifuncs or a deferred forkserver this can crash the target. -On unusual operating systems/processors/kernels or weird libraries this might -fail so to change the fixed address at compile time set -AFL_LLVM_MAP_ADDR with a better value (a value of 0 or empty sets the map address -to be dynamic - the original afl way, which is slower). - -## Document edge IDs - -Setting `export AFL_LLVM_DOCUMENT_IDS=file` will document to a file which edge -ID was given to which function. This helps to identify functions with variable -bytes or which functions were touched by an input. - -## Solving difficult targets - -Some targets are difficult because the configure script does unusual stuff that -is unexpected for afl. See the next chapter `Potential issues` how to solve -these. - -### Example: ffmpeg - -An example of a hard to solve target is ffmpeg. Here is how to successfully -instrument it: - -1. Get and extract the current ffmpeg and change to it's directory - -2. Running configure with --cc=clang fails and various other items will fail - when compiling, so we have to trick configure: - -``` -./configure --enable-lto --disable-shared --disable-inline-asm -``` - -3. Now the configuration is done - and we edit the settings in `./ffbuild/config.mak` - (-: the original line, +: what to change it into): -``` --CC=gcc -+CC=afl-clang-lto --CXX=g++ -+CXX=afl-clang-lto++ --AS=gcc -+AS=llvm-as --LD=gcc -+LD=afl-clang-lto++ --DEPCC=gcc -+DEPCC=afl-clang-lto --DEPAS=gcc -+DEPAS=afl-clang-lto++ --AR=ar -+AR=llvm-ar --AR_CMD=ar -+AR_CMD=llvm-ar --NM_CMD=nm -g -+NM_CMD=llvm-nm -g --RANLIB=ranlib -D -+RANLIB=llvm-ranlib -D -``` - -4. Then type make, wait for a long time and you are done :) - -### Example: WebKit jsc - -Building jsc is difficult as the build script has bugs. - -1. checkout Webkit: -``` -svn checkout https://svn.webkit.org/repository/webkit/trunk WebKit -cd WebKit -``` - -2. Fix the build environment: -``` -mkdir -p WebKitBuild/Release -cd WebKitBuild/Release -ln -s ../../../../../usr/bin/llvm-ar-12 llvm-ar-12 -ln -s ../../../../../usr/bin/llvm-ranlib-12 llvm-ranlib-12 -cd ../.. -``` - -3. Build :) - -``` -Tools/Scripts/build-jsc --jsc-only --cli --cmakeargs="-DCMAKE_AR='llvm-ar-12' -DCMAKE_RANLIB='llvm-ranlib-12' -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_CC_FLAGS='-O3 -lrt' -DCMAKE_CXX_FLAGS='-O3 -lrt' -DIMPORTED_LOCATION='/lib/x86_64-linux-gnu/' -DCMAKE_CC=afl-clang-lto -DCMAKE_CXX=afl-clang-lto++ -DENABLE_STATIC_JSC=ON" -``` - -## Potential issues - -### compiling libraries fails - -If you see this message: -``` -/bin/ld: libfoo.a: error adding symbols: archive has no index; run ranlib to add one -``` -This is because usually gnu gcc ranlib is being called which cannot deal with clang LTO files. -The solution is simple: when you ./configure you have also have to set RANLIB=llvm-ranlib and AR=llvm-ar - -Solution: -``` -AR=llvm-ar RANLIB=llvm-ranlib CC=afl-clang-lto CXX=afl-clang-lto++ ./configure --disable-shared -``` -and on some target you have to to AR=/RANLIB= even for make as the configure script does not save it. -Other targets ignore environment variables and need the parameters set via -`./configure --cc=... --cxx= --ranlib= ...` etc. (I am looking at you ffmpeg!). - - -If you see this message -``` -assembler command failed ... -``` -then try setting `llvm-as` for configure: -``` -AS=llvm-as ... -``` - -### compiling programs still fail - -afl-clang-lto is still work in progress. - -Known issues: - * Anything that llvm 11+ cannot compile, afl-clang-lto can not compile either - obviously - * Anything that does not compile with LTO, afl-clang-lto can not compile either - obviously - -Hence if building a target with afl-clang-lto fails try to build it with llvm12 -and LTO enabled (`CC=clang-12` `CXX=clang++-12` `CFLAGS=-flto=full` and -`CXXFLAGS=-flto=full`). - -If this succeeeds then there is an issue with afl-clang-lto. Please report at -[https://github.com/AFLplusplus/AFLplusplus/issues/226](https://github.com/AFLplusplus/AFLplusplus/issues/226) - -Even some targets where clang-12 fails can be build if the fail is just in -`./configure`, see `Solving difficult targets` above. - -## History - -This was originally envisioned by hexcoder- in Summer 2019, however we saw no -way to create a pass that is run at link time - although there is a option -for this in the PassManager: EP_FullLinkTimeOptimizationLast -("Fun" info - nobody knows what this is doing. And the developer who -implemented this didn't respond to emails.) - -In December came then the idea to implement this as a pass that is run via -the llvm "opt" program, which is performed via an own linker that afterwards -calls the real linker. -This was first implemented in January and work ... kinda. -The LTO time instrumentation worked, however the "how" the basic blocks were -instrumented was a problem, as reducing duplicates turned out to be very, -very difficult with a program that has so many paths and therefore so many -dependencies. At lot of strategies were implemented - and failed. -And then sat solvers were tried, but with over 10.000 variables that turned -out to be a dead-end too. - -The final idea to solve this came from domenukk who proposed to insert a block -into an edge and then just use incremental counters ... and this worked! -After some trials and errors to implement this vanhauser-thc found out that -there is actually an llvm function for this: SplitEdge() :-) - -Still more problems came up though as this only works without bugs from -llvm 9 onwards, and with high optimization the link optimization ruins -the instrumented control flow graph. - -This is all now fixed with llvm 11+. The llvm's own linker is now able to -load passes and this bypasses all problems we had. - -Happy end :) diff --git a/llvm_mode/README.md b/llvm_mode/README.md deleted file mode 100644 index f23d7150..00000000 --- a/llvm_mode/README.md +++ /dev/null @@ -1,186 +0,0 @@ -# Fast LLVM-based instrumentation for afl-fuzz - - (See [../README](../README.md) for the general instruction manual.) - - (See [../gcc_plugin/README](../gcc_plugin/README.md) for the GCC-based instrumentation.) - -## 1) Introduction - -! llvm_mode works with llvm versions 3.4 up to 12 ! - -The code in this directory allows you to instrument programs for AFL using -true compiler-level instrumentation, instead of the more crude -assembly-level rewriting approach taken by afl-gcc and afl-clang. This has -several interesting properties: - - - The compiler can make many optimizations that are hard to pull off when - manually inserting assembly. As a result, some slow, CPU-bound programs will - run up to around 2x faster. - - The gains are less pronounced for fast binaries, where the speed is limited - chiefly by the cost of creating new processes. In such cases, the gain will - probably stay within 10%. - - - The instrumentation is CPU-independent. At least in principle, you should - be able to rely on it to fuzz programs on non-x86 architectures (after - building afl-fuzz with AFL_NO_X86=1). - - - The instrumentation can cope a bit better with multi-threaded targets. - - - Because the feature relies on the internals of LLVM, it is clang-specific - and will *not* work with GCC (see ../gcc_plugin/ for an alternative once - it is available). - -Once this implementation is shown to be sufficiently robust and portable, it -will probably replace afl-clang. For now, it can be built separately and -co-exists with the original code. - -The idea and much of the intial implementation came from Laszlo Szekeres. - -## 2a) How to use this - short - -Set the `LLVM_CONFIG` variable to the clang version you want to use, e.g. -``` -LLVM_CONFIG=llvm-config-9 make -``` -In case you have your own compiled llvm version specify the full path: -``` -LLVM_CONFIG=~/llvm-project/build/bin/llvm-config make -``` -If you try to use a new llvm version on an old Linux this can fail because of -old c++ libraries. In this case usually switching to gcc/g++ to compile -llvm_mode will work: -``` -LLVM_CONFIG=llvm-config-7 REAL_CC=gcc REAL_CXX=g++ make -``` -It is highly recommended to use the newest clang version you can put your -hands on :) - -Then look at [README.persistent_mode.md](README.persistent_mode.md). - -## 2b) How to use this - long - -In order to leverage this mechanism, you need to have clang installed on your -system. You should also make sure that the llvm-config tool is in your path -(or pointed to via LLVM_CONFIG in the environment). - -Note that if you have several LLVM versions installed, pointing LLVM_CONFIG -to the version you want to use will switch compiling to this specific -version - if you installation is set up correctly :-) - -Unfortunately, some systems that do have clang come without llvm-config or the -LLVM development headers; one example of this is FreeBSD. FreeBSD users will -also run into problems with clang being built statically and not being able to -load modules (you'll see "Service unavailable" when loading afl-llvm-pass.so). - -To solve all your problems, you can grab pre-built binaries for your OS from: - - http://llvm.org/releases/download.html - -...and then put the bin/ directory from the tarball at the beginning of your -$PATH when compiling the feature and building packages later on. You don't need -to be root for that. - -To build the instrumentation itself, type 'make'. This will generate binaries -called afl-clang-fast and afl-clang-fast++ in the parent directory. Once this -is done, you can instrument third-party code in a way similar to the standard -operating mode of AFL, e.g.: - -``` - CC=/path/to/afl/afl-clang-fast ./configure [...options...] - make -``` - -Be sure to also include CXX set to afl-clang-fast++ for C++ code. - -The tool honors roughly the same environmental variables as afl-gcc (see -[docs/env_variables.md](../docs/env_variables.md)). This includes AFL_USE_ASAN, -AFL_HARDEN, and AFL_DONT_OPTIMIZE. However AFL_INST_RATIO is not honored -as it does not serve a good purpose with the more effective instrim CFG -analysis. - -Note: if you want the LLVM helper to be installed on your system for all -users, you need to build it before issuing 'make install' in the parent -directory. - -## 3) Options - -Several options are present to make llvm_mode faster or help it rearrange -the code to make afl-fuzz path discovery easier. - -If you need just to instrument specific parts of the code, you can the instrument file list -which C/C++ files to actually instrument. See [README.instrument_list](README.instrument_list.md) - -For splitting memcmp, strncmp, etc. please see [README.laf-intel](README.laf-intel.md) - -Then there are different ways of instrumenting the target: - -1. There is an optimized instrumentation strategy that uses CFGs and -markers to just instrument what is needed. This increases speed by 10-15% -without any disadvantages -If you want to use this, set AFL_LLVM_INSTRUMENT=CFG or AFL_LLVM_INSTRIM=1 -See [README.instrim](README.instrim.md) - -2. An even better instrumentation strategy uses LTO and link time -instrumentation. Note that not all targets can compile in this mode, however -if it works it is the best option you can use. -Simply use afl-clang-lto/afl-clang-lto++ to use this option. -See [README.lto](README.lto.md) - -3. Alternativly you can choose a completely different coverage method: - -3a. N-GRAM coverage - which combines the previous visited edges with the -current one. This explodes the map but on the other hand has proven to be -effective for fuzzing. -See [README.ngram](README.ngram.md) - -3b. Context sensitive coverage - which combines the visited edges with an -individual caller ID (the function that called the current one) -[README.ctx](README.ctx.md) - -Then - additionally to one of the instrumentation options above - there is -a very effective new instrumentation option called CmpLog as an alternative to -laf-intel that allow AFL++ to apply mutations similar to Redqueen. -See [README.cmplog](README.cmplog.md) - -Finally if your llvm version is 8 or lower, you can activate a mode that -prevents that a counter overflow result in a 0 value. This is good for -path discovery, but the llvm implementation for x86 for this functionality -is not optimal and was only fixed in llvm 9. -You can set this with AFL_LLVM_NOT_ZERO=1 -See [README.neverzero](README.neverzero.md) - -## 4) Snapshot feature - -To speed up fuzzing you can use a linux loadable kernel module which enables -a snapshot feature. -See [README.snapshot](README.snapshot.md) - -## 5) Gotchas, feedback, bugs - -This is an early-stage mechanism, so field reports are welcome. You can send bug -reports to . - -## 6) deferred initialization, persistent mode, shared memory fuzzing - -This is the most powerful and effective fuzzing you can do. -Please see [README.persistent_mode.md](README.persistent_mode.md) for a -full explanation. - -## 7) Bonus feature: 'trace-pc-guard' mode - -LLVM is shipping with a built-in execution tracing feature -that provides AFL with the necessary tracing data without the need to -post-process the assembly or install any compiler plugins. See: - - http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards - -If you have not an outdated compiler and want to give it a try, build -targets this way: - -``` -AFL_LLVM_INSTRUMENT=PCGUARD make -``` - -Note that this us currently the default, as it is the best mode. -If you have llvm 11+ and compiled afl-clang-lto - this is the only better mode. diff --git a/llvm_mode/README.neverzero.md b/llvm_mode/README.neverzero.md deleted file mode 100644 index 903e5bd3..00000000 --- a/llvm_mode/README.neverzero.md +++ /dev/null @@ -1,35 +0,0 @@ -# NeverZero counters for LLVM instrumentation - -## Usage - -In larger, complex or reiterative programs the counters that collect the edge -coverage can easily fill up and wrap around. -This is not that much of an issue - unless by chance it wraps just to a value -of zero when the program execution ends. -In this case afl-fuzz is not able to see that the edge has been accessed and -will ignore it. - -NeverZero prevents this behaviour. If a counter wraps, it jumps over the value -0 directly to a 1. This improves path discovery (by a very little amount) -at a very little cost (one instruction per edge). - -(The alternative of saturated counters has been tested also and proved to be -inferior in terms of path discovery.) - -This is implemented in afl-gcc, however for llvm_mode this is optional if -the llvm version is below 9 - as there is a perfomance bug that is only fixed -in version 9 and onwards. - -If you want to enable this for llvm versions below 9 then set - -``` -export AFL_LLVM_NOT_ZERO=1 -``` - -In case you are on llvm 9 or greater and you do not want this behaviour then -you can set: -``` -AFL_LLVM_SKIP_NEVERZERO=1 -``` -If the target does not have extensive loops or functions that are called -a lot then this can give a small performance boost. diff --git a/llvm_mode/README.ngram.md b/llvm_mode/README.ngram.md deleted file mode 100644 index de3ba432..00000000 --- a/llvm_mode/README.ngram.md +++ /dev/null @@ -1,28 +0,0 @@ -# AFL N-Gram Branch Coverage - -## Source - -This is an LLVM-based implementation of the n-gram branch coverage proposed in -the paper ["Be Sensitive and Collaborative: Analzying Impact of Coverage Metrics -in Greybox Fuzzing"](https://www.usenix.org/system/files/raid2019-wang-jinghan.pdf), -by Jinghan Wang, et. al. - -Note that the original implementation (available -[here](https://github.com/bitsecurerlab/afl-sensitive)) -is built on top of AFL's QEMU mode. -This is essentially a port that uses LLVM vectorized instructions to achieve -the same results when compiling source code. - -In math the branch coverage is performed as follows: -`map[current_location ^ prev_location[0] >> 1 ^ prev_location[1] >> 1 ^ ... up to n-1`] += 1` - -## Usage - -The size of `n` (i.e., the number of branches to remember) is an option -that is specified either in the `AFL_LLVM_INSTRUMENT=NGRAM-{value}` or the -`AFL_LLVM_NGRAM_SIZE` environment variable. -Good values are 2, 4 or 8, valid are 2-16. - -It is highly recommended to increase the MAP_SIZE_POW2 definition in -config.h to at least 18 and maybe up to 20 for this as otherwise too -many map collisions occur. diff --git a/llvm_mode/README.persistent_mode.md b/llvm_mode/README.persistent_mode.md deleted file mode 100644 index 7d2fd93b..00000000 --- a/llvm_mode/README.persistent_mode.md +++ /dev/null @@ -1,209 +0,0 @@ -# llvm_mode persistent mode - -## 1) Introduction - -The most effective way is to fuzz in persistent mode, as the speed can easily -be x10 or x20 times faster without any disadvanges. -*All professionel fuzzing is using this mode.* - -This requires that the target can be called in a (or several) function(s), -and that the state can be resetted so that multiple calls be be performed -without memory leaking and former runs having no impact on following runs -(this can be seen by the `stability` indicator in the `afl-fuzz` UI). - -Examples can be found in [examples/persistent_mode](../examples/persistent_mode). - -## 2) TLDR; - -Example `fuzz_target.c`: -``` -#include "what_you_need_for_your_target.h" - -__AFL_FUZZ_INIT(); - -main() { - -#ifdef __AFL_HAVE_MANUAL_CONTROL - __AFL_INIT(); -#endif - - unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT - - while (__AFL_LOOP(10000)) { - - int len = __AFL_FUZZ_TESTCASE_LEN; - if (len < 8) continue; // check for a required/useful minimum input length - - /* Setup function call, e.g. struct target *tmp = libtarget_init() */ - /* Call function to be fuzzed, e.g.: */ - target_function(buf, len); - /* Reset state. e.g. libtarget_free(tmp) */ - - } - - return 0; - -} -``` -And then compile: -``` -afl-clang-fast -o fuzz_target fuzz_target.c -lwhat_you_need_for_your_target -``` -And that is it! -The speed increase is usually x10 to x20. - -If you want to be able to compile the target without afl-clang-fast/lto then -add this just after the includes: - -``` -#ifndef __AFL_FUZZ_TESTCASE_LEN - ssize_t fuzz_len; - #define __AFL_FUZZ_TESTCASE_LEN fuzz_len - unsigned char fuzz_buf[1024000]; - #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf - #define __AFL_FUZZ_INIT() void sync(void); - #define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? - #define __AFL_INIT() sync() -#endif -``` - -## 3) deferred initialization - -AFL tries to optimize performance by executing the targeted binary just once, -stopping it just before main(), and then cloning this "main" process to get -a steady supply of targets to fuzz. - -Although this approach eliminates much of the OS-, linker- and libc-level -costs of executing the program, it does not always help with binaries that -perform other time-consuming initialization steps - say, parsing a large config -file before getting to the fuzzed data. - -In such cases, it's beneficial to initialize the forkserver a bit later, once -most of the initialization work is already done, but before the binary attempts -to read the fuzzed input and parse it; in some cases, this can offer a 10x+ -performance gain. You can implement delayed initialization in LLVM mode in a -fairly simple way. - -First, find a suitable location in the code where the delayed cloning can -take place. This needs to be done with *extreme* care to avoid breaking the -binary. In particular, the program will probably malfunction if you select -a location after: - - - The creation of any vital threads or child processes - since the forkserver - can't clone them easily. - - - The initialization of timers via setitimer() or equivalent calls. - - - The creation of temporary files, network sockets, offset-sensitive file - descriptors, and similar shared-state resources - but only provided that - their state meaningfully influences the behavior of the program later on. - - - Any access to the fuzzed input, including reading the metadata about its - size. - -With the location selected, add this code in the appropriate spot: - -```c -#ifdef __AFL_HAVE_MANUAL_CONTROL - __AFL_INIT(); -#endif -``` - -You don't need the #ifdef guards, but including them ensures that the program -will keep working normally when compiled with a tool other than afl-clang-fast. - -Finally, recompile the program with afl-clang-fast (afl-gcc or afl-clang will -*not* generate a deferred-initialization binary) - and you should be all set! - -*NOTE:* In the code between `main` and `__AFL_INIT()` should not be any code -run that is instrumented - otherwise a crash might occure. -In case this is useful (e.g. for expensive one time initialization) you can -try to do the following: - -Add after the includes: -``` -extern unsigned char *__afl_area_ptr; -#define MAX_DUMMY_SIZE 256000 - -__attribute__((constructor(1))) void __afl_protect(void) { -#ifdef MAP_FIXED_NOREPLACE - __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); - if ((uint64_t)__afl_area_ptr == -1) -#endif - __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - if ((uint64_t)__afl_area_ptr == -1) - __afl_area_ptr = (unsigned char*) mmap(NULL, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); -} - -``` -and just before `__AFL_INIT()`: -``` - munmap(__afl_area_ptr, MAX_DUMMY_SIZE); - __afl_area_ptr = NULL; -``` - -## 4) persistent mode - -Some libraries provide APIs that are stateless, or whose state can be reset in -between processing different input files. When such a reset is performed, a -single long-lived process can be reused to try out multiple test cases, -eliminating the need for repeated fork() calls and the associated OS overhead. - -The basic structure of the program that does this would be: - -```c - while (__AFL_LOOP(1000)) { - - /* Read input data. */ - /* Call library code to be fuzzed. */ - /* Reset state. */ - - } - - /* Exit normally */ -``` - -The numerical value specified within the loop controls the maximum number -of iterations before AFL will restart the process from scratch. This minimizes -the impact of memory leaks and similar glitches; 1000 is a good starting point, -and going much higher increases the likelihood of hiccups without giving you -any real performance benefits. - -A more detailed template is shown in ../examples/persistent_demo/. -Similarly to the previous mode, the feature works only with afl-clang-fast; #ifdef -guards can be used to suppress it when using other compilers. - -Note that as with the previous mode, the feature is easy to misuse; if you -do not fully reset the critical state, you may end up with false positives or -waste a whole lot of CPU power doing nothing useful at all. Be particularly -wary of memory leaks and of the state of file descriptors. - -PS. Because there are task switches still involved, the mode isn't as fast as -"pure" in-process fuzzing offered, say, by LLVM's LibFuzzer; but it is a lot -faster than the normal fork() model, and compared to in-process fuzzing, -should be a lot more robust. - -## 5) shared memory fuzzing - -You can speed up the fuzzing process even more by receiving the fuzzing data -via shared memory instead of stdin or files. -This is a further speed multiplier of about 2x. - -Setting this up is very easy: - -After the includes set the following macro: - -``` -__AFL_FUZZ_INIT(); -``` -Directly at the start of main - or if you are using the deferred forkserver -with `__AFL_INIT()` then *after* `__AFL_INIT? : -``` - unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; -``` - -Then as first line after the `__AFL_LOOP` while loop: -``` - int len = __AFL_FUZZ_TESTCASE_LEN; -``` -and that is all! diff --git a/llvm_mode/README.snapshot.md b/llvm_mode/README.snapshot.md deleted file mode 100644 index 9c12a8ba..00000000 --- a/llvm_mode/README.snapshot.md +++ /dev/null @@ -1,16 +0,0 @@ -# AFL++ snapshot feature - -Snapshotting is a feature that makes a snapshot from a process and then -restores it's state, which is faster then forking it again. - -All targets compiled with llvm_mode are automatically enabled for the -snapshot feature. - -To use the snapshot feature for fuzzing compile and load this kernel -module: [https://github.com/AFLplusplus/AFL-Snapshot-LKM](https://github.com/AFLplusplus/AFL-Snapshot-LKM) - -Note that is has little value for persistent (__AFL_LOOP) fuzzing. - -## Notes - -Snapshot does not work with multithreaded targets yet. Still in WIP, it is now usable only for single threaded applications. diff --git a/llvm_mode/SanitizerCoverageLTO.so.cc b/llvm_mode/SanitizerCoverageLTO.so.cc deleted file mode 100644 index 1dd65188..00000000 --- a/llvm_mode/SanitizerCoverageLTO.so.cc +++ /dev/null @@ -1,1503 +0,0 @@ -/* SanitizeCoverage.cpp ported to afl++ LTO :-) */ - -#define AFL_LLVM_PASS - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/Analysis/EHPersonalities.h" -#include "llvm/Analysis/PostDominators.h" -#include "llvm/Analysis/ValueTracking.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/CFG.h" -#include "llvm/IR/Constant.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/DebugInfo.h" -#include "llvm/IR/Dominators.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/InlineAsm.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/IntrinsicInst.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/MDBuilder.h" -#include "llvm/IR/Mangler.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Type.h" -#include "llvm/InitializePasses.h" -#include "llvm/Pass.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/SpecialCaseList.h" -#include "llvm/Support/VirtualFileSystem.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/Instrumentation.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Transforms/Utils/ModuleUtils.h" - -#include "config.h" -#include "debug.h" -#include "afl-llvm-common.h" - -using namespace llvm; - -#define DEBUG_TYPE "sancov" - -static const char *const SanCovTracePCIndirName = - "__sanitizer_cov_trace_pc_indir"; -static const char *const SanCovTracePCName = "__sanitizer_cov_trace_pc"; -// static const char *const SanCovTracePCGuardName = -// "__sanitizer_cov_trace_pc_guard"; -static const char *const SanCovGuardsSectionName = "sancov_guards"; -static const char *const SanCovCountersSectionName = "sancov_cntrs"; -static const char *const SanCovBoolFlagSectionName = "sancov_bools"; -static const char *const SanCovPCsSectionName = "sancov_pcs"; - -static cl::opt ClCoverageLevel( - "lto-coverage-level", - cl::desc("Sanitizer Coverage. 0: none, 1: entry block, 2: all blocks, " - "3: all blocks and critical edges"), - cl::Hidden, cl::init(3)); - -static cl::opt ClTracePC("lto-coverage-trace-pc", - cl::desc("Experimental pc tracing"), cl::Hidden, - cl::init(false)); - -static cl::opt ClTracePCGuard("lto-coverage-trace-pc-guard", - cl::desc("pc tracing with a guard"), - cl::Hidden, cl::init(false)); - -// If true, we create a global variable that contains PCs of all instrumented -// BBs, put this global into a named section, and pass this section's bounds -// to __sanitizer_cov_pcs_init. -// This way the coverage instrumentation does not need to acquire the PCs -// at run-time. Works with trace-pc-guard, inline-8bit-counters, and -// inline-bool-flag. -static cl::opt ClCreatePCTable("lto-coverage-pc-table", - cl::desc("create a static PC table"), - cl::Hidden, cl::init(false)); - -static cl::opt ClInline8bitCounters( - "lto-coverage-inline-8bit-counters", - cl::desc("increments 8-bit counter for every edge"), cl::Hidden, - cl::init(false)); - -static cl::opt ClInlineBoolFlag( - "lto-coverage-inline-bool-flag", - cl::desc("sets a boolean flag for every edge"), cl::Hidden, - cl::init(false)); - -static cl::opt ClPruneBlocks( - "lto-coverage-prune-blocks", - cl::desc("Reduce the number of instrumented blocks"), cl::Hidden, - cl::init(true)); - -namespace { - -SanitizerCoverageOptions getOptions(int LegacyCoverageLevel) { - - SanitizerCoverageOptions Res; - switch (LegacyCoverageLevel) { - - case 0: - Res.CoverageType = SanitizerCoverageOptions::SCK_None; - break; - case 1: - Res.CoverageType = SanitizerCoverageOptions::SCK_Function; - break; - case 2: - Res.CoverageType = SanitizerCoverageOptions::SCK_BB; - break; - case 3: - Res.CoverageType = SanitizerCoverageOptions::SCK_Edge; - break; - case 4: - Res.CoverageType = SanitizerCoverageOptions::SCK_Edge; - Res.IndirectCalls = true; - break; - - } - - return Res; - -} - -SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) { - - // Sets CoverageType and IndirectCalls. - SanitizerCoverageOptions CLOpts = getOptions(ClCoverageLevel); - Options.CoverageType = std::max(Options.CoverageType, CLOpts.CoverageType); - Options.IndirectCalls |= CLOpts.IndirectCalls; - Options.TracePC |= ClTracePC; - Options.TracePCGuard |= ClTracePCGuard; - Options.Inline8bitCounters |= ClInline8bitCounters; - Options.InlineBoolFlag |= ClInlineBoolFlag; - Options.PCTable |= ClCreatePCTable; - Options.NoPrune |= !ClPruneBlocks; - if (!Options.TracePCGuard && !Options.TracePC && - !Options.Inline8bitCounters && !Options.InlineBoolFlag) - Options.TracePCGuard = true; // TracePCGuard is default. - return Options; - -} - -using DomTreeCallback = function_ref; -using PostDomTreeCallback = - function_ref; - -class ModuleSanitizerCoverage { - - public: - ModuleSanitizerCoverage( - const SanitizerCoverageOptions &Options = SanitizerCoverageOptions()) - : Options(OverrideFromCL(Options)) { - - /* , - const SpecialCaseList * Allowlist = nullptr, - const SpecialCaseList * Blocklist = nullptr) - , - Allowlist(Allowlist), - Blocklist(Blocklist) { - - */ - - } - - bool instrumentModule(Module &M, DomTreeCallback DTCallback, - PostDomTreeCallback PDTCallback); - - private: - void instrumentFunction(Function &F, DomTreeCallback DTCallback, - PostDomTreeCallback PDTCallback); - void InjectCoverageForIndirectCalls(Function & F, - ArrayRef IndirCalls); - bool InjectCoverage(Function &F, ArrayRef AllBlocks, - bool IsLeafFunc = true); - GlobalVariable *CreateFunctionLocalArrayInSection(size_t NumElements, - Function &F, Type *Ty, - const char *Section); - GlobalVariable *CreatePCArray(Function &F, ArrayRef AllBlocks); - void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks); - void InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx, - bool IsLeafFunc = true); - std::pair CreateSecStartEnd(Module &M, const char *Section, - Type *Ty); - - void SetNoSanitizeMetadata(Instruction *I) { - - I->setMetadata(I->getModule()->getMDKindID("nosanitize"), - MDNode::get(*C, None)); - - } - - std::string getSectionName(const std::string &Section) const; - std::string getSectionStart(const std::string &Section) const; - std::string getSectionEnd(const std::string &Section) const; - FunctionCallee SanCovTracePCIndir; - FunctionCallee SanCovTracePC /*, SanCovTracePCGuard*/; - Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy, - *Int16Ty, *Int8Ty, *Int8PtrTy, *Int1Ty, *Int1PtrTy; - Module * CurModule; - std::string CurModuleUniqueId; - Triple TargetTriple; - LLVMContext * C; - const DataLayout *DL; - - GlobalVariable *FunctionGuardArray; // for trace-pc-guard. - GlobalVariable *Function8bitCounterArray; // for inline-8bit-counters. - GlobalVariable *FunctionBoolArray; // for inline-bool-flag. - GlobalVariable *FunctionPCsArray; // for pc-table. - SmallVector GlobalsToAppendToUsed; - SmallVector GlobalsToAppendToCompilerUsed; - - SanitizerCoverageOptions Options; - - // afl++ START - // const SpecialCaseList * Allowlist; - // const SpecialCaseList * Blocklist; - uint32_t autodictionary = 1; - uint32_t inst = 0; - uint32_t afl_global_id = 0; - uint64_t map_addr = 0; - char * skip_nozero = NULL; - std::vector BlockList; - DenseMap valueMap; - std::vector dictionary; - IntegerType * Int8Tyi = NULL; - IntegerType * Int32Tyi = NULL; - IntegerType * Int64Tyi = NULL; - ConstantInt * Zero = NULL; - ConstantInt * One = NULL; - LLVMContext * Ct = NULL; - Module * Mo = NULL; - GlobalVariable * AFLMapPtr = NULL; - Value * MapPtrFixed = NULL; - FILE * documentFile = NULL; - // afl++ END - -}; - -class ModuleSanitizerCoverageLegacyPass : public ModulePass { - - public: - static char ID; - StringRef getPassName() const override { - - return "sancov"; - - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - - AU.addRequired(); - AU.addRequired(); - - } - - ModuleSanitizerCoverageLegacyPass( - const SanitizerCoverageOptions &Options = SanitizerCoverageOptions()) - : ModulePass(ID), Options(Options) { - - /* , - const std::vector &AllowlistFiles = - std::vector(), - const std::vector &BlocklistFiles = - std::vector()) - if (AllowlistFiles.size() > 0) - Allowlist = SpecialCaseList::createOrDie(AllowlistFiles, - *vfs::getRealFileSystem()); - if (BlocklistFiles.size() > 0) - Blocklist = SpecialCaseList::createOrDie(BlocklistFiles, - *vfs::getRealFileSystem()); - */ - initializeModuleSanitizerCoverageLegacyPassPass( - *PassRegistry::getPassRegistry()); - - } - - bool runOnModule(Module &M) override { - - ModuleSanitizerCoverage ModuleSancov(Options); - // , Allowlist.get(), Blocklist.get()); - auto DTCallback = [this](Function &F) -> const DominatorTree * { - - return &this->getAnalysis(F).getDomTree(); - - }; - - auto PDTCallback = [this](Function &F) -> const PostDominatorTree * { - - return &this->getAnalysis(F) - .getPostDomTree(); - - }; - - return ModuleSancov.instrumentModule(M, DTCallback, PDTCallback); - - } - - private: - SanitizerCoverageOptions Options; - - // std::unique_ptr Allowlist; - // std::unique_ptr Blocklist; - -}; - -} // namespace - -PreservedAnalyses ModuleSanitizerCoveragePass::run(Module & M, - ModuleAnalysisManager &MAM) { - - ModuleSanitizerCoverage ModuleSancov(Options); - // Allowlist.get(), Blocklist.get()); - auto &FAM = MAM.getResult(M).getManager(); - auto DTCallback = [&FAM](Function &F) -> const DominatorTree * { - - return &FAM.getResult(F); - - }; - - auto PDTCallback = [&FAM](Function &F) -> const PostDominatorTree * { - - return &FAM.getResult(F); - - }; - - if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback)) - return PreservedAnalyses::none(); - - return PreservedAnalyses::all(); - -} - -std::pair ModuleSanitizerCoverage::CreateSecStartEnd( - Module &M, const char *Section, Type *Ty) { - - GlobalVariable *SecStart = - new GlobalVariable(M, Ty, false, GlobalVariable::ExternalLinkage, nullptr, - getSectionStart(Section)); - SecStart->setVisibility(GlobalValue::HiddenVisibility); - GlobalVariable *SecEnd = - new GlobalVariable(M, Ty, false, GlobalVariable::ExternalLinkage, nullptr, - getSectionEnd(Section)); - SecEnd->setVisibility(GlobalValue::HiddenVisibility); - IRBuilder<> IRB(M.getContext()); - Value * SecEndPtr = IRB.CreatePointerCast(SecEnd, Ty); - if (!TargetTriple.isOSBinFormatCOFF()) - return std::make_pair(IRB.CreatePointerCast(SecStart, Ty), SecEndPtr); - - // Account for the fact that on windows-msvc __start_* symbols actually - // point to a uint64_t before the start of the array. - auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy); - auto GEP = IRB.CreateGEP(Int8Ty, SecStartI8Ptr, - ConstantInt::get(IntptrTy, sizeof(uint64_t))); - return std::make_pair(IRB.CreatePointerCast(GEP, Ty), SecEndPtr); - -} - -bool ModuleSanitizerCoverage::instrumentModule( - Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { - - if (Options.CoverageType == SanitizerCoverageOptions::SCK_None) return false; - /* - if (Allowlist && - !Allowlist->inSection("coverage", "src", M.getSourceFileName())) - return false; - if (Blocklist && - Blocklist->inSection("coverage", "src", M.getSourceFileName())) - return false; - */ - BlockList.clear(); - valueMap.clear(); - dictionary.clear(); - C = &(M.getContext()); - DL = &M.getDataLayout(); - CurModule = &M; - CurModuleUniqueId = getUniqueModuleId(CurModule); - TargetTriple = Triple(M.getTargetTriple()); - FunctionGuardArray = nullptr; - Function8bitCounterArray = nullptr; - FunctionBoolArray = nullptr; - FunctionPCsArray = nullptr; - IntptrTy = Type::getIntNTy(*C, DL->getPointerSizeInBits()); - IntptrPtrTy = PointerType::getUnqual(IntptrTy); - Type * VoidTy = Type::getVoidTy(*C); - IRBuilder<> IRB(*C); - Int64PtrTy = PointerType::getUnqual(IRB.getInt64Ty()); - Int32PtrTy = PointerType::getUnqual(IRB.getInt32Ty()); - Int8PtrTy = PointerType::getUnqual(IRB.getInt8Ty()); - Int1PtrTy = PointerType::getUnqual(IRB.getInt1Ty()); - Int64Ty = IRB.getInt64Ty(); - Int32Ty = IRB.getInt32Ty(); - Int16Ty = IRB.getInt16Ty(); - Int8Ty = IRB.getInt8Ty(); - Int1Ty = IRB.getInt1Ty(); - - /* afl++ START */ - char * ptr; - LLVMContext &Ctx = M.getContext(); - Ct = &Ctx; - Int8Tyi = IntegerType::getInt8Ty(Ctx); - Int32Tyi = IntegerType::getInt32Ty(Ctx); - Int64Tyi = IntegerType::getInt64Ty(Ctx); - - /* Show a banner */ - setvbuf(stdout, NULL, _IONBF, 0); - if (getenv("AFL_DEBUG")) debug = 1; - - if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { - - SAYF(cCYA "afl-llvm-lto" VERSION cRST - " by Marc \"vanHauser\" Heuse \n"); - - } else - - be_quiet = 1; - - skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); - - if ((ptr = getenv("AFL_LLVM_LTO_STARTID")) != NULL) - if ((afl_global_id = atoi(ptr)) < 0) - FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is negative\n", ptr); - - if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { - - if ((documentFile = fopen(ptr, "a")) == NULL) - WARNF("Cannot access document file %s", ptr); - - } - - // we make this the default as the fixed map has problems with - // defered forkserver, early constructors, ifuncs and maybe more - /*if (getenv("AFL_LLVM_MAP_DYNAMIC"))*/ - map_addr = 0; - - if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { - - uint64_t val; - if (!*ptr || !strcmp(ptr, "0") || !strcmp(ptr, "0x0")) { - - map_addr = 0; - - } else if (getenv("AFL_LLVM_MAP_DYNAMIC")) { - - FATAL( - "AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used together"); - - } else if (strncmp(ptr, "0x", 2) != 0) { - - map_addr = 0x10000; // the default - - } else { - - val = strtoull(ptr, NULL, 16); - if (val < 0x100 || val > 0xffffffff00000000) { - - FATAL( - "AFL_LLVM_MAP_ADDR must be a value between 0x100 and " - "0xffffffff00000000"); - - } - - map_addr = val; - - } - - } - - /* Get/set the globals for the SHM region. */ - - if (!map_addr) { - - AFLMapPtr = - new GlobalVariable(M, PointerType::get(Int8Tyi, 0), false, - GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); - - } else { - - ConstantInt *MapAddr = ConstantInt::get(Int64Tyi, map_addr); - MapPtrFixed = - ConstantExpr::getIntToPtr(MapAddr, PointerType::getUnqual(Int8Tyi)); - - } - - Zero = ConstantInt::get(Int8Tyi, 0); - One = ConstantInt::get(Int8Tyi, 1); - - scanForDangerousFunctions(&M); - Mo = &M; - - if (autodictionary) { - - for (auto &F : M) { - - for (auto &BB : F) { - - for (auto &IN : BB) { - - CallInst *callInst = nullptr; - - if ((callInst = dyn_cast(&IN))) { - - bool isStrcmp = true; - bool isMemcmp = true; - bool isStrncmp = true; - bool isStrcasecmp = true; - bool isStrncasecmp = true; - bool isIntMemcpy = true; - bool addedNull = false; - size_t optLen = 0; - - Function *Callee = callInst->getCalledFunction(); - if (!Callee) continue; - if (callInst->getCallingConv() != llvm::CallingConv::C) continue; - std::string FuncName = Callee->getName().str(); - isStrcmp &= !FuncName.compare("strcmp"); - isMemcmp &= !FuncName.compare("memcmp"); - isStrncmp &= !FuncName.compare("strncmp"); - isStrcasecmp &= !FuncName.compare("strcasecmp"); - isStrncasecmp &= !FuncName.compare("strncasecmp"); - isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); - - /* we do something different here, putting this BB and the - successors in a block map */ - if (!FuncName.compare("__afl_persistent_loop")) { - - BlockList.push_back(&BB); - for (succ_iterator SI = succ_begin(&BB), SE = succ_end(&BB); - SI != SE; ++SI) { - - BasicBlock *succ = *SI; - BlockList.push_back(succ); - - } - - } - - if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) - continue; - - /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function - * prototype */ - FunctionType *FT = Callee->getFunctionType(); - - isStrcmp &= FT->getNumParams() == 2 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()); - isStrcasecmp &= FT->getNumParams() == 2 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()); - isMemcmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0)->isPointerTy() && - FT->getParamType(1)->isPointerTy() && - FT->getParamType(2)->isIntegerTy(); - isStrncmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); - isStrncasecmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); - - if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) - continue; - - /* is a str{n,}{case,}cmp/memcmp, check if we have - * str{case,}cmp(x, "const") or str{case,}cmp("const", x) - * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, ..) - * memcmp(x, "const", ..) or memcmp("const", x, ..) */ - Value *Str1P = callInst->getArgOperand(0), - *Str2P = callInst->getArgOperand(1); - std::string Str1, Str2; - StringRef TmpStr; - bool HasStr1 = getConstantStringInfo(Str1P, TmpStr); - if (TmpStr.empty()) - HasStr1 = false; - else - Str1 = TmpStr.str(); - bool HasStr2 = getConstantStringInfo(Str2P, TmpStr); - if (TmpStr.empty()) - HasStr2 = false; - else - Str2 = TmpStr.str(); - - if (debug) - fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n", - FuncName.c_str(), Str1P, Str1P->getName().str().c_str(), - Str1.c_str(), HasStr1 == true ? "true" : "false", Str2P, - Str2P->getName().str().c_str(), Str2.c_str(), - HasStr2 == true ? "true" : "false"); - - // we handle the 2nd parameter first because of llvm memcpy - if (!HasStr2) { - - auto *Ptr = dyn_cast(Str2P); - if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { - - if (auto *Var = dyn_cast(Ptr->getOperand(0))) { - - if (Var->hasInitializer()) { - - if (auto *Array = dyn_cast( - Var->getInitializer())) { - - HasStr2 = true; - Str2 = Array->getAsString().str(); - - } - - } - - } - - } - - } - - // for the internal memcpy routine we only care for the second - // parameter and are not reporting anything. - if (isIntMemcpy == true) { - - if (HasStr2 == true) { - - Value * op2 = callInst->getArgOperand(2); - ConstantInt *ilen = dyn_cast(op2); - if (ilen) { - - uint64_t literalLength = Str2.size(); - uint64_t optLength = ilen->getZExtValue(); - if (literalLength + 1 == optLength) { - - Str2.append("\0", 1); // add null byte - addedNull = true; - - } - - } - - valueMap[Str1P] = new std::string(Str2); - - if (debug) - fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), Str1P); - continue; - - } - - continue; - - } - - // Neither a literal nor a global variable? - // maybe it is a local variable that we saved - if (!HasStr2) { - - std::string *strng = valueMap[Str2P]; - if (strng && !strng->empty()) { - - Str2 = *strng; - HasStr2 = true; - if (debug) - fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(), - Str2P); - - } - - } - - if (!HasStr1) { - - auto Ptr = dyn_cast(Str1P); - - if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { - - if (auto *Var = dyn_cast(Ptr->getOperand(0))) { - - if (Var->hasInitializer()) { - - if (auto *Array = dyn_cast( - Var->getInitializer())) { - - HasStr1 = true; - Str1 = Array->getAsString().str(); - - } - - } - - } - - } - - } - - // Neither a literal nor a global variable? - // maybe it is a local variable that we saved - if (!HasStr1) { - - std::string *strng = valueMap[Str1P]; - if (strng && !strng->empty()) { - - Str1 = *strng; - HasStr1 = true; - if (debug) - fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(), - Str1P); - - } - - } - - /* handle cases of one string is const, one string is variable */ - if (!(HasStr1 ^ HasStr2)) continue; - - std::string thestring; - - if (HasStr1) - thestring = Str1; - else - thestring = Str2; - - optLen = thestring.length(); - - if (isMemcmp || isStrncmp || isStrncasecmp) { - - Value * op2 = callInst->getArgOperand(2); - ConstantInt *ilen = dyn_cast(op2); - if (ilen) { - - uint64_t literalLength = optLen; - optLen = ilen->getZExtValue(); - if (literalLength + 1 == optLen) { // add null byte - thestring.append("\0", 1); - addedNull = true; - - } - - } - - } - - // add null byte if this is a string compare function and a null - // was not already added - if (!isMemcmp) { - - if (addedNull == false) { - - thestring.append("\0", 1); // add null byte - optLen++; - - } - - // ensure we do not have garbage - size_t offset = thestring.find('\0', 0); - if (offset + 1 < optLen) optLen = offset + 1; - thestring = thestring.substr(0, optLen); - - } - - if (!be_quiet) { - - std::string outstring; - fprintf(stderr, "%s: length %zu/%zu \"", FuncName.c_str(), optLen, - thestring.length()); - for (uint8_t i = 0; i < thestring.length(); i++) { - - uint8_t c = thestring[i]; - if (c <= 32 || c >= 127) - fprintf(stderr, "\\x%02x", c); - else - fprintf(stderr, "%c", c); - - } - - fprintf(stderr, "\"\n"); - - } - - // we take the longer string, even if the compare was to a - // shorter part. Note that depending on the optimizer of the - // compiler this can be wrong, but it is more likely that this - // is helping the fuzzer - if (optLen != thestring.length()) optLen = thestring.length(); - if (optLen > MAX_AUTO_EXTRA) optLen = MAX_AUTO_EXTRA; - if (optLen < MIN_AUTO_EXTRA) // too short? skip - continue; - - dictionary.push_back(thestring.substr(0, optLen)); - - } - - } - - } - - } - - } - - // afl++ END - - SanCovTracePCIndir = - M.getOrInsertFunction(SanCovTracePCIndirName, VoidTy, IntptrTy); - // Make sure smaller parameters are zero-extended to i64 as required by the - // x86_64 ABI. - AttributeList SanCovTraceCmpZeroExtAL; - if (TargetTriple.getArch() == Triple::x86_64) { - - SanCovTraceCmpZeroExtAL = - SanCovTraceCmpZeroExtAL.addParamAttribute(*C, 0, Attribute::ZExt); - SanCovTraceCmpZeroExtAL = - SanCovTraceCmpZeroExtAL.addParamAttribute(*C, 1, Attribute::ZExt); - - } - - SanCovTracePC = M.getOrInsertFunction(SanCovTracePCName, VoidTy); - - // SanCovTracePCGuard = - // M.getOrInsertFunction(SanCovTracePCGuardName, VoidTy, Int32PtrTy); - - for (auto &F : M) - instrumentFunction(F, DTCallback, PDTCallback); - - // afl++ START - if (documentFile) { - - fclose(documentFile); - documentFile = NULL; - - } - - if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) { - - // yes we could create our own function, insert it into ctors ... - // but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o - - Function *f = M.getFunction("__afl_auto_init_globals"); - - if (!f) { - - fprintf(stderr, - "Error: init function could not be found (this should not " - "happen)\n"); - exit(-1); - - } - - BasicBlock *bb = &f->getEntryBlock(); - if (!bb) { - - fprintf(stderr, - "Error: init function does not have an EntryBlock (this should " - "not happen)\n"); - exit(-1); - - } - - BasicBlock::iterator IP = bb->getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - - if (map_addr) { - - GlobalVariable *AFLMapAddrFixed = new GlobalVariable( - M, Int64Tyi, true, GlobalValue::ExternalLinkage, 0, "__afl_map_addr"); - ConstantInt *MapAddr = ConstantInt::get(Int64Tyi, map_addr); - StoreInst * StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed); - StoreMapAddr->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(Ctx, None)); - - } - - if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) { - - uint32_t write_loc = afl_global_id; - - if (afl_global_id % 8) write_loc = (((afl_global_id + 8) >> 3) << 3); - - GlobalVariable *AFLFinalLoc = - new GlobalVariable(M, Int32Tyi, true, GlobalValue::ExternalLinkage, 0, - "__afl_final_loc"); - ConstantInt *const_loc = ConstantInt::get(Int32Tyi, write_loc); - StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc); - StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(Ctx, None)); - - } - - if (dictionary.size()) { - - size_t memlen = 0, count = 0, offset = 0; - char * ptr; - - for (auto token : dictionary) { - - memlen += token.length(); - count++; - - } - - if (!be_quiet) - printf("AUTODICTIONARY: %lu string%s found\n", count, - count == 1 ? "" : "s"); - - if (count) { - - if ((ptr = (char *)malloc(memlen + count)) == NULL) { - - fprintf(stderr, "Error: malloc for %lu bytes failed!\n", - memlen + count); - exit(-1); - - } - - count = 0; - - for (auto token : dictionary) { - - if (offset + token.length() < 0xfffff0 && count < MAX_AUTO_EXTRAS) { - - ptr[offset++] = (uint8_t)token.length(); - memcpy(ptr + offset, token.c_str(), token.length()); - offset += token.length(); - count++; - - } - - } - - GlobalVariable *AFLDictionaryLen = - new GlobalVariable(M, Int32Tyi, false, GlobalValue::ExternalLinkage, - 0, "__afl_dictionary_len"); - ConstantInt *const_len = ConstantInt::get(Int32Tyi, offset); - StoreInst *StoreDictLen = IRB.CreateStore(const_len, AFLDictionaryLen); - StoreDictLen->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(Ctx, None)); - - ArrayType *ArrayTy = ArrayType::get(IntegerType::get(Ctx, 8), offset); - GlobalVariable *AFLInternalDictionary = new GlobalVariable( - M, ArrayTy, true, GlobalValue::ExternalLinkage, - ConstantDataArray::get(Ctx, - *(new ArrayRef((char *)ptr, offset))), - "__afl_internal_dictionary"); - AFLInternalDictionary->setInitializer(ConstantDataArray::get( - Ctx, *(new ArrayRef((char *)ptr, offset)))); - AFLInternalDictionary->setConstant(true); - - GlobalVariable *AFLDictionary = new GlobalVariable( - M, PointerType::get(Int8Tyi, 0), false, - GlobalValue::ExternalLinkage, 0, "__afl_dictionary"); - - Value *AFLDictOff = IRB.CreateGEP(AFLInternalDictionary, Zero); - Value *AFLDictPtr = - IRB.CreatePointerCast(AFLDictOff, PointerType::get(Int8Tyi, 0)); - StoreInst *StoreDict = IRB.CreateStore(AFLDictPtr, AFLDictionary); - StoreDict->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(Ctx, None)); - - } - - } - - } - - /* Say something nice. */ - - if (!be_quiet) { - - if (!inst) - WARNF("No instrumentation targets found."); - else { - - char modeline[100]; - snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", - getenv("AFL_HARDEN") ? "hardened" : "non-hardened", - getenv("AFL_USE_ASAN") ? ", ASAN" : "", - getenv("AFL_USE_MSAN") ? ", MSAN" : "", - getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", - getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); - OKF("Instrumented %u locations with no collisions (on average %llu " - "collisions would be in afl-gcc/afl-clang-fast) (%s mode).", - inst, calculateCollisions(inst), modeline); - - } - - } - - // afl++ END - - // We don't reference these arrays directly in any of our runtime functions, - // so we need to prevent them from being dead stripped. - if (TargetTriple.isOSBinFormatMachO()) appendToUsed(M, GlobalsToAppendToUsed); - appendToCompilerUsed(M, GlobalsToAppendToCompilerUsed); - return true; - -} - -// True if block has successors and it dominates all of them. -static bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) { - - if (succ_begin(BB) == succ_end(BB)) return false; - - for (const BasicBlock *SUCC : make_range(succ_begin(BB), succ_end(BB))) { - - if (!DT->dominates(BB, SUCC)) return false; - - } - - return true; - -} - -// True if block has predecessors and it postdominates all of them. -static bool isFullPostDominator(const BasicBlock * BB, - const PostDominatorTree *PDT) { - - if (pred_begin(BB) == pred_end(BB)) return false; - - for (const BasicBlock *PRED : make_range(pred_begin(BB), pred_end(BB))) { - - if (!PDT->dominates(BB, PRED)) return false; - - } - - return true; - -} - -static bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB, - const DominatorTree * DT, - const PostDominatorTree * PDT, - const SanitizerCoverageOptions &Options) { - - // Don't insert coverage for blocks containing nothing but unreachable: we - // will never call __sanitizer_cov() for them, so counting them in - // NumberOfInstrumentedBlocks() might complicate calculation of code coverage - // percentage. Also, unreachable instructions frequently have no debug - // locations. - if (isa(BB->getFirstNonPHIOrDbgOrLifetime())) return false; - - // Don't insert coverage into blocks without a valid insertion point - // (catchswitch blocks). - if (BB->getFirstInsertionPt() == BB->end()) return false; - - // afl++ START - if (!Options.NoPrune && &F.getEntryBlock() == BB && F.size() > 1) - return false; - // afl++ END - - if (Options.NoPrune || &F.getEntryBlock() == BB) return true; - - if (Options.CoverageType == SanitizerCoverageOptions::SCK_Function && - &F.getEntryBlock() != BB) - return false; - - // Do not instrument full dominators, or full post-dominators with multiple - // predecessors. - return !isFullDominator(BB, DT) && - !(isFullPostDominator(BB, PDT) && !BB->getSinglePredecessor()); - -} - -void ModuleSanitizerCoverage::instrumentFunction( - Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { - - if (F.empty()) return; - if (F.getName().find(".module_ctor") != std::string::npos) - return; // Should not instrument sanitizer init functions. - if (F.getName().startswith("__sanitizer_")) - return; // Don't instrument __sanitizer_* callbacks. - // Don't touch available_externally functions, their actual body is elewhere. - if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) return; - // Don't instrument MSVC CRT configuration helpers. They may run before normal - // initialization. - if (F.getName() == "__local_stdio_printf_options" || - F.getName() == "__local_stdio_scanf_options") - return; - if (isa(F.getEntryBlock().getTerminator())) return; - // Don't instrument functions using SEH for now. Splitting basic blocks like - // we do for coverage breaks WinEHPrepare. - // FIXME: Remove this when SEH no longer uses landingpad pattern matching. - if (F.hasPersonalityFn() && - isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) - return; - // if (Allowlist && !Allowlist->inSection("coverage", "fun", F.getName())) - // return; - // if (Blocklist && Blocklist->inSection("coverage", "fun", F.getName())) - // return; - - // afl++ START - if (!F.size()) return; - if (isIgnoreFunction(&F)) return; - // afl++ END - - if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge) - SplitAllCriticalEdges( - F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests()); - SmallVector IndirCalls; - SmallVector BlocksToInstrument; - - const DominatorTree * DT = DTCallback(F); - const PostDominatorTree *PDT = PDTCallback(F); - bool IsLeafFunc = true; - - for (auto &BB : F) { - - if (shouldInstrumentBlock(F, &BB, DT, PDT, Options)) - BlocksToInstrument.push_back(&BB); - for (auto &Inst : BB) { - - if (Options.IndirectCalls) { - - CallBase *CB = dyn_cast(&Inst); - if (CB && !CB->getCalledFunction()) IndirCalls.push_back(&Inst); - - } - - } - - } - - InjectCoverage(F, BlocksToInstrument, IsLeafFunc); - InjectCoverageForIndirectCalls(F, IndirCalls); - -} - -GlobalVariable *ModuleSanitizerCoverage::CreateFunctionLocalArrayInSection( - size_t NumElements, Function &F, Type *Ty, const char *Section) { - - ArrayType *ArrayTy = ArrayType::get(Ty, NumElements); - auto Array = new GlobalVariable( - *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage, - Constant::getNullValue(ArrayTy), "__sancov_gen_"); - - if (TargetTriple.supportsCOMDAT() && !F.isInterposable()) - if (auto Comdat = - GetOrCreateFunctionComdat(F, TargetTriple, CurModuleUniqueId)) - Array->setComdat(Comdat); - Array->setSection(getSectionName(Section)); - Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize())); - GlobalsToAppendToUsed.push_back(Array); - GlobalsToAppendToCompilerUsed.push_back(Array); - MDNode *MD = MDNode::get(F.getContext(), ValueAsMetadata::get(&F)); - Array->addMetadata(LLVMContext::MD_associated, *MD); - - return Array; - -} - -GlobalVariable *ModuleSanitizerCoverage::CreatePCArray( - Function &F, ArrayRef AllBlocks) { - - size_t N = AllBlocks.size(); - assert(N); - SmallVector PCs; - IRBuilder<> IRB(&*F.getEntryBlock().getFirstInsertionPt()); - for (size_t i = 0; i < N; i++) { - - if (&F.getEntryBlock() == AllBlocks[i]) { - - PCs.push_back((Constant *)IRB.CreatePointerCast(&F, IntptrPtrTy)); - PCs.push_back((Constant *)IRB.CreateIntToPtr( - ConstantInt::get(IntptrTy, 1), IntptrPtrTy)); - - } else { - - PCs.push_back((Constant *)IRB.CreatePointerCast( - BlockAddress::get(AllBlocks[i]), IntptrPtrTy)); - PCs.push_back((Constant *)IRB.CreateIntToPtr( - ConstantInt::get(IntptrTy, 0), IntptrPtrTy)); - - } - - } - - auto *PCArray = CreateFunctionLocalArrayInSection(N * 2, F, IntptrPtrTy, - SanCovPCsSectionName); - PCArray->setInitializer( - ConstantArray::get(ArrayType::get(IntptrPtrTy, N * 2), PCs)); - PCArray->setConstant(true); - - return PCArray; - -} - -void ModuleSanitizerCoverage::CreateFunctionLocalArrays( - Function &F, ArrayRef AllBlocks) { - - if (Options.TracePCGuard) - FunctionGuardArray = CreateFunctionLocalArrayInSection( - AllBlocks.size(), F, Int32Ty, SanCovGuardsSectionName); - if (Options.Inline8bitCounters) - Function8bitCounterArray = CreateFunctionLocalArrayInSection( - AllBlocks.size(), F, Int8Ty, SanCovCountersSectionName); - if (Options.InlineBoolFlag) - FunctionBoolArray = CreateFunctionLocalArrayInSection( - AllBlocks.size(), F, Int1Ty, SanCovBoolFlagSectionName); - if (Options.PCTable) FunctionPCsArray = CreatePCArray(F, AllBlocks); - -} - -bool ModuleSanitizerCoverage::InjectCoverage(Function & F, - ArrayRef AllBlocks, - bool IsLeafFunc) { - - if (AllBlocks.empty()) return false; - CreateFunctionLocalArrays(F, AllBlocks); - for (size_t i = 0, N = AllBlocks.size(); i < N; i++) { - - // afl++ START - if (BlockList.size()) { - - int skip = 0; - for (uint32_t k = 0; k < BlockList.size(); k++) { - - if (AllBlocks[i] == BlockList[k]) { - - if (debug) - fprintf(stderr, - "DEBUG: Function %s skipping BB with/after __afl_loop\n", - F.getName().str().c_str()); - skip = 1; - - } - - } - - if (skip) continue; - - } - - // afl++ END - - InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc); - - } - - return true; - -} - -// On every indirect call we call a run-time function -// __sanitizer_cov_indir_call* with two parameters: -// - callee address, -// - global cache array that contains CacheSize pointers (zero-initialized). -// The cache is used to speed up recording the caller-callee pairs. -// The address of the caller is passed implicitly via caller PC. -// CacheSize is encoded in the name of the run-time function. -void ModuleSanitizerCoverage::InjectCoverageForIndirectCalls( - Function &F, ArrayRef IndirCalls) { - - if (IndirCalls.empty()) return; - assert(Options.TracePC || Options.TracePCGuard || - Options.Inline8bitCounters || Options.InlineBoolFlag); - for (auto I : IndirCalls) { - - IRBuilder<> IRB(I); - CallBase & CB = cast(*I); - Value * Callee = CB.getCalledOperand(); - if (isa(Callee)) continue; - IRB.CreateCall(SanCovTracePCIndir, IRB.CreatePointerCast(Callee, IntptrTy)); - - } - -} - -void ModuleSanitizerCoverage::InjectCoverageAtBlock(Function &F, BasicBlock &BB, - size_t Idx, - bool IsLeafFunc) { - - BasicBlock::iterator IP = BB.getFirstInsertionPt(); - bool IsEntryBB = &BB == &F.getEntryBlock(); - DebugLoc EntryLoc; - if (IsEntryBB) { - - if (auto SP = F.getSubprogram()) - EntryLoc = DebugLoc::get(SP->getScopeLine(), 0, SP); - // Keep static allocas and llvm.localescape calls in the entry block. Even - // if we aren't splitting the block, it's nice for allocas to be before - // calls. - IP = PrepareToSplitEntryBlock(BB, IP); - - } else { - - EntryLoc = IP->getDebugLoc(); - - } - - IRBuilder<> IRB(&*IP); - IRB.SetCurrentDebugLocation(EntryLoc); - if (Options.TracePC) { - - IRB.CreateCall(SanCovTracePC) -#if LLVM_VERSION_MAJOR < 12 - ->cannotMerge(); // gets the PC using GET_CALLER_PC. -#else - ->setCannotMerge(); // gets the PC using GET_CALLER_PC. -#endif - - } - - if (Options.TracePCGuard) { - - // afl++ START - ++afl_global_id; - - if (documentFile) { - - unsigned long long int moduleID = - (((unsigned long long int)(rand() & 0xffffffff)) << 32) | getpid(); - fprintf(documentFile, "ModuleID=%llu Function=%s edgeID=%u\n", moduleID, - F.getName().str().c_str(), afl_global_id); - - } - - /* Set the ID of the inserted basic block */ - - ConstantInt *CurLoc = ConstantInt::get(Int32Tyi, afl_global_id); - - /* Load SHM pointer */ - - Value *MapPtrIdx; - - if (map_addr) { - - MapPtrIdx = IRB.CreateGEP(MapPtrFixed, CurLoc); - - } else { - - LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); - MapPtr->setMetadata(Mo->getMDKindID("nosanitize"), - MDNode::get(*Ct, None)); - MapPtrIdx = IRB.CreateGEP(MapPtr, CurLoc); - - } - - /* Update bitmap */ - - LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); - Counter->setMetadata(Mo->getMDKindID("nosanitize"), MDNode::get(*Ct, None)); - - Value *Incr = IRB.CreateAdd(Counter, One); - - if (skip_nozero == NULL) { - - auto cf = IRB.CreateICmpEQ(Incr, Zero); - auto carry = IRB.CreateZExt(cf, Int8Tyi); - Incr = IRB.CreateAdd(Incr, carry); - - } - - IRB.CreateStore(Incr, MapPtrIdx) - ->setMetadata(Mo->getMDKindID("nosanitize"), MDNode::get(*Ct, None)); - - // done :) - - inst++; - // afl++ END - - /* - XXXXXXXXXXXXXXXXXXX - - auto GuardPtr = IRB.CreateIntToPtr( - IRB.CreateAdd(IRB.CreatePointerCast(FunctionGuardArray, IntptrTy), - ConstantInt::get(IntptrTy, Idx * 4)), - Int32PtrTy); - - IRB.CreateCall(SanCovTracePCGuard, GuardPtr)->setCannotMerge(); - */ - - } - - if (Options.Inline8bitCounters) { - - auto CounterPtr = IRB.CreateGEP( - Function8bitCounterArray->getValueType(), Function8bitCounterArray, - {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)}); - auto Load = IRB.CreateLoad(Int8Ty, CounterPtr); - auto Inc = IRB.CreateAdd(Load, ConstantInt::get(Int8Ty, 1)); - auto Store = IRB.CreateStore(Inc, CounterPtr); - SetNoSanitizeMetadata(Load); - SetNoSanitizeMetadata(Store); - - } - - if (Options.InlineBoolFlag) { - - auto FlagPtr = IRB.CreateGEP( - FunctionBoolArray->getValueType(), FunctionBoolArray, - {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)}); - auto Load = IRB.CreateLoad(Int1Ty, FlagPtr); - auto ThenTerm = - SplitBlockAndInsertIfThen(IRB.CreateIsNull(Load), &*IP, false); - IRBuilder<> ThenIRB(ThenTerm); - auto Store = ThenIRB.CreateStore(ConstantInt::getTrue(Int1Ty), FlagPtr); - SetNoSanitizeMetadata(Load); - SetNoSanitizeMetadata(Store); - - } - -} - -std::string ModuleSanitizerCoverage::getSectionName( - const std::string &Section) const { - - if (TargetTriple.isOSBinFormatCOFF()) { - - if (Section == SanCovCountersSectionName) return ".SCOV$CM"; - if (Section == SanCovBoolFlagSectionName) return ".SCOV$BM"; - if (Section == SanCovPCsSectionName) return ".SCOVP$M"; - return ".SCOV$GM"; // For SanCovGuardsSectionName. - - } - - if (TargetTriple.isOSBinFormatMachO()) return "__DATA,__" + Section; - return "__" + Section; - -} - -std::string ModuleSanitizerCoverage::getSectionStart( - const std::string &Section) const { - - if (TargetTriple.isOSBinFormatMachO()) - return "\1section$start$__DATA$__" + Section; - return "__start___" + Section; - -} - -std::string ModuleSanitizerCoverage::getSectionEnd( - const std::string &Section) const { - - if (TargetTriple.isOSBinFormatMachO()) - return "\1section$end$__DATA$__" + Section; - return "__stop___" + Section; - -} - -char ModuleSanitizerCoverageLegacyPass::ID = 0; - -INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLegacyPass, "sancov", - "Pass for instrumenting coverage on functions", false, - false) -INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) -INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass) -INITIALIZE_PASS_END(ModuleSanitizerCoverageLegacyPass, "sancov", - "Pass for instrumenting coverage on functions", false, - false) - -ModulePass *llvm::createModuleSanitizerCoverageLegacyPassPass( - const SanitizerCoverageOptions &Options, - const std::vector &AllowlistFiles, - const std::vector &BlocklistFiles) { - - return new ModuleSanitizerCoverageLegacyPass(Options); - //, AllowlistFiles, BlocklistFiles); - -} - -static void registerLTOPass(const PassManagerBuilder &, - legacy::PassManagerBase &PM) { - - auto p = new ModuleSanitizerCoverageLegacyPass(); - PM.add(p); - -} - -static RegisterStandardPasses RegisterCompTransPass( - PassManagerBuilder::EP_OptimizerLast, registerLTOPass); - -static RegisterStandardPasses RegisterCompTransPass0( - PassManagerBuilder::EP_EnabledOnOptLevel0, registerLTOPass); - -#if LLVM_VERSION_MAJOR >= 11 -static RegisterStandardPasses RegisterCompTransPassLTO( - PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerLTOPass); -#endif - diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c deleted file mode 100644 index ccdbca9d..00000000 --- a/llvm_mode/afl-clang-fast.c +++ /dev/null @@ -1,1143 +0,0 @@ -/* - american fuzzy lop++ - LLVM-mode wrapper for clang - ------------------------------------------------ - - Written by Laszlo Szekeres and - Michal Zalewski - - LLVM integration design comes from Laszlo Szekeres. - - Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This program is a drop-in replacement for clang, similar in most respects - to ../afl-gcc. It tries to figure out compilation mode, adds a bunch - of flags, and then calls the real compiler. - - */ - -#define AFL_MAIN - -#include "common.h" -#include "config.h" -#include "types.h" -#include "debug.h" -#include "alloc-inl.h" -#include "llvm-ngram-coverage.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "llvm/Config/llvm-config.h" - -static u8 * obj_path; /* Path to runtime libraries */ -static u8 **cc_params; /* Parameters passed to the real CC */ -static u32 cc_par_cnt = 1; /* Param count, including argv0 */ -static u8 llvm_fullpath[PATH_MAX]; -static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode, cpp_mode; -static u8 *lto_flag = AFL_CLANG_FLTO; -static u8 debug; -static u8 cwd[4096]; -static u8 cmplog_mode; -u8 use_stdin = 0; /* dummy */ -// static u8 *march_opt = CFLAGS_OPT; - -enum { - - INSTURMENT_DEFAULT = 0, - INSTRUMENT_CLASSIC = 1, - INSTRUMENT_AFL = 1, - INSTRUMENT_PCGUARD = 2, - INSTRUMENT_INSTRIM = 3, - INSTRUMENT_CFG = 3, - INSTRUMENT_LTO = 4, - INSTRUMENT_OPT_CTX = 8, - INSTRUMENT_OPT_NGRAM = 16 - -}; - -char instrument_mode_string[18][18] = { - - "DEFAULT", "CLASSIC", "PCGUARD", "CFG", "LTO", "", "", "", "CTX", "", - "", "", "", "", "", "", "NGRAM", "" - -}; - -u8 *getthecwd() { - - static u8 fail[] = ""; - if (getcwd(cwd, sizeof(cwd)) == NULL) return fail; - return cwd; - -} - -/* Try to find the runtime libraries. If that fails, abort. */ - -static void find_obj(u8 *argv0) { - - u8 *afl_path = getenv("AFL_PATH"); - u8 *slash, *tmp; - - if (afl_path) { - -#ifdef __ANDROID__ - tmp = alloc_printf("%s/afl-llvm-rt.so", afl_path); -#else - tmp = alloc_printf("%s/afl-llvm-rt.o", afl_path); -#endif - - if (!access(tmp, R_OK)) { - - obj_path = afl_path; - ck_free(tmp); - return; - - } - - ck_free(tmp); - - } - - slash = strrchr(argv0, '/'); - - if (slash) { - - u8 *dir; - - *slash = 0; - dir = ck_strdup(argv0); - *slash = '/'; - -#ifdef __ANDROID__ - tmp = alloc_printf("%s/afl-llvm-rt.so", dir); -#else - tmp = alloc_printf("%s/afl-llvm-rt.o", dir); -#endif - - if (!access(tmp, R_OK)) { - - obj_path = dir; - ck_free(tmp); - return; - - } - - ck_free(tmp); - ck_free(dir); - - } - -#ifdef __ANDROID__ - if (!access(AFL_PATH "/afl-llvm-rt.so", R_OK)) { - -#else - if (!access(AFL_PATH "/afl-llvm-rt.o", R_OK)) { - -#endif - - obj_path = AFL_PATH; - return; - - } - - FATAL( - "Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so'. Please set " - "AFL_PATH"); - -} - -/* Copy argv to cc_params, making the necessary edits. */ - -static void edit_params(u32 argc, char **argv, char **envp) { - - u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, shared_linking = 0, - preprocessor_only = 0; - u8 have_pic = 0; - u8 *name; - - cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); - - name = strrchr(argv[0], '/'); - if (!name) - name = argv[0]; - else - ++name; - - if (lto_mode) - if (lto_flag[0] != '-') - FATAL( - "Using afl-clang-lto is not possible because Makefile magic did not " - "identify the correct -flto flag"); - - if (!strcmp(name, "afl-clang-fast++") || !strcmp(name, "afl-clang-lto++") || - !strcmp(name, "afl-clang++")) { - - u8 *alt_cxx = getenv("AFL_CXX"); - if (USE_BINDIR) - snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang++", LLVM_BINDIR); - else - sprintf(llvm_fullpath, CLANGPP_BIN); - cc_params[0] = alt_cxx && *alt_cxx ? alt_cxx : (u8 *)llvm_fullpath; - cpp_mode = 1; - - } else if (!strcmp(name, "afl-clang-fast") || - - !strcmp(name, "afl-clang-lto") || !strcmp(name, "afl-clang")) { - - u8 *alt_cc = getenv("AFL_CC"); - if (USE_BINDIR) - snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang", LLVM_BINDIR); - else - sprintf(llvm_fullpath, CLANG_BIN); - cc_params[0] = alt_cc && *alt_cc ? alt_cc : (u8 *)llvm_fullpath; - - } else { - - fprintf(stderr, "Name of the binary: %s\n", argv[0]); - FATAL( - "Name of the binary is not a known name, expected afl-clang-fast(++) " - "or afl-clang-lto(++)"); - - } - - cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; - - if (lto_mode && cpp_mode) - cc_params[cc_par_cnt++] = "-lc++"; // needed by fuzzbench, early - - /* There are several ways to compile with afl-clang-fast. In the traditional - mode, we use afl-llvm-pass.so, then there is libLLVMInsTrim.so which is - faster and creates less map pollution. - Then there is the 'trace-pc-guard' mode, we use native LLVM - instrumentation callbacks instead. For trace-pc-guard see: - http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards - The best instrumentatation is with the LTO modes, the classic and - InsTrimLTO, the latter is faster. The LTO modes are activated by using - afl-clang-lto(++) - */ - - if (lto_mode) { - - if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { - - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-lto-instrumentlist.so", obj_path); - - } - - } - - // laf - if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { - - if (lto_mode) { - - cc_params[cc_par_cnt++] = - alloc_printf("-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); - - } else { - - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-switches-pass.so", obj_path); - - } - - } - - if (getenv("LAF_TRANSFORM_COMPARES") || - getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { - - if (lto_mode) { - - cc_params[cc_par_cnt++] = alloc_printf( - "-Wl,-mllvm=-load=%s/compare-transform-pass.so", obj_path); - - } else { - - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/compare-transform-pass.so", obj_path); - - } - - } - - if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || - getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { - - if (lto_mode) { - - cc_params[cc_par_cnt++] = - alloc_printf("-Wl,-mllvm=-load=%s/split-compares-pass.so", obj_path); - - } else { - - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-compares-pass.so", obj_path); - - } - - } - - // /laf - - unsetenv("AFL_LD"); - unsetenv("AFL_LD_CALLER"); - if (cmplog_mode) { - - if (lto_mode) { - - cc_params[cc_par_cnt++] = - alloc_printf("-Wl,-mllvm=-load=%s/cmplog-routines-pass.so", obj_path); - cc_params[cc_par_cnt++] = - alloc_printf("-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); - cc_params[cc_par_cnt++] = alloc_printf( - "-Wl,-mllvm=-load=%s/cmplog-instructions-pass.so", obj_path); - - } else { - - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/cmplog-routines-pass.so", obj_path); - - // reuse split switches from laf - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-switches-pass.so", obj_path); - - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/cmplog-instructions-pass.so", obj_path); - - } - - cc_params[cc_par_cnt++] = "-fno-inline"; - - } - - if (lto_mode) { - -#if defined(AFL_CLANG_LDPATH) && LLVM_VERSION_MAJOR >= 12 - u8 *ld_ptr = strrchr(AFL_REAL_LD, '/'); - if (!ld_ptr) ld_ptr = "ld.lld"; - cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", ld_ptr); - cc_params[cc_par_cnt++] = alloc_printf("--ld-path=%s", AFL_REAL_LD); -#else - cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", AFL_REAL_LD); -#endif - - cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; - - if (instrument_mode == INSTRUMENT_CFG) - cc_params[cc_par_cnt++] = - alloc_printf("-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path); - else - - cc_params[cc_par_cnt++] = alloc_printf( - "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path); - cc_params[cc_par_cnt++] = lto_flag; - - } else { - - if (instrument_mode == INSTRUMENT_PCGUARD) { - -#if LLVM_VERSION_MAJOR > 4 || \ - (LLVM_VERSION_MAJOR == 4 && \ - (LLVM_VERSION_MINOR > 0 || LLVM_VERSION_PATCH >= 1)) - cc_params[cc_par_cnt++] = - "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default -#else - FATAL("pcguard instrumentation requires llvm 4.0.1+"); -#endif - - } else { - - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - if (instrument_mode == INSTRUMENT_CFG) - cc_params[cc_par_cnt++] = - alloc_printf("%s/libLLVMInsTrim.so", obj_path); - else - cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path); - - } - - } - - // cc_params[cc_par_cnt++] = "-Qunused-arguments"; - - // in case LLVM is installed not via a package manager or "make install" - // e.g. compiled download or compiled from github then it's ./lib directory - // might not be in the search path. Add it if so. - u8 *libdir = strdup(LLVM_LIBDIR); - if (cpp_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) && - strncmp(libdir, "/lib", 4)) { - - cc_params[cc_par_cnt++] = "-rpath"; - cc_params[cc_par_cnt++] = libdir; - - } else { - - free(libdir); - - } - - u32 idx; - if (lto_mode && argc > 1) { - - for (idx = 1; idx < argc; idx++) { - - if (!strncasecmp(argv[idx], "-fpic", 5)) have_pic = 1; - - } - - if (!have_pic) cc_params[cc_par_cnt++] = "-fPIC"; - - } - - /* Detect stray -v calls from ./configure scripts. */ - - while (--argc) { - - u8 *cur = *(++argv); - - if (!strcmp(cur, "-m32")) bit_mode = 32; - if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32; - if (!strcmp(cur, "-m64")) bit_mode = 64; - - if (!strcmp(cur, "-x")) x_set = 1; - - if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory")) - asan_set = 1; - - if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; - - if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined")) - continue; - - if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue; - if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; - - if (!strcmp(cur, "-E")) preprocessor_only = 1; - if (!strcmp(cur, "-shared")) shared_linking = 1; - - cc_params[cc_par_cnt++] = cur; - - } - - if (getenv("AFL_HARDEN")) { - - cc_params[cc_par_cnt++] = "-fstack-protector-all"; - - if (!fortify_set) cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2"; - - } - - if (!asan_set) { - - if (getenv("AFL_USE_ASAN")) { - - if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive"); - - if (getenv("AFL_HARDEN")) - FATAL("ASAN and AFL_HARDEN are mutually exclusive"); - - cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; - cc_params[cc_par_cnt++] = "-fsanitize=address"; - - } else if (getenv("AFL_USE_MSAN")) { - - if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive"); - - if (getenv("AFL_HARDEN")) - FATAL("MSAN and AFL_HARDEN are mutually exclusive"); - - cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; - cc_params[cc_par_cnt++] = "-fsanitize=memory"; - - } - - } - - if (getenv("AFL_USE_UBSAN")) { - - cc_params[cc_par_cnt++] = "-fsanitize=undefined"; - cc_params[cc_par_cnt++] = "-fsanitize-undefined-trap-on-error"; - cc_params[cc_par_cnt++] = "-fno-sanitize-recover=all"; - - } - - if (getenv("AFL_USE_CFISAN")) { - - if (!lto_mode) { - - uint32_t i = 0, found = 0; - while (envp[i] != NULL && !found) - if (strncmp("-flto", envp[i++], 5) == 0) found = 1; - if (!found) cc_params[cc_par_cnt++] = "-flto"; - - } - - cc_params[cc_par_cnt++] = "-fsanitize=cfi"; - cc_params[cc_par_cnt++] = "-fvisibility=hidden"; - - } - - if (!getenv("AFL_DONT_OPTIMIZE")) { - - cc_params[cc_par_cnt++] = "-g"; - cc_params[cc_par_cnt++] = "-O3"; - cc_params[cc_par_cnt++] = "-funroll-loops"; - // if (strlen(march_opt) > 1 && march_opt[0] == '-') - // cc_params[cc_par_cnt++] = march_opt; - - } - - if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || - getenv("LAF_TRANSFORM_COMPARES") || lto_mode) { - - cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-bcmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strstr"; - cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr"; - - } - -#if defined(USEMMAP) && !defined(__HAIKU__) - cc_params[cc_par_cnt++] = "-lrt"; -#endif - - cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; - cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; - cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; - - /* When the user tries to use persistent or deferred forkserver modes by - appending a single line to the program, we want to reliably inject a - signature into the binary (to be picked up by afl-fuzz) and we want - to call a function from the runtime .o file. This is unnecessarily - painful for three reasons: - - 1) We need to convince the compiler not to optimize out the signature. - This is done with __attribute__((used)). - - 2) We need to convince the linker, when called with -Wl,--gc-sections, - not to do the same. This is done by forcing an assignment to a - 'volatile' pointer. - - 3) We need to declare __afl_persistent_loop() in the global namespace, - but doing this within a method in a class is hard - :: and extern "C" - are forbidden and __attribute__((alias(...))) doesn't work. Hence the - __asm__ aliasing trick. - - */ - - cc_params[cc_par_cnt++] = - "-D__AFL_FUZZ_INIT()=" - "int __afl_sharedmem_fuzzing = 1;" - "extern unsigned int *__afl_fuzz_len;" - "extern unsigned char *__afl_fuzz_ptr;" - "unsigned char __afl_fuzz_alt[1024000];" - "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; - cc_params[cc_par_cnt++] = - "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " - "__afl_fuzz_alt_ptr)"; - cc_params[cc_par_cnt++] = - "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : " - "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1024000)) == 0xffffffff " - "? 0 : *__afl_fuzz_len)"; - - cc_params[cc_par_cnt++] = - "-D__AFL_LOOP(_A)=" - "({ static volatile char *_B __attribute__((used)); " - " _B = (char*)\"" PERSIST_SIG - "\"; " -#ifdef __APPLE__ - "__attribute__((visibility(\"default\"))) " - "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " -#else - "__attribute__((visibility(\"default\"))) " - "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " -#endif /* ^__APPLE__ */ - "_L(_A); })"; - - cc_params[cc_par_cnt++] = - "-D__AFL_INIT()=" - "do { static volatile char *_A __attribute__((used)); " - " _A = (char*)\"" DEFER_SIG - "\"; " -#ifdef __APPLE__ - "__attribute__((visibility(\"default\"))) " - "void _I(void) __asm__(\"___afl_manual_init\"); " -#else - "__attribute__((visibility(\"default\"))) " - "void _I(void) __asm__(\"__afl_manual_init\"); " -#endif /* ^__APPLE__ */ - "_I(); } while (0)"; - - if (x_set) { - - cc_params[cc_par_cnt++] = "-x"; - cc_params[cc_par_cnt++] = "none"; - - } - - if (preprocessor_only) { - - /* In the preprocessor_only case (-E), we are not actually compiling at - all but requesting the compiler to output preprocessed sources only. - We must not add the runtime in this case because the compiler will - simply output its binary content back on stdout, breaking any build - systems that rely on a separate source preprocessing step. */ - cc_params[cc_par_cnt] = NULL; - return; - - } - -#ifndef __ANDROID__ - switch (bit_mode) { - - case 0: - cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt.o", obj_path); - if (lto_mode) - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); - break; - - case 32: - cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-32.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m32 is not supported by your compiler"); - if (lto_mode) { - - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m32 is not supported by your compiler"); - - } - - break; - - case 64: - cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-rt-64.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m64 is not supported by your compiler"); - if (lto_mode) { - - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m64 is not supported by your compiler"); - - } - - break; - - } - - #ifndef __APPLE__ - if (!shared_linking) - cc_params[cc_par_cnt++] = - alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); - #endif - -#endif - - cc_params[cc_par_cnt] = NULL; - -} - -/* Main entry point */ - -int main(int argc, char **argv, char **envp) { - - int i; - char *callname = "afl-clang-fast", *ptr = NULL; - - if (getenv("AFL_DEBUG")) { - - debug = 1; - if (strcmp(getenv("AFL_DEBUG"), "0") == 0) unsetenv("AFL_DEBUG"); - - } else if (getenv("AFL_QUIET")) - - be_quiet = 1; - - if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") || - getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) { - - if (instrument_mode == 0) - instrument_mode = INSTRUMENT_PCGUARD; - else if (instrument_mode != INSTRUMENT_PCGUARD) - FATAL("you can not set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together"); - - } - - if ((getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) && - getenv("AFL_DONT_OPTIMIZE")) - WARNF( - "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined " - "for file matching, only function matching!"); - - if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || - getenv("INSTRIM_LIB")) { - - if (instrument_mode == 0) - instrument_mode = INSTRUMENT_CFG; - else if (instrument_mode != INSTRUMENT_CFG) - FATAL( - "you can not set AFL_LLVM_INSTRUMENT and AFL_LLVM_INSTRIM together"); - - } - - if (getenv("AFL_LLVM_CTX")) instrument_opt_mode |= INSTRUMENT_OPT_CTX; - - if (getenv("AFL_LLVM_NGRAM_SIZE")) { - - instrument_opt_mode |= INSTRUMENT_OPT_NGRAM; - ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE")); - if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) - FATAL( - "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX " - "(%u)", - NGRAM_SIZE_MAX); - - } - - if (getenv("AFL_LLVM_INSTRUMENT")) { - - u8 *ptr = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); - - while (ptr) { - - if (strncasecmp(ptr, "afl", strlen("afl")) == 0 || - strncasecmp(ptr, "classic", strlen("classic")) == 0) { - - if (instrument_mode == INSTRUMENT_LTO) { - - instrument_mode = INSTRUMENT_CLASSIC; - lto_mode = 1; - - } else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL) - - instrument_mode = INSTRUMENT_AFL; - else - FATAL("main instrumentation mode already set with %s", - instrument_mode_string[instrument_mode]); - - } - - if (strncasecmp(ptr, "pc-guard", strlen("pc-guard")) == 0 || - strncasecmp(ptr, "pcguard", strlen("pcguard")) == 0) { - - if (!instrument_mode || instrument_mode == INSTRUMENT_PCGUARD) - instrument_mode = INSTRUMENT_PCGUARD; - else - FATAL("main instrumentation mode already set with %s", - instrument_mode_string[instrument_mode]); - - } - - if (strncasecmp(ptr, "cfg", strlen("cfg")) == 0 || - strncasecmp(ptr, "instrim", strlen("instrim")) == 0) { - - if (instrument_mode == INSTRUMENT_LTO) { - - instrument_mode = INSTRUMENT_CFG; - lto_mode = 1; - - } else if (!instrument_mode || instrument_mode == INSTRUMENT_CFG) - - instrument_mode = INSTRUMENT_CFG; - else - FATAL("main instrumentation mode already set with %s", - instrument_mode_string[instrument_mode]); - - } - - if (strncasecmp(ptr, "lto", strlen("lto")) == 0) { - - lto_mode = 1; - if (!instrument_mode || instrument_mode == INSTRUMENT_LTO) - instrument_mode = INSTRUMENT_LTO; - else if (instrument_mode != INSTRUMENT_CFG) - FATAL("main instrumentation mode already set with %s", - instrument_mode_string[instrument_mode]); - - } - - if (strncasecmp(ptr, "ctx", strlen("ctx")) == 0) { - - instrument_opt_mode |= INSTRUMENT_OPT_CTX; - setenv("AFL_LLVM_CTX", "1", 1); - - } - - if (strncasecmp(ptr, "ngram", strlen("ngram")) == 0) { - - ptr += strlen("ngram"); - while (*ptr && (*ptr < '0' || *ptr > '9')) { - - ptr++; - - } - - if (!*ptr) { - - ptr = getenv("AFL_LLVM_NGRAM_SIZE"); - if (!ptr || !*ptr) { - - FATAL( - "you must set the NGRAM size with (e.g. for value 2) " - "AFL_LLVM_INSTRUMENT=ngram-2"); - - } - - } - - ngram_size = atoi(ptr); - if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) - FATAL( - "NGRAM instrumentation option must be between 2 and " - "NGRAM_SIZE_MAX " - "(%u)", - NGRAM_SIZE_MAX); - instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM); - ptr = alloc_printf("%u", ngram_size); - setenv("AFL_LLVM_NGRAM_SIZE", ptr, 1); - - } - - ptr = strtok(NULL, ":,;"); - - } - - } - - if (strstr(argv[0], "afl-clang-lto") != NULL) { - - if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO || - instrument_mode == INSTRUMENT_CFG) { - - lto_mode = 1; - callname = "afl-clang-lto"; - if (!instrument_mode) { - - instrument_mode = INSTRUMENT_CFG; - ptr = instrument_mode_string[instrument_mode]; - - } - - } else if (instrument_mode == INSTRUMENT_LTO || - - instrument_mode == INSTRUMENT_CLASSIC) { - - lto_mode = 1; - callname = "afl-clang-lto"; - - } else { - - if (!be_quiet) - WARNF("afl-clang-lto called with mode %s, using that mode instead", - instrument_mode_string[instrument_mode]); - - } - - } - - if (instrument_mode == 0) { - -#if LLVM_VERSION_MAJOR <= 6 - instrument_mode = INSTRUMENT_AFL; -#else - if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { - - instrument_mode = INSTRUMENT_AFL; - WARNF( - "switching to classic instrumentation because " - "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD. Use " - "-fsanitize-coverage-allowlist=allowlist.txt or " - "-fsanitize-coverage-blocklist=denylist.txt if you want to use " - "PCGUARD. Requires llvm 12+. See https://clang.llvm.org/docs/ " - "SanitizerCoverage.html#partially-disabling-instrumentation"); - - } else - - instrument_mode = INSTRUMENT_PCGUARD; -#endif - - } - - if (instrument_opt_mode && lto_mode) - FATAL( - "CTX and NGRAM can not be used in LTO mode (and would make LTO " - "useless)"); - - if (!instrument_opt_mode) { - - if (lto_mode && instrument_mode == INSTRUMENT_CFG) - ptr = alloc_printf("InsTrimLTO"); - else - ptr = instrument_mode_string[instrument_mode]; - - } else if (instrument_opt_mode == INSTRUMENT_OPT_CTX) - - ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]); - else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM) - ptr = alloc_printf("%s + NGRAM-%u", instrument_mode_string[instrument_mode], - ngram_size); - else - ptr = alloc_printf("%s + CTX + NGRAM-%u", - instrument_mode_string[instrument_mode], ngram_size); - -#ifndef AFL_CLANG_FLTO - if (lto_mode) - FATAL( - "instrumentation mode LTO specified but LLVM support not available " - "(requires LLVM 11 or higher)"); -#endif - - if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC && - instrument_mode != INSTRUMENT_CFG) - FATAL( - "CTX and NGRAM instrumentation options can only be used with CFG " - "(recommended) and CLASSIC instrumentation modes!"); - - if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO")) - FATAL( - "AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set " - "together"); - - if (instrument_mode == INSTRUMENT_PCGUARD && - (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST"))) - FATAL( - "Instrumentation type PCGUARD does not support " - "AFL_LLVM_ALLOWLIST/DENYLIST! Use " - "-fsanitize-coverage-allowlist=allowlist.txt or " - "-fsanitize-coverage-blocklist=denylist.txt instead (requires llvm " - "12+), see " - "https://clang.llvm.org/docs/" - "SanitizerCoverage.html#partially-disabling-instrumentation"); - - if (argc < 2 || strcmp(argv[1], "-h") == 0) { - - if (!lto_mode) - printf("afl-clang-fast" VERSION " by in %s mode\n", - ptr); - else - printf("afl-clang-lto" VERSION - " by Marc \"vanHauser\" Heuse in %s mode\n", - ptr); - - SAYF( - "\n" - "%s[++] [options]\n" - "\n" - "This is a helper application for afl-fuzz. It serves as a drop-in " - "replacement\n" - "for clang, letting you recompile third-party code with the " - "required " - "runtime\n" - "instrumentation. A common use pattern would be one of the " - "following:\n\n" - - " CC=%s/afl-clang-fast ./configure\n" - " CXX=%s/afl-clang-fast++ ./configure\n\n" - - "In contrast to the traditional afl-clang tool, this version is " - "implemented as\n" - "an LLVM pass and tends to offer improved performance with slow " - "programs.\n\n" - - "Environment variables used:\n" - "AFL_CC: path to the C compiler to use\n" - "AFL_CXX: path to the C++ compiler to use\n" - "AFL_DEBUG: enable developer debugging output\n" - "AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" - "AFL_HARDEN: adds code hardening to catch memory bugs\n" - "AFL_INST_RATIO: percentage of branches to instrument\n" -#if LLVM_VERSION_MAJOR < 9 - "AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" -#else - "AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" -#endif - "AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n" - "AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n" - "AFL_LLVM_LAF_SPLIT_SWITCHES: casc. comp. in 'switch'\n" - " to cascaded comparisons\n" - "AFL_LLVM_LAF_SPLIT_FLOATS: transform floating point comp. to " - "cascaded comp.\n" - "AFL_LLVM_LAF_TRANSFORM_COMPARES: transform library comparison " - "function calls\n" - "AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n" - "AFL_LLVM_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: enable instrument" - "allow/deny listing (selective instrumentation)\n" - "AFL_NO_BUILTIN: compile for use with libtokencap.so\n" - "AFL_PATH: path to instrumenting pass and runtime " - "(afl-llvm-rt.*o)\n" - "AFL_LLVM_DOCUMENT_IDS: document edge IDs given to which function (LTO " - "only)\n" - "AFL_QUIET: suppress verbose output\n" - "AFL_USE_ASAN: activate address sanitizer\n" - "AFL_USE_CFISAN: activate control flow sanitizer\n" - "AFL_USE_MSAN: activate memory sanitizer\n" - "AFL_USE_UBSAN: activate undefined behaviour sanitizer\n", - callname, BIN_PATH, BIN_PATH); - - SAYF( - "\nafl-clang-fast specific environment variables:\n" - "AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen mutator)\n" - "AFL_LLVM_INSTRUMENT: set instrumentation mode: AFL, CFG " - "(INSTRIM), PCGUARD [DEFAULT], LTO, CTX, NGRAM-2 ... NGRAM-16\n" - " You can also use the old environment variables instead:\n" - " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation " - "[DEFAULT]\n" - " AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n" - " AFL_LLVM_INSTRIM_LOOPHEAD: optimize loop tracing for speed (" - "option to INSTRIM)\n" - " AFL_LLVM_CTX: use context sensitive coverage\n" - " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage\n"); - -#ifdef AFL_CLANG_FLTO - SAYF( - "\nafl-clang-lto specific environment variables:\n" - "AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), e.g. " - "0x10000\n" - "AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding " - "functions they are in into this file\n" - "AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " - "global var\n" - "AFL_LLVM_LTO_STARTID: from which ID to start counting from for a " - "bb\n" - "AFL_REAL_LD: use this lld linker instead of the compiled in path\n" - "\nafl-clang-lto was built with linker target \"%s\" and LTO flags " - "\"%s\"\n" - "If anything fails - be sure to read README.lto.md!\n", - AFL_REAL_LD, AFL_CLANG_FLTO); -#endif - - SAYF( - "\nafl-clang-fast was built for llvm %s with the llvm binary path " - "of \"%s\".\n", - LLVM_VERSION, LLVM_BINDIR); - - SAYF("\n"); - - exit(1); - - } else if ((isatty(2) && !be_quiet) || - - getenv("AFL_DEBUG") != NULL) { - - if (!lto_mode) - - SAYF(cCYA "afl-clang-fast" VERSION cRST - " by in %s mode\n", - ptr); - - else - - SAYF(cCYA "afl-clang-lto" VERSION cRST - " by Marc \"vanHauser\" Heuse in mode %s\n", - ptr); - - } - - u8 *ptr2; - if (!be_quiet && !lto_mode && - ((ptr2 = getenv("AFL_MAP_SIZE")) || (ptr2 = getenv("AFL_MAPSIZE")))) { - - u32 map_size = atoi(ptr2); - if (map_size != MAP_SIZE) - WARNF("AFL_MAP_SIZE is not supported by afl-clang-fast"); - - } - - if (debug) { - - SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); - for (i = 0; i < argc; i++) - SAYF(" \"%s\"", argv[i]); - SAYF("\n"); - - } - - check_environment_vars(envp); - - if (getenv("AFL_LLVM_LAF_ALL")) { - - setenv("AFL_LLVM_LAF_SPLIT_SWITCHES", "1", 1); - setenv("AFL_LLVM_LAF_SPLIT_COMPARES", "1", 1); - setenv("AFL_LLVM_LAF_SPLIT_FLOATS", "1", 1); - setenv("AFL_LLVM_LAF_TRANSFORM_COMPARES", "1", 1); - - } - - cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG"); - if (!be_quiet && cmplog_mode) - printf("CmpLog mode by \n"); - -#ifndef __ANDROID__ - find_obj(argv[0]); -#endif - - edit_params(argc, argv, envp); - - if (debug) { - - SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); - for (i = 0; i < cc_par_cnt; i++) - SAYF(" \"%s\"", cc_params[i]); - SAYF("\n"); - - } - - execvp(cc_params[0], (char **)cc_params); - - FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]); - - return 0; - -} - diff --git a/llvm_mode/afl-ld-lto.c b/llvm_mode/afl-ld-lto.c deleted file mode 100644 index 771e2d0d..00000000 --- a/llvm_mode/afl-ld-lto.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - american fuzzy lop++ - wrapper for llvm 11+ lld - ----------------------------------------------- - - Written by Marc Heuse for afl++ - - Maintained by Marc Heuse , - Heiko Eißfeldt - Andrea Fioraldi - Dominik Maier - - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - The sole purpose of this wrapper is to preprocess clang LTO files when - linking with lld and performing the instrumentation on the whole program. - -*/ - -#define AFL_MAIN -#define _GNU_SOURCE - -#include "config.h" -#include "types.h" -#include "debug.h" -#include "alloc-inl.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#define MAX_PARAM_COUNT 4096 - -static u8 **ld_params; /* Parameters passed to the real 'ld' */ - -static u8 *afl_path = AFL_PATH; -static u8 *real_ld = AFL_REAL_LD; - -static u8 be_quiet, /* Quiet mode (no stderr output) */ - debug, /* AFL_DEBUG */ - passthrough, /* AFL_LD_PASSTHROUGH - no link+optimize*/ - just_version; /* Just show version? */ - -static u32 ld_param_cnt = 1; /* Number of params to 'ld' */ - -/* Examine and modify parameters to pass to 'ld', 'llvm-link' and 'llmv-ar'. - Note that the file name is always the last parameter passed by GCC, - so we exploit this property to keep the code "simple". */ -static void edit_params(int argc, char **argv) { - - u32 i, instrim = 0, gold_pos = 0, gold_present = 0, rt_present = 0, - rt_lto_present = 0, inst_present = 0; - char *ptr; - - ld_params = ck_alloc(4096 * sizeof(u8 *)); - - ld_params[0] = (u8 *)real_ld; - - if (!passthrough) { - - for (i = 1; i < argc; i++) { - - if (strstr(argv[i], "/afl-llvm-rt-lto.o") != NULL) rt_lto_present = 1; - if (strstr(argv[i], "/afl-llvm-rt.o") != NULL) rt_present = 1; - if (strstr(argv[i], "/afl-llvm-lto-instr") != NULL) inst_present = 1; - - } - - for (i = 1; i < argc && !gold_pos; i++) { - - if (strcmp(argv[i], "-plugin") == 0) { - - if (strncmp(argv[i], "-plugin=", strlen("-plugin=")) == 0) { - - if (strcasestr(argv[i], "LLVMgold.so") != NULL) - gold_present = gold_pos = i + 1; - - } else if (i < argc && strcasestr(argv[i + 1], "LLVMgold.so") != NULL) { - - gold_present = gold_pos = i + 2; - - } - - } - - } - - if (!gold_pos) { - - for (i = 1; i + 1 < argc && !gold_pos; i++) { - - if (argv[i][0] != '-') { - - if (argv[i - 1][0] == '-') { - - switch (argv[i - 1][1]) { - - case 'b': - break; - case 'd': - break; - case 'e': - break; - case 'F': - break; - case 'f': - break; - case 'I': - break; - case 'l': - break; - case 'L': - break; - case 'm': - break; - case 'o': - break; - case 'O': - break; - case 'p': - if (index(argv[i - 1], '=') == NULL) gold_pos = i; - break; - case 'R': - break; - case 'T': - break; - case 'u': - break; - case 'y': - break; - case 'z': - break; - case '-': { - - if (strcmp(argv[i - 1], "--oformat") == 0) break; - if (strcmp(argv[i - 1], "--output") == 0) break; - if (strncmp(argv[i - 1], "--opt-remarks-", 14) == 0) break; - gold_pos = i; - break; - - } - - default: - gold_pos = i; - - } - - } else - - gold_pos = i; - - } - - } - - } - - if (!gold_pos) gold_pos = 1; - - } - - if (getenv("AFL_LLVM_INSTRIM")) - instrim = 1; - else if ((ptr = getenv("AFL_LLVM_INSTRUMENT")) && - (strcasestr(ptr, "CFG") == 0 || strcasestr(ptr, "INSTRIM") == 0)) - instrim = 1; - - if (debug) - SAYF(cMGN "[D] " cRST - "passthrough=%s instrim=%d, gold_pos=%d, gold_present=%s " - "inst_present=%s rt_present=%s rt_lto_present=%s\n", - passthrough ? "true" : "false", instrim, gold_pos, - gold_present ? "true" : "false", inst_present ? "true" : "false", - rt_present ? "true" : "false", rt_lto_present ? "true" : "false"); - - for (i = 1; i < argc; i++) { - - if (ld_param_cnt >= MAX_PARAM_COUNT) - FATAL( - "Too many command line parameters because of unpacking .a archives, " - "this would need to be done by hand ... sorry! :-("); - - if (strcmp(argv[i], "--afl") == 0) { - - if (!be_quiet) OKF("afl++ test command line flag detected, exiting."); - exit(0); - - } - - if (i == gold_pos && !passthrough) { - - ld_params[ld_param_cnt++] = alloc_printf("-L%s/../lib", LLVM_BINDIR); - - if (!gold_present) { - - ld_params[ld_param_cnt++] = "-plugin"; - ld_params[ld_param_cnt++] = - alloc_printf("%s/../lib/LLVMgold.so", LLVM_BINDIR); - - } - - ld_params[ld_param_cnt++] = "--allow-multiple-definition"; - - if (!inst_present) { - - if (instrim) - ld_params[ld_param_cnt++] = - alloc_printf("-mllvm=-load=%s/afl-llvm-lto-instrim.so", afl_path); - else - ld_params[ld_param_cnt++] = alloc_printf( - "-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", afl_path); - - } - - if (!rt_present) - ld_params[ld_param_cnt++] = alloc_printf("%s/afl-llvm-rt.o", afl_path); - if (!rt_lto_present) - ld_params[ld_param_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto.o", afl_path); - - } - - ld_params[ld_param_cnt++] = argv[i]; - - } - - ld_params[ld_param_cnt] = NULL; - -} - -/* Main entry point */ - -int main(int argc, char **argv) { - - s32 pid, i, status; - u8 * ptr; - char thecwd[PATH_MAX]; - - if ((ptr = getenv("AFL_LD_CALLER")) != NULL) { - - FATAL("ld loop detected! Set AFL_REAL_LD!\n"); - - } - - if (isatty(2) && !getenv("AFL_QUIET") && !getenv("AFL_DEBUG")) { - - SAYF(cCYA "afl-ld-to" VERSION cRST - " by Marc \"vanHauser\" Heuse \n"); - - } else - - be_quiet = 1; - - if (getenv("AFL_DEBUG") != NULL) debug = 1; - if (getenv("AFL_PATH") != NULL) afl_path = getenv("AFL_PATH"); - if (getenv("AFL_LD_PASSTHROUGH") != NULL) passthrough = 1; - if (getenv("AFL_REAL_LD") != NULL) real_ld = getenv("AFL_REAL_LD"); - - if (!afl_path || !*afl_path) afl_path = "/usr/local/lib/afl"; - - setenv("AFL_LD_CALLER", "1", 1); - - if (debug) { - - if (getcwd(thecwd, sizeof(thecwd)) != 0) strcpy(thecwd, "."); - - SAYF(cMGN "[D] " cRST "cd \"%s\";", thecwd); - for (i = 0; i < argc; i++) - SAYF(" \"%s\"", argv[i]); - SAYF("\n"); - - } - - if (argc < 2) { - - SAYF( - "\n" - "This is a helper application for afl-clang-lto. It is a wrapper " - "around GNU " - "llvm's 'lld',\n" - "executed by the toolchain whenever using " - "afl-clang-lto/afl-clang-lto++.\n" - "You probably don't want to run this program directly but rather pass " - "it as LD parameter to configure scripts\n\n" - - "Environment variables:\n" - " AFL_LD_PASSTHROUGH do not link+optimize == no instrumentation\n" - " AFL_REAL_LD point to the real llvm 11 lld if necessary\n" - - "\nafl-ld-to was compiled with the fixed real 'ld' of %s and the " - "binary path of %s\n\n", - real_ld, LLVM_BINDIR); - - exit(1); - - } - - edit_params(argc, argv); // here most of the magic happens :-) - - if (debug) { - - SAYF(cMGN "[D]" cRST " cd \"%s\";", thecwd); - for (i = 0; i < ld_param_cnt; i++) - SAYF(" \"%s\"", ld_params[i]); - SAYF("\n"); - - } - - if (!(pid = fork())) { - - if (strlen(real_ld) > 1) execvp(real_ld, (char **)ld_params); - execvp("ld", (char **)ld_params); // fallback - FATAL("Oops, failed to execute 'ld' - check your PATH"); - - } - - if (pid < 0) PFATAL("fork() failed"); - - if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed"); - if (debug) SAYF(cMGN "[D] " cRST "linker result: %d\n", status); - - if (!just_version) { - - if (status == 0) { - - if (!be_quiet) OKF("Linker was successful"); - - } else { - - SAYF(cLRD "[-] " cRST - "Linker failed, please investigate and send a bug report. Most " - "likely an 'ld' option is incompatible with %s.\n", - AFL_CLANG_FLTO); - - } - - } - - exit(WEXITSTATUS(status)); - -} - diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc deleted file mode 100644 index 189b4ec6..00000000 --- a/llvm_mode/afl-llvm-common.cc +++ /dev/null @@ -1,575 +0,0 @@ -#define AFL_LLVM_PASS - -#include "config.h" -#include "debug.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#define IS_EXTERN extern -#include "afl-llvm-common.h" - -using namespace llvm; - -static std::list allowListFiles; -static std::list allowListFunctions; -static std::list denyListFiles; -static std::list denyListFunctions; - -char *getBBName(const llvm::BasicBlock *BB) { - - static char *name; - - if (!BB->getName().empty()) { - - name = strdup(BB->getName().str().c_str()); - return name; - - } - - std::string Str; - raw_string_ostream OS(Str); - -#if LLVM_VERSION_MAJOR >= 4 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) - BB->printAsOperand(OS, false); -#endif - name = strdup(OS.str().c_str()); - return name; - -} - -/* Function that we never instrument or analyze */ -/* Note: this ignore check is also called in isInInstrumentList() */ -bool isIgnoreFunction(const llvm::Function *F) { - - // Starting from "LLVMFuzzer" these are functions used in libfuzzer based - // fuzzing campaign installations, e.g. oss-fuzz - - static const char *ignoreList[] = { - - "asan.", - "llvm.", - "sancov.", - "__ubsan_", - "ign.", - "__afl_", - "_fini", - "__libc_csu", - "__asan", - "__msan", - "__cmplog", - "__sancov", - "msan.", - "LLVMFuzzer", - "__decide_deferred", - "maybe_duplicate_stderr", - "discard_output", - "close_stdout", - "dup_and_close_stderr", - "maybe_close_fd_mask", - "ExecuteFilesOnyByOne" - - }; - - for (auto const &ignoreListFunc : ignoreList) { - - if (F->getName().startswith(ignoreListFunc)) { return true; } - - } - - return false; - -} - -void initInstrumentList() { - - char *allowlist = getenv("AFL_LLVM_ALLOWLIST"); - if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); - if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); - char *denylist = getenv("AFL_LLVM_DENYLIST"); - if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); - - if (allowlist && denylist) - FATAL( - "You can only specify either AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST " - "but not both!"); - - if (allowlist) { - - std::string line; - std::ifstream fileStream; - fileStream.open(allowlist); - if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_ALLOWLIST"); - getline(fileStream, line); - - while (fileStream) { - - int is_file = -1; - std::size_t npos; - std::string original_line = line; - - line.erase(std::remove_if(line.begin(), line.end(), ::isspace), - line.end()); - - // remove # and following - if ((npos = line.find("#")) != std::string::npos) - line = line.substr(0, npos); - - if (line.compare(0, 4, "fun:") == 0) { - - is_file = 0; - line = line.substr(4); - - } else if (line.compare(0, 9, "function:") == 0) { - - is_file = 0; - line = line.substr(9); - - } else if (line.compare(0, 4, "src:") == 0) { - - is_file = 1; - line = line.substr(4); - - } else if (line.compare(0, 7, "source:") == 0) { - - is_file = 1; - line = line.substr(7); - - } - - if (line.find(":") != std::string::npos) { - - FATAL("invalid line in AFL_LLVM_ALLOWLIST: %s", original_line.c_str()); - - } - - if (line.length() > 0) { - - // if the entry contains / or . it must be a file - if (is_file == -1) - if (line.find("/") != std::string::npos || - line.find(".") != std::string::npos) - is_file = 1; - // otherwise it is a function - - if (is_file == 1) - allowListFiles.push_back(line); - else - allowListFunctions.push_back(line); - getline(fileStream, line); - - } - - } - - if (debug) - SAYF(cMGN "[D] " cRST - "loaded allowlist with %zu file and %zu function entries\n", - allowListFiles.size(), allowListFunctions.size()); - - } - - if (denylist) { - - std::string line; - std::ifstream fileStream; - fileStream.open(denylist); - if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_DENYLIST"); - getline(fileStream, line); - - while (fileStream) { - - int is_file = -1; - std::size_t npos; - std::string original_line = line; - - line.erase(std::remove_if(line.begin(), line.end(), ::isspace), - line.end()); - - // remove # and following - if ((npos = line.find("#")) != std::string::npos) - line = line.substr(0, npos); - - if (line.compare(0, 4, "fun:") == 0) { - - is_file = 0; - line = line.substr(4); - - } else if (line.compare(0, 9, "function:") == 0) { - - is_file = 0; - line = line.substr(9); - - } else if (line.compare(0, 4, "src:") == 0) { - - is_file = 1; - line = line.substr(4); - - } else if (line.compare(0, 7, "source:") == 0) { - - is_file = 1; - line = line.substr(7); - - } - - if (line.find(":") != std::string::npos) { - - FATAL("invalid line in AFL_LLVM_DENYLIST: %s", original_line.c_str()); - - } - - if (line.length() > 0) { - - // if the entry contains / or . it must be a file - if (is_file == -1) - if (line.find("/") != std::string::npos || - line.find(".") != std::string::npos) - is_file = 1; - // otherwise it is a function - - if (is_file == 1) - denyListFiles.push_back(line); - else - denyListFunctions.push_back(line); - getline(fileStream, line); - - } - - } - - if (debug) - SAYF(cMGN "[D] " cRST - "loaded denylist with %zu file and %zu function entries\n", - denyListFiles.size(), denyListFunctions.size()); - - } - -} - -void scanForDangerousFunctions(llvm::Module *M) { - - if (!M) return; - -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 9) - - for (GlobalIFunc &IF : M->ifuncs()) { - - StringRef ifunc_name = IF.getName(); - Constant *r = IF.getResolver(); - StringRef r_name = cast(r->getOperand(0))->getName(); - if (!be_quiet) - fprintf(stderr, - "Info: Found an ifunc with name %s that points to resolver " - "function %s, we will not instrument this, putting it into the " - "block list.\n", - ifunc_name.str().c_str(), r_name.str().c_str()); - denyListFunctions.push_back(r_name.str()); - - } - - GlobalVariable *GV = M->getNamedGlobal("llvm.global_ctors"); - if (GV && !GV->isDeclaration() && !GV->hasLocalLinkage()) { - - ConstantArray *InitList = dyn_cast(GV->getInitializer()); - - if (InitList) { - - for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) { - - if (ConstantStruct *CS = - dyn_cast(InitList->getOperand(i))) { - - if (CS->getNumOperands() >= 2) { - - if (CS->getOperand(1)->isNullValue()) - break; // Found a null terminator, stop here. - - ConstantInt *CI = dyn_cast(CS->getOperand(0)); - int Priority = CI ? CI->getSExtValue() : 0; - - Constant *FP = CS->getOperand(1); - if (ConstantExpr *CE = dyn_cast(FP)) - if (CE->isCast()) FP = CE->getOperand(0); - if (Function *F = dyn_cast(FP)) { - - if (!F->isDeclaration() && - strncmp(F->getName().str().c_str(), "__afl", 5) != 0) { - - if (!be_quiet) - fprintf(stderr, - "Info: Found constructor function %s with prio " - "%u, we will not instrument this, putting it into a " - "block list.\n", - F->getName().str().c_str(), Priority); - denyListFunctions.push_back(F->getName().str()); - - } - - } - - } - - } - - } - - } - - } - -#endif - -} - -static std::string getSourceName(llvm::Function *F) { - - // let's try to get the filename for the function - auto bb = &F->getEntryBlock(); - BasicBlock::iterator IP = bb->getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - DebugLoc Loc = IP->getDebugLoc(); - -#if LLVM_VERSION_MAJOR >= 4 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) - if (Loc) { - - StringRef instFilename; - DILocation *cDILoc = dyn_cast(Loc.getAsMDNode()); - - if (cDILoc) { instFilename = cDILoc->getFilename(); } - - if (instFilename.str().empty()) { - - /* If the original location is empty, try using the inlined location - */ - DILocation *oDILoc = cDILoc->getInlinedAt(); - if (oDILoc) { instFilename = oDILoc->getFilename(); } - - } - - return instFilename.str(); - - } - -#else - if (!Loc.isUnknown()) { - - DILocation cDILoc(Loc.getAsMDNode(F->getContext())); - - StringRef instFilename = cDILoc.getFilename(); - - /* Continue only if we know where we actually are */ - return instFilename.str(); - - } - -#endif - - return std::string(""); - -} - -bool isInInstrumentList(llvm::Function *F) { - - bool return_default = true; - - // is this a function with code? If it is external we don't instrument it - // anyway and it can't be in the instrument file list. Or if it is it is - // ignored. - if (!F->size() || isIgnoreFunction(F)) return false; - - if (!denyListFiles.empty() || !denyListFunctions.empty()) { - - if (!denyListFunctions.empty()) { - - std::string instFunction = F->getName().str(); - - for (std::list::iterator it = denyListFunctions.begin(); - it != denyListFunctions.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (instFunction.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - - if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the deny function list, " - "not instrumenting ... \n", - instFunction.c_str()); - return false; - - } - - } - - } - - } - - if (!denyListFiles.empty()) { - - std::string source_file = getSourceName(F); - - if (!source_file.empty()) { - - for (std::list::iterator it = denyListFiles.begin(); - it != denyListFiles.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (source_file.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - - return false; - - } - - } - - } - - } else { - - // we could not find out the location. in this case we say it is not - // in the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will be " - "instrumented (recompile with -g -O[1-3]).", - F->getName().str().c_str()); - - } - - } - - } - - // if we do not have a instrument file list return true - if (!allowListFiles.empty() || !allowListFunctions.empty()) { - - return_default = false; - - if (!allowListFunctions.empty()) { - - std::string instFunction = F->getName().str(); - - for (std::list::iterator it = allowListFunctions.begin(); - it != allowListFunctions.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (instFunction.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - - if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allow function list, " - "instrumenting ... \n", - instFunction.c_str()); - return true; - - } - - } - - } - - } - - if (!allowListFiles.empty()) { - - std::string source_file = getSourceName(F); - - if (!source_file.empty()) { - - for (std::list::iterator it = allowListFiles.begin(); - it != allowListFiles.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (source_file.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - - if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allowlist (%s), " - "instrumenting ... \n", - F->getName().str().c_str(), source_file.c_str()); - return true; - - } - - } - - } - - } else { - - // we could not find out the location. In this case we say it is not - // in the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will not be " - "instrumented (recompile with -g -O[1-3]).", - F->getName().str().c_str()); - return false; - - } - - } - - } - - return return_default; - -} - -// Calculate the number of average collisions that would occur if all -// location IDs would be assigned randomly (like normal afl/afl++). -// This uses the "balls in bins" algorithm. -unsigned long long int calculateCollisions(uint32_t edges) { - - double bins = MAP_SIZE; - double balls = edges; - double step1 = 1 - (1 / bins); - double step2 = pow(step1, balls); - double step3 = bins * step2; - double step4 = round(step3); - unsigned long long int empty = step4; - unsigned long long int collisions = edges - (MAP_SIZE - empty); - return collisions; - -} - diff --git a/llvm_mode/afl-llvm-common.h b/llvm_mode/afl-llvm-common.h deleted file mode 100644 index a1561d9c..00000000 --- a/llvm_mode/afl-llvm-common.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef __AFLLLVMCOMMON_H -#define __AFLLLVMCOMMON_H - -#include -#include -#include - -#include -#include -#include -#include - -#include "llvm/Config/llvm-config.h" -#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5 -typedef long double max_align_t; -#endif - -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/MathExtras.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" - -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) - #include "llvm/IR/DebugInfo.h" - #include "llvm/IR/CFG.h" -#else - #include "llvm/DebugInfo.h" - #include "llvm/Support/CFG.h" -#endif - -char * getBBName(const llvm::BasicBlock *BB); -bool isIgnoreFunction(const llvm::Function *F); -void initInstrumentList(); -bool isInInstrumentList(llvm::Function *F); -unsigned long long int calculateCollisions(uint32_t edges); -void scanForDangerousFunctions(llvm::Module *M); - -#ifndef IS_EXTERN - #define IS_EXTERN -#endif - -IS_EXTERN int debug; -IS_EXTERN int be_quiet; - -#undef IS_EXTERN - -#endif - diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc deleted file mode 100644 index 125db229..00000000 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ /dev/null @@ -1,957 +0,0 @@ -/* - american fuzzy lop++ - LLVM LTO instrumentation pass - ---------------------------------------------------- - - Written by Marc Heuse - - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This library is plugged into LLVM when invoking clang through afl-clang-lto. - - */ - -#define AFL_LLVM_PASS - -#include "config.h" -#include "debug.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "llvm/Config/llvm-config.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/DebugInfo.h" -#include "llvm/IR/CFG.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Analysis/LoopInfo.h" -#include "llvm/Analysis/MemorySSAUpdater.h" -#include "llvm/Analysis/ValueTracking.h" -#include "llvm/Pass.h" -#include "llvm/IR/Constants.h" - -#include "afl-llvm-common.h" - -using namespace llvm; - -namespace { - -class AFLLTOPass : public ModulePass { - - public: - static char ID; - - AFLLTOPass() : ModulePass(ID) { - - char *ptr; - - if (getenv("AFL_DEBUG")) debug = 1; - if ((ptr = getenv("AFL_LLVM_LTO_STARTID")) != NULL) - if ((afl_global_id = atoi(ptr)) < 0 || afl_global_id >= MAP_SIZE) - FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is not between 0 and %d\n", - ptr, MAP_SIZE - 1); - - skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); - - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - - ModulePass::getAnalysisUsage(AU); - AU.addRequired(); - AU.addRequired(); - - } - - bool runOnModule(Module &M) override; - - protected: - int afl_global_id = 1, autodictionary = 1; - uint32_t function_minimum_size = 1; - uint32_t inst_blocks = 0, inst_funcs = 0, total_instr = 0; - uint64_t map_addr = 0x10000; - char * skip_nozero = NULL; - -}; - -} // namespace - -bool AFLLTOPass::runOnModule(Module &M) { - - LLVMContext & C = M.getContext(); - std::vector dictionary; - std::vector calls; - DenseMap valueMap; - std::vector BlockList; - char * ptr; - FILE * documentFile = NULL; - - srand((unsigned int)time(NULL)); - - unsigned long long int moduleID = - (((unsigned long long int)(rand() & 0xffffffff)) << 32) | getpid(); - - IntegerType *Int8Ty = IntegerType::getInt8Ty(C); - IntegerType *Int32Ty = IntegerType::getInt32Ty(C); - IntegerType *Int64Ty = IntegerType::getInt64Ty(C); - - /* Show a banner */ - setvbuf(stdout, NULL, _IONBF, 0); - - if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { - - SAYF(cCYA "afl-llvm-lto" VERSION cRST - " by Marc \"vanHauser\" Heuse \n"); - - } else - - be_quiet = 1; - - if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { - - if ((documentFile = fopen(ptr, "a")) == NULL) - WARNF("Cannot access document file %s", ptr); - - } - - // we make this the default as the fixed map has problems with - // defered forkserver, early constructors, ifuncs and maybe more - /*if (getenv("AFL_LLVM_MAP_DYNAMIC"))*/ - map_addr = 0; - - if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { - - uint64_t val; - if (!*ptr || !strcmp(ptr, "0") || !strcmp(ptr, "0x0")) { - - map_addr = 0; - - } else if (getenv("AFL_LLVM_MAP_DYNAMIC")) { - - FATAL( - "AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used together"); - - } else if (strncmp(ptr, "0x", 2) != 0) { - - map_addr = 0x10000; // the default - - } else { - - val = strtoull(ptr, NULL, 16); - if (val < 0x100 || val > 0xffffffff00000000) { - - FATAL( - "AFL_LLVM_MAP_ADDR must be a value between 0x100 and " - "0xffffffff00000000"); - - } - - map_addr = val; - - } - - } - - if (debug) { fprintf(stderr, "map address is 0x%lx\n", map_addr); } - - /* Get/set the globals for the SHM region. */ - - GlobalVariable *AFLMapPtr = NULL; - Value * MapPtrFixed = NULL; - - if (!map_addr) { - - AFLMapPtr = - new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, - GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); - - } else { - - ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr); - MapPtrFixed = - ConstantExpr::getIntToPtr(MapAddr, PointerType::getUnqual(Int8Ty)); - - } - - ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); - ConstantInt *One = ConstantInt::get(Int8Ty, 1); - - // This dumps all inialized global strings - might be useful in the future - /* - for (auto G=M.getGlobalList().begin(); G!=M.getGlobalList().end(); G++) { - - GlobalVariable &GV=*G; - if (!GV.getName().str().empty()) { - - fprintf(stderr, "Global Variable: %s", GV.getName().str().c_str()); - if (GV.hasInitializer()) - if (auto *Val = dyn_cast(GV.getInitializer())) - fprintf(stderr, " Value: \"%s\"", Val->getAsString().str().c_str()); - fprintf(stderr, "\n"); - - } - - } - - */ - - scanForDangerousFunctions(&M); - - /* Instrument all the things! */ - - int inst_blocks = 0; - - for (auto &F : M) { - - /*For debugging - AttributeSet X = F.getAttributes().getFnAttributes(); - fprintf(stderr, "DEBUG: Module %s Function %s attributes %u\n", - M.getName().str().c_str(), F.getName().str().c_str(), - X.getNumAttributes()); - */ - - if (F.size() < function_minimum_size) continue; - if (isIgnoreFunction(&F)) continue; - - // the instrument file list check - AttributeList Attrs = F.getAttributes(); - if (Attrs.hasAttribute(-1, StringRef("skipinstrument"))) { - - if (debug) - fprintf(stderr, - "DEBUG: Function %s is not in a source file that was specified " - "in the instrument file list\n", - F.getName().str().c_str()); - continue; - - } - - std::vector InsBlocks; - - if (autodictionary) { - - /* Some implementation notes. - * - * We try to handle 3 cases: - * - memcmp("foo", arg, 3) <- literal string - * - static char globalvar[] = "foo"; - * memcmp(globalvar, arg, 3) <- global variable - * - char localvar[] = "foo"; - * memcmp(locallvar, arg, 3) <- local variable - * - * The local variable case is the hardest. We can only detect that - * case if there is no reassignment or change in the variable. - * And it might not work across llvm version. - * What we do is hooking the initializer function for local variables - * (llvm.memcpy.p0i8.p0i8.i64) and note the string and the assigned - * variable. And if that variable is then used in a compare function - * we use that noted string. - * This seems not to work for tokens that have a size <= 4 :-( - * - * - if the compared length is smaller than the string length we - * save the full string. This is likely better for fuzzing but - * might be wrong in a few cases depending on optimizers - * - * - not using StringRef because there is a bug in the llvm 11 - * checkout I am using which sometimes points to wrong strings - * - * Over and out. Took me a full day. damn. mh/vh - */ - - for (auto &BB : F) { - - for (auto &IN : BB) { - - CallInst *callInst = nullptr; - - if ((callInst = dyn_cast(&IN))) { - - bool isStrcmp = true; - bool isMemcmp = true; - bool isStrncmp = true; - bool isStrcasecmp = true; - bool isStrncasecmp = true; - bool isIntMemcpy = true; - bool addedNull = false; - size_t optLen = 0; - - Function *Callee = callInst->getCalledFunction(); - if (!Callee) continue; - if (callInst->getCallingConv() != llvm::CallingConv::C) continue; - std::string FuncName = Callee->getName().str(); - isStrcmp &= !FuncName.compare("strcmp"); - isMemcmp &= !FuncName.compare("memcmp"); - isStrncmp &= !FuncName.compare("strncmp"); - isStrcasecmp &= !FuncName.compare("strcasecmp"); - isStrncasecmp &= !FuncName.compare("strncasecmp"); - isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); - - /* we do something different here, putting this BB and the - successors in a block map */ - if (!FuncName.compare("__afl_persistent_loop")) { - - BlockList.push_back(&BB); - /* - for (succ_iterator SI = succ_begin(&BB), SE = - succ_end(&BB); SI != SE; ++SI) { - - BasicBlock *succ = *SI; - BlockList.push_back(succ); - - } - - */ - - } - - if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) - continue; - - /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function - * prototype */ - FunctionType *FT = Callee->getFunctionType(); - - isStrcmp &= FT->getNumParams() == 2 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()); - isStrcasecmp &= FT->getNumParams() == 2 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()); - isMemcmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0)->isPointerTy() && - FT->getParamType(1)->isPointerTy() && - FT->getParamType(2)->isIntegerTy(); - isStrncmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); - isStrncasecmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); - - if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) - continue; - - /* is a str{n,}{case,}cmp/memcmp, check if we have - * str{case,}cmp(x, "const") or str{case,}cmp("const", x) - * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, ..) - * memcmp(x, "const", ..) or memcmp("const", x, ..) */ - Value *Str1P = callInst->getArgOperand(0), - *Str2P = callInst->getArgOperand(1); - std::string Str1, Str2; - StringRef TmpStr; - bool HasStr1 = getConstantStringInfo(Str1P, TmpStr); - if (TmpStr.empty()) { - - HasStr1 = false; - - } else { - - HasStr1 = true; - Str1 = TmpStr.str(); - - } - - bool HasStr2 = getConstantStringInfo(Str2P, TmpStr); - if (TmpStr.empty()) { - - HasStr2 = false; - - } else { - - HasStr2 = true; - Str2 = TmpStr.str(); - - } - - if (debug) - fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n", - FuncName.c_str(), Str1P, Str1P->getName().str().c_str(), - Str1.c_str(), HasStr1 == true ? "true" : "false", Str2P, - Str2P->getName().str().c_str(), Str2.c_str(), - HasStr2 == true ? "true" : "false"); - - // we handle the 2nd parameter first because of llvm memcpy - if (!HasStr2) { - - auto *Ptr = dyn_cast(Str2P); - if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { - - if (auto *Var = dyn_cast(Ptr->getOperand(0))) { - - if (Var->hasInitializer()) { - - if (auto *Array = dyn_cast( - Var->getInitializer())) { - - HasStr2 = true; - Str2 = Array->getAsString().str(); - - } - - } - - } - - } - - } - - // for the internal memcpy routine we only care for the second - // parameter and are not reporting anything. - if (isIntMemcpy == true) { - - if (HasStr2 == true) { - - Value * op2 = callInst->getArgOperand(2); - ConstantInt *ilen = dyn_cast(op2); - if (ilen) { - - uint64_t literalLength = Str2.size(); - uint64_t optLength = ilen->getZExtValue(); - if (literalLength + 1 == optLength) { - - Str2.append("\0", 1); // add null byte - addedNull = true; - - } - - } - - valueMap[Str1P] = new std::string(Str2); - - if (debug) - fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), Str1P); - continue; - - } - - continue; - - } - - // Neither a literal nor a global variable? - // maybe it is a local variable that we saved - if (!HasStr2) { - - std::string *strng = valueMap[Str2P]; - if (strng && !strng->empty()) { - - Str2 = *strng; - HasStr2 = true; - if (debug) - fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(), - Str2P); - - } - - } - - if (!HasStr1) { - - auto Ptr = dyn_cast(Str1P); - - if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { - - if (auto *Var = dyn_cast(Ptr->getOperand(0))) { - - if (Var->hasInitializer()) { - - if (auto *Array = dyn_cast( - Var->getInitializer())) { - - HasStr1 = true; - Str1 = Array->getAsString().str(); - - } - - } - - } - - } - - } - - // Neither a literal nor a global variable? - // maybe it is a local variable that we saved - if (!HasStr1) { - - std::string *strng = valueMap[Str1P]; - if (strng && !strng->empty()) { - - Str1 = *strng; - HasStr1 = true; - if (debug) - fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(), - Str1P); - - } - - } - - /* handle cases of one string is const, one string is variable */ - if (!(HasStr1 ^ HasStr2)) continue; - - std::string thestring; - - if (HasStr1) - thestring = Str1; - else - thestring = Str2; - - optLen = thestring.length(); - - if (isMemcmp || isStrncmp || isStrncasecmp) { - - Value * op2 = callInst->getArgOperand(2); - ConstantInt *ilen = dyn_cast(op2); - if (ilen) { - - uint64_t literalLength = optLen; - optLen = ilen->getZExtValue(); - if (literalLength + 1 == optLen) { // add null byte - thestring.append("\0", 1); - addedNull = true; - - } - - } - - } - - // add null byte if this is a string compare function and a null - // was not already added - if (!isMemcmp) { - - if (addedNull == false) { - - thestring.append("\0", 1); // add null byte - optLen++; - - } - - // ensure we do not have garbage - size_t offset = thestring.find('\0', 0); - if (offset + 1 < optLen) optLen = offset + 1; - thestring = thestring.substr(0, optLen); - - } - - if (!be_quiet) { - - std::string outstring; - fprintf(stderr, "%s: length %zu/%zu \"", FuncName.c_str(), optLen, - thestring.length()); - for (uint8_t i = 0; i < thestring.length(); i++) { - - uint8_t c = thestring[i]; - if (c <= 32 || c >= 127) - fprintf(stderr, "\\x%02x", c); - else - fprintf(stderr, "%c", c); - - } - - fprintf(stderr, "\"\n"); - - } - - // we take the longer string, even if the compare was to a - // shorter part. Note that depending on the optimizer of the - // compiler this can be wrong, but it is more likely that this - // is helping the fuzzer - if (optLen != thestring.length()) optLen = thestring.length(); - if (optLen > MAX_AUTO_EXTRA) optLen = MAX_AUTO_EXTRA; - if (optLen < MIN_AUTO_EXTRA) // too short? skip - continue; - - dictionary.push_back(thestring.substr(0, optLen)); - - } - - } - - } - - } - - for (auto &BB : F) { - - if (F.size() == 1) { - - InsBlocks.push_back(&BB); - continue; - - } - - uint32_t succ = 0; - for (succ_iterator SI = succ_begin(&BB), SE = succ_end(&BB); SI != SE; - ++SI) - if ((*SI)->size() > 0) succ++; - if (succ < 2) // no need to instrument - continue; - - if (BlockList.size()) { - - int skip = 0; - for (uint32_t k = 0; k < BlockList.size(); k++) { - - if (&BB == BlockList[k]) { - - if (debug) - fprintf(stderr, - "DEBUG: Function %s skipping BB with/after __afl_loop\n", - F.getName().str().c_str()); - skip = 1; - - } - - } - - if (skip) continue; - - } - - InsBlocks.push_back(&BB); - - } - - if (InsBlocks.size() > 0) { - - uint32_t i = InsBlocks.size(); - - do { - - --i; - BasicBlock * newBB = NULL; - BasicBlock * origBB = &(*InsBlocks[i]); - std::vector Successors; - Instruction * TI = origBB->getTerminator(); - uint32_t fs = origBB->getParent()->size(); - uint32_t countto; - - for (succ_iterator SI = succ_begin(origBB), SE = succ_end(origBB); - SI != SE; ++SI) { - - BasicBlock *succ = *SI; - Successors.push_back(succ); - - } - - if (fs == 1) { - - newBB = origBB; - countto = 1; - - } else { - - if (TI == NULL || TI->getNumSuccessors() < 2) continue; - countto = Successors.size(); - - } - - // if (Successors.size() != TI->getNumSuccessors()) - // FATAL("Different successor numbers %lu <-> %u\n", Successors.size(), - // TI->getNumSuccessors()); - - for (uint32_t j = 0; j < countto; j++) { - - if (fs != 1) newBB = llvm::SplitEdge(origBB, Successors[j]); - - if (!newBB) { - - if (!be_quiet) WARNF("Split failed!"); - continue; - - } - - if (documentFile) { - - fprintf(documentFile, "ModuleID=%llu Function=%s edgeID=%u\n", - moduleID, F.getName().str().c_str(), afl_global_id); - - } - - BasicBlock::iterator IP = newBB->getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - - /* Set the ID of the inserted basic block */ - - ConstantInt *CurLoc = ConstantInt::get(Int32Ty, afl_global_id++); - - /* Load SHM pointer */ - - Value *MapPtrIdx; - - if (map_addr) { - - MapPtrIdx = IRB.CreateGEP(MapPtrFixed, CurLoc); - - } else { - - LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); - MapPtr->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - MapPtrIdx = IRB.CreateGEP(MapPtr, CurLoc); - - } - - /* Update bitmap */ - - LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); - Counter->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - Value *Incr = IRB.CreateAdd(Counter, One); - - if (skip_nozero == NULL) { - - auto cf = IRB.CreateICmpEQ(Incr, Zero); - auto carry = IRB.CreateZExt(cf, Int8Ty); - Incr = IRB.CreateAdd(Incr, carry); - - } - - IRB.CreateStore(Incr, MapPtrIdx) - ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - // done :) - - inst_blocks++; - - } - - } while (i > 0); - - } - - } - - if (documentFile) fclose(documentFile); - documentFile = NULL; - - // save highest location ID to global variable - // do this after each function to fail faster - if (!be_quiet && afl_global_id > MAP_SIZE && - afl_global_id > FS_OPT_MAX_MAPSIZE) { - - uint32_t pow2map = 1, map = afl_global_id; - while ((map = map >> 1)) - pow2map++; - WARNF( - "We have %u blocks to instrument but the map size is only %u. Either " - "edit config.h and set MAP_SIZE_POW2 from %u to %u, then recompile " - "afl-fuzz and llvm_mode and then make this target - or set " - "AFL_MAP_SIZE with at least size %u when running afl-fuzz with this " - "target.", - afl_global_id, MAP_SIZE, MAP_SIZE_POW2, pow2map, afl_global_id); - - } - - if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) { - - // yes we could create our own function, insert it into ctors ... - // but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o - - Function *f = M.getFunction("__afl_auto_init_globals"); - - if (!f) { - - fprintf(stderr, - "Error: init function could not be found (this should not " - "happen)\n"); - exit(-1); - - } - - BasicBlock *bb = &f->getEntryBlock(); - if (!bb) { - - fprintf(stderr, - "Error: init function does not have an EntryBlock (this should " - "not happen)\n"); - exit(-1); - - } - - BasicBlock::iterator IP = bb->getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - - if (map_addr) { - - GlobalVariable *AFLMapAddrFixed = new GlobalVariable( - M, Int64Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_map_addr"); - ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr); - StoreInst * StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed); - StoreMapAddr->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } - - if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) { - - uint32_t write_loc = afl_global_id; - - if (afl_global_id % 8) write_loc = (((afl_global_id + 8) >> 3) << 3); - - GlobalVariable *AFLFinalLoc = new GlobalVariable( - M, Int32Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_final_loc"); - ConstantInt *const_loc = ConstantInt::get(Int32Ty, write_loc); - StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc); - StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } - - if (dictionary.size()) { - - size_t memlen = 0, count = 0, offset = 0; - char * ptr; - - for (auto token : dictionary) { - - memlen += token.length(); - count++; - - } - - if (!be_quiet) - printf("AUTODICTIONARY: %lu string%s found\n", count, - count == 1 ? "" : "s"); - - if (count) { - - if ((ptr = (char *)malloc(memlen + count)) == NULL) { - - fprintf(stderr, "Error: malloc for %lu bytes failed!\n", - memlen + count); - exit(-1); - - } - - count = 0; - - for (auto token : dictionary) { - - if (offset + token.length() < 0xfffff0 && count < MAX_AUTO_EXTRAS) { - - ptr[offset++] = (uint8_t)token.length(); - memcpy(ptr + offset, token.c_str(), token.length()); - offset += token.length(); - count++; - - } - - } - - GlobalVariable *AFLDictionaryLen = - new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, - 0, "__afl_dictionary_len"); - ConstantInt *const_len = ConstantInt::get(Int32Ty, offset); - StoreInst *StoreDictLen = IRB.CreateStore(const_len, AFLDictionaryLen); - StoreDictLen->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - ArrayType *ArrayTy = ArrayType::get(IntegerType::get(C, 8), offset); - GlobalVariable *AFLInternalDictionary = new GlobalVariable( - M, ArrayTy, true, GlobalValue::ExternalLinkage, - ConstantDataArray::get(C, - *(new ArrayRef((char *)ptr, offset))), - "__afl_internal_dictionary"); - AFLInternalDictionary->setInitializer(ConstantDataArray::get( - C, *(new ArrayRef((char *)ptr, offset)))); - AFLInternalDictionary->setConstant(true); - - GlobalVariable *AFLDictionary = new GlobalVariable( - M, PointerType::get(Int8Ty, 0), false, GlobalValue::ExternalLinkage, - 0, "__afl_dictionary"); - - Value *AFLDictOff = IRB.CreateGEP(AFLInternalDictionary, Zero); - Value *AFLDictPtr = - IRB.CreatePointerCast(AFLDictOff, PointerType::get(Int8Ty, 0)); - StoreInst *StoreDict = IRB.CreateStore(AFLDictPtr, AFLDictionary); - StoreDict->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } - - } - - } - - /* Say something nice. */ - - if (!be_quiet) { - - if (!inst_blocks) - WARNF("No instrumentation targets found."); - else { - - char modeline[100]; - snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", - getenv("AFL_HARDEN") ? "hardened" : "non-hardened", - getenv("AFL_USE_ASAN") ? ", ASAN" : "", - getenv("AFL_USE_MSAN") ? ", MSAN" : "", - getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", - getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); - OKF("Instrumented %u locations with no collisions (on average %llu " - "collisions would be in afl-gcc/afl-clang-fast) (%s mode).", - inst_blocks, calculateCollisions(inst_blocks), modeline); - - } - - } - - return true; - -} - -char AFLLTOPass::ID = 0; - -static void registerAFLLTOPass(const PassManagerBuilder &, - legacy::PassManagerBase &PM) { - - PM.add(new AFLLTOPass()); - -} - -static RegisterPass X("afl-lto", "afl++ LTO instrumentation pass", - false, false); - -static RegisterStandardPasses RegisterAFLLTOPass( - PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerAFLLTOPass); - diff --git a/llvm_mode/afl-llvm-lto-instrumentlist.so.cc b/llvm_mode/afl-llvm-lto-instrumentlist.so.cc deleted file mode 100644 index a7331444..00000000 --- a/llvm_mode/afl-llvm-lto-instrumentlist.so.cc +++ /dev/null @@ -1,147 +0,0 @@ -/* - american fuzzy lop++ - LLVM-mode instrumentation pass - --------------------------------------------------- - - Written by Laszlo Szekeres and - Michal Zalewski - - LLVM integration design comes from Laszlo Szekeres. C bits copied-and-pasted - from afl-as.c are Michal's fault. - - Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This library is plugged into LLVM when invoking clang through afl-clang-fast. - It tells the compiler to add code roughly equivalent to the bits discussed - in ../afl-as.h. - - */ - -#define AFL_LLVM_PASS - -#include "config.h" -#include "debug.h" - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "llvm/IR/DebugInfo.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/Debug.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/IR/CFG.h" - -#include "afl-llvm-common.h" - -using namespace llvm; - -namespace { - -class AFLcheckIfInstrument : public ModulePass { - - public: - static char ID; - AFLcheckIfInstrument() : ModulePass(ID) { - - if (getenv("AFL_DEBUG")) debug = 1; - - initInstrumentList(); - - } - - bool runOnModule(Module &M) override; - - // StringRef getPassName() const override { - - // return "American Fuzzy Lop Instrumentation"; - // } - - protected: - std::list myInstrumentList; - -}; - -} // namespace - -char AFLcheckIfInstrument::ID = 0; - -bool AFLcheckIfInstrument::runOnModule(Module &M) { - - /* Show a banner */ - - setvbuf(stdout, NULL, _IONBF, 0); - - if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { - - SAYF(cCYA "afl-llvm-lto-instrumentlist" VERSION cRST - " by Marc \"vanHauser\" Heuse \n"); - - } else if (getenv("AFL_QUIET")) - - be_quiet = 1; - - for (auto &F : M) { - - if (F.size() < 1) continue; - - // fprintf(stderr, "F:%s\n", F.getName().str().c_str()); - - if (isInInstrumentList(&F)) { - - if (debug) - SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n", - F.getName().str().c_str()); - - } else { - - if (debug) - SAYF(cMGN "[D] " cRST - "function %s is NOT in the instrument file list\n", - F.getName().str().c_str()); - - auto & Ctx = F.getContext(); - AttributeList Attrs = F.getAttributes(); - AttrBuilder NewAttrs; - NewAttrs.addAttribute("skipinstrument"); - F.setAttributes( - Attrs.addAttributes(Ctx, AttributeList::FunctionIndex, NewAttrs)); - - } - - } - - return true; - -} - -static void registerAFLcheckIfInstrumentpass(const PassManagerBuilder &, - legacy::PassManagerBase &PM) { - - PM.add(new AFLcheckIfInstrument()); - -} - -static RegisterStandardPasses RegisterAFLcheckIfInstrumentpass( - PassManagerBuilder::EP_ModuleOptimizerEarly, - registerAFLcheckIfInstrumentpass); - -static RegisterStandardPasses RegisterAFLcheckIfInstrumentpass0( - PassManagerBuilder::EP_EnabledOnOptLevel0, - registerAFLcheckIfInstrumentpass); - diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc deleted file mode 100644 index 8c8c987a..00000000 --- a/llvm_mode/afl-llvm-pass.so.cc +++ /dev/null @@ -1,654 +0,0 @@ -/* - american fuzzy lop++ - LLVM-mode instrumentation pass - --------------------------------------------------- - - Written by Laszlo Szekeres , - Adrian Herrera , - Michal Zalewski - - LLVM integration design comes from Laszlo Szekeres. C bits copied-and-pasted - from afl-as.c are Michal's fault. - - NGRAM previous location coverage comes from Adrian Herrera. - - Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This library is plugged into LLVM when invoking clang through afl-clang-fast. - It tells the compiler to add code roughly equivalent to the bits discussed - in ../afl-as.h. - - */ - -#define AFL_LLVM_PASS - -#include "config.h" -#include "debug.h" -#include -#include -#include - -#include -#include -#include -#include - -#include "llvm/Config/llvm-config.h" -#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5 -typedef long double max_align_t; -#endif - -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/MathExtras.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" - -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) - #include "llvm/IR/DebugInfo.h" - #include "llvm/IR/CFG.h" -#else - #include "llvm/DebugInfo.h" - #include "llvm/Support/CFG.h" -#endif - -#include "afl-llvm-common.h" -#include "llvm-ngram-coverage.h" - -using namespace llvm; - -namespace { - -class AFLCoverage : public ModulePass { - - public: - static char ID; - AFLCoverage() : ModulePass(ID) { - - initInstrumentList(); - - } - - bool runOnModule(Module &M) override; - - protected: - uint32_t ngram_size = 0; - uint32_t map_size = MAP_SIZE; - uint32_t function_minimum_size = 1; - char * ctx_str = NULL, *skip_nozero = NULL; - -}; - -} // namespace - -char AFLCoverage::ID = 0; - -/* needed up to 3.9.0 */ -#if LLVM_VERSION_MAJOR == 3 && \ - (LLVM_VERSION_MINOR < 9 || \ - (LLVM_VERSION_MINOR == 9 && LLVM_VERSION_PATCH < 1)) -uint64_t PowerOf2Ceil(unsigned in) { - - uint64_t in64 = in - 1; - in64 |= (in64 >> 1); - in64 |= (in64 >> 2); - in64 |= (in64 >> 4); - in64 |= (in64 >> 8); - in64 |= (in64 >> 16); - in64 |= (in64 >> 32); - return in64 + 1; - -} - -#endif - -/* #if LLVM_VERSION_STRING >= "4.0.1" */ -#if LLVM_VERSION_MAJOR > 4 || \ - (LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1) - #define AFL_HAVE_VECTOR_INTRINSICS 1 -#endif -bool AFLCoverage::runOnModule(Module &M) { - - LLVMContext &C = M.getContext(); - - IntegerType *Int8Ty = IntegerType::getInt8Ty(C); - IntegerType *Int32Ty = IntegerType::getInt32Ty(C); -#ifdef AFL_HAVE_VECTOR_INTRINSICS - IntegerType *IntLocTy = - IntegerType::getIntNTy(C, sizeof(PREV_LOC_T) * CHAR_BIT); -#endif - struct timeval tv; - struct timezone tz; - u32 rand_seed; - unsigned int cur_loc = 0; - - /* Setup random() so we get Actually Random(TM) outputs from AFL_R() */ - gettimeofday(&tv, &tz); - rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); - AFL_SR(rand_seed); - - /* Show a banner */ - - setvbuf(stdout, NULL, _IONBF, 0); - - if (getenv("AFL_DEBUG")) debug = 1; - - if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { - - SAYF(cCYA "afl-llvm-pass" VERSION cRST - " by and \n"); - - } else - - be_quiet = 1; - - /* - char *ptr; - if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) { - - map_size = atoi(ptr); - if (map_size < 8 || map_size > (1 << 29)) - FATAL("illegal AFL_MAP_SIZE %u, must be between 2^3 and 2^30", - map_size); if (map_size % 8) map_size = (((map_size >> 3) + 1) << 3); - - } - - */ - - /* Decide instrumentation ratio */ - - char * inst_ratio_str = getenv("AFL_INST_RATIO"); - unsigned int inst_ratio = 100; - - if (inst_ratio_str) { - - if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || - inst_ratio > 100) - FATAL("Bad value of AFL_INST_RATIO (must be between 1 and 100)"); - - } - -#if LLVM_VERSION_MAJOR < 9 - char *neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO"); -#endif - skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); - - unsigned PrevLocSize = 0; - - char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); - if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); - ctx_str = getenv("AFL_LLVM_CTX"); - -#ifdef AFL_HAVE_VECTOR_INTRINSICS - /* Decide previous location vector size (must be a power of two) */ - VectorType *PrevLocTy = NULL; - - if (ngram_size_str) - if (sscanf(ngram_size_str, "%u", &ngram_size) != 1 || ngram_size < 2 || - ngram_size > NGRAM_SIZE_MAX) - FATAL( - "Bad value of AFL_NGRAM_SIZE (must be between 2 and NGRAM_SIZE_MAX " - "(%u))", - NGRAM_SIZE_MAX); - - if (ngram_size == 1) ngram_size = 0; - if (ngram_size) - PrevLocSize = ngram_size - 1; - else -#else - if (ngram_size_str) - #ifndef LLVM_VERSION_PATCH - FATAL( - "Sorry, NGRAM branch coverage is not supported with llvm version " - "%d.%d.%d!", - LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0); - #else - FATAL( - "Sorry, NGRAM branch coverage is not supported with llvm version " - "%d.%d.%d!", - LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH); - #endif -#endif - PrevLocSize = 1; - -#ifdef AFL_HAVE_VECTOR_INTRINSICS - int PrevLocVecSize = PowerOf2Ceil(PrevLocSize); - if (ngram_size) - PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize - #if LLVM_VERSION_MAJOR >= 12 - , - false - #endif - ); -#endif - - /* Get globals for the SHM region and the previous location. Note that - __afl_prev_loc is thread-local. */ - - GlobalVariable *AFLMapPtr = - new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, - GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); - GlobalVariable *AFLPrevLoc; - GlobalVariable *AFLContext = NULL; - - if (ctx_str) -#ifdef __ANDROID__ - AFLContext = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx"); -#else - AFLContext = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx", 0, - GlobalVariable::GeneralDynamicTLSModel, 0, false); -#endif - -#ifdef AFL_HAVE_VECTOR_INTRINSICS - if (ngram_size) - #ifdef __ANDROID__ - AFLPrevLoc = new GlobalVariable( - M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage, - /* Initializer */ nullptr, "__afl_prev_loc"); - #else - AFLPrevLoc = new GlobalVariable( - M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage, - /* Initializer */ nullptr, "__afl_prev_loc", - /* InsertBefore */ nullptr, GlobalVariable::GeneralDynamicTLSModel, - /* AddressSpace */ 0, /* IsExternallyInitialized */ false); - #endif - else -#endif -#ifdef __ANDROID__ - AFLPrevLoc = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc"); -#else - AFLPrevLoc = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0, - GlobalVariable::GeneralDynamicTLSModel, 0, false); -#endif - -#ifdef AFL_HAVE_VECTOR_INTRINSICS - /* Create the vector shuffle mask for updating the previous block history. - Note that the first element of the vector will store cur_loc, so just set - it to undef to allow the optimizer to do its thing. */ - - SmallVector PrevLocShuffle = {UndefValue::get(Int32Ty)}; - - for (unsigned I = 0; I < PrevLocSize - 1; ++I) - PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, I)); - - for (int I = PrevLocSize; I < PrevLocVecSize; ++I) - PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize)); - - Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle); -#endif - - // other constants we need - ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); - ConstantInt *One = ConstantInt::get(Int8Ty, 1); - - LoadInst *PrevCtx = NULL; // CTX sensitive coverage - - /* Instrument all the things! */ - - int inst_blocks = 0; - scanForDangerousFunctions(&M); - - for (auto &F : M) { - - int has_calls = 0; - if (debug) - fprintf(stderr, "FUNCTION: %s (%zu)\n", F.getName().str().c_str(), - F.size()); - - if (!isInInstrumentList(&F)) continue; - - if (F.size() < function_minimum_size) continue; - - for (auto &BB : F) { - - BasicBlock::iterator IP = BB.getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - - // Context sensitive coverage - if (ctx_str && &BB == &F.getEntryBlock()) { - - // load the context ID of the previous function and write to to a local - // variable on the stack - PrevCtx = IRB.CreateLoad(AFLContext); - PrevCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - // does the function have calls? and is any of the calls larger than one - // basic block? - for (auto &BB : F) { - - if (has_calls) break; - for (auto &IN : BB) { - - CallInst *callInst = nullptr; - if ((callInst = dyn_cast(&IN))) { - - Function *Callee = callInst->getCalledFunction(); - if (!Callee || Callee->size() < function_minimum_size) - continue; - else { - - has_calls = 1; - break; - - } - - } - - } - - } - - // if yes we store a context ID for this function in the global var - if (has_calls) { - - ConstantInt *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size)); - StoreInst * StoreCtx = IRB.CreateStore(NewCtx, AFLContext); - StoreCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } - - } - - if (AFL_R(100) >= inst_ratio) continue; - - /* Make up cur_loc */ - - // cur_loc++; - cur_loc = AFL_R(map_size); - -/* There is a problem with Ubuntu 18.04 and llvm 6.0 (see issue #63). - The inline function successors() is not inlined and also not found at runtime - :-( As I am unable to detect Ubuntu18.04 heree, the next best thing is to - disable this optional optimization for LLVM 6.0.0 and Linux */ -#if !(LLVM_VERSION_MAJOR == 6 && LLVM_VERSION_MINOR == 0) || !defined __linux__ - // only instrument if this basic block is the destination of a previous - // basic block that has multiple successors - // this gets rid of ~5-10% of instrumentations that are unnecessary - // result: a little more speed and less map pollution - int more_than_one = -1; - // fprintf(stderr, "BB %u: ", cur_loc); - for (pred_iterator PI = pred_begin(&BB), E = pred_end(&BB); PI != E; - ++PI) { - - BasicBlock *Pred = *PI; - - int count = 0; - if (more_than_one == -1) more_than_one = 0; - // fprintf(stderr, " %p=>", Pred); - - for (succ_iterator SI = succ_begin(Pred), E = succ_end(Pred); SI != E; - ++SI) { - - BasicBlock *Succ = *SI; - - // if (count > 0) - // fprintf(stderr, "|"); - if (Succ != NULL) count++; - // fprintf(stderr, "%p", Succ); - - } - - if (count > 1) more_than_one = 1; - - } - - // fprintf(stderr, " == %d\n", more_than_one); - if (F.size() > 1 && more_than_one != 1) { - - // in CTX mode we have to restore the original context for the caller - - // she might be calling other functions which need the correct CTX - if (ctx_str && has_calls) { - - Instruction *Inst = BB.getTerminator(); - if (isa(Inst) || isa(Inst)) { - - IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); - RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } - - } - - continue; - - } - -#endif - - ConstantInt *CurLoc; - -#ifdef AFL_HAVE_VECTOR_INTRINSICS - if (ngram_size) - CurLoc = ConstantInt::get(IntLocTy, cur_loc); - else -#endif - CurLoc = ConstantInt::get(Int32Ty, cur_loc); - - /* Load prev_loc */ - - LoadInst *PrevLoc = IRB.CreateLoad(AFLPrevLoc); - PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - Value *PrevLocTrans; - -#ifdef AFL_HAVE_VECTOR_INTRINSICS - /* "For efficiency, we propose to hash the tuple as a key into the - hit_count map as (prev_block_trans << 1) ^ curr_block_trans, where - prev_block_trans = (block_trans_1 ^ ... ^ block_trans_(n-1)" */ - - if (ngram_size) - PrevLocTrans = - IRB.CreateZExt(IRB.CreateXorReduce(PrevLoc), IRB.getInt32Ty()); - else -#endif - PrevLocTrans = PrevLoc; - - if (ctx_str) - PrevLocTrans = - IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty); - else - PrevLocTrans = IRB.CreateZExt(PrevLocTrans, IRB.getInt32Ty()); - - /* Load SHM pointer */ - - LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); - MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - Value *MapPtrIdx; -#ifdef AFL_HAVE_VECTOR_INTRINSICS - if (ngram_size) - MapPtrIdx = IRB.CreateGEP( - MapPtr, - IRB.CreateZExt( - IRB.CreateXor(PrevLocTrans, IRB.CreateZExt(CurLoc, Int32Ty)), - Int32Ty)); - else -#endif - MapPtrIdx = IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocTrans, CurLoc)); - - /* Update bitmap */ - - LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); - Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - Value *Incr = IRB.CreateAdd(Counter, One); - -#if LLVM_VERSION_MAJOR < 9 - if (neverZero_counters_str != - NULL) { // with llvm 9 we make this the default as the bug in llvm is - // then fixed -#else - if (!skip_nozero) { - -#endif - /* hexcoder: Realize a counter that skips zero during overflow. - * Once this counter reaches its maximum value, it next increments to 1 - * - * Instead of - * Counter + 1 -> Counter - * we inject now this - * Counter + 1 -> {Counter, OverflowFlag} - * Counter + OverflowFlag -> Counter - */ - - auto cf = IRB.CreateICmpEQ(Incr, Zero); - auto carry = IRB.CreateZExt(cf, Int8Ty); - Incr = IRB.CreateAdd(Incr, carry); - - } - - IRB.CreateStore(Incr, MapPtrIdx) - ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - /* Update prev_loc history vector (by placing cur_loc at the head of the - vector and shuffle the other elements back by one) */ - - StoreInst *Store; - -#ifdef AFL_HAVE_VECTOR_INTRINSICS - if (ngram_size) { - - Value *ShuffledPrevLoc = IRB.CreateShuffleVector( - PrevLoc, UndefValue::get(PrevLocTy), PrevLocShuffleMask); - Value *UpdatedPrevLoc = IRB.CreateInsertElement( - ShuffledPrevLoc, IRB.CreateLShr(CurLoc, (uint64_t)1), (uint64_t)0); - - Store = IRB.CreateStore(UpdatedPrevLoc, AFLPrevLoc); - Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - } else - -#endif - { - - Store = IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), - AFLPrevLoc); - - } - - // in CTX mode we have to restore the original context for the caller - - // she might be calling other functions which need the correct CTX. - // Currently this is only needed for the Ubuntu clang-6.0 bug - if (ctx_str && has_calls) { - - Instruction *Inst = BB.getTerminator(); - if (isa(Inst) || isa(Inst)) { - - IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); - RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } - - } - - inst_blocks++; - - } - - } - - /* - // This is currently disabled because we not only need to create/insert a - // function (easy), but also add it as a constructor with an ID < 5 - - if (getenv("AFL_LLVM_DONTWRITEID") == NULL) { - - // yes we could create our own function, insert it into ctors ... - // but this would be a pain in the butt ... so we use afl-llvm-rt.o - - Function *f = ... - - if (!f) { - - fprintf(stderr, - "Error: init function could not be created (this should not - happen)\n"); exit(-1); - - } - - ... constructor for f = 4 - - BasicBlock *bb = &f->getEntryBlock(); - if (!bb) { - - fprintf(stderr, - "Error: init function does not have an EntryBlock (this should - not happen)\n"); exit(-1); - - } - - BasicBlock::iterator IP = bb->getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - - if (map_size <= 0x800000) { - - GlobalVariable *AFLFinalLoc = new GlobalVariable( - M, Int32Ty, true, GlobalValue::ExternalLinkage, 0, - "__afl_final_loc"); - ConstantInt *const_loc = ConstantInt::get(Int32Ty, map_size); - StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc); - StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } - - } - - */ - - /* Say something nice. */ - - if (!be_quiet) { - - if (!inst_blocks) - WARNF("No instrumentation targets found."); - else { - - char modeline[100]; - snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", - getenv("AFL_HARDEN") ? "hardened" : "non-hardened", - getenv("AFL_USE_ASAN") ? ", ASAN" : "", - getenv("AFL_USE_MSAN") ? ", MSAN" : "", - getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", - getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); - OKF("Instrumented %u locations (%s mode, ratio %u%%).", inst_blocks, - modeline, inst_ratio); - - } - - } - - return true; - -} - -static void registerAFLPass(const PassManagerBuilder &, - legacy::PassManagerBase &PM) { - - PM.add(new AFLCoverage()); - -} - -static RegisterStandardPasses RegisterAFLPass( - PassManagerBuilder::EP_OptimizerLast, registerAFLPass); - -static RegisterStandardPasses RegisterAFLPass0( - PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass); - diff --git a/llvm_mode/afl-llvm-rt-lto.o.c b/llvm_mode/afl-llvm-rt-lto.o.c deleted file mode 100644 index e53785ff..00000000 --- a/llvm_mode/afl-llvm-rt-lto.o.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - american fuzzy lop++ - LLVM instrumentation bootstrap - ----------------------------------------------------- - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - -*/ - -#include -#include - -// to prevent the function from being removed -unsigned char __afl_lto_mode = 0; - -/* Proper initialization routine. */ - -__attribute__((constructor(0))) void __afl_auto_init_globals(void) { - - if (getenv("AFL_DEBUG")) fprintf(stderr, "[__afl_auto_init_globals]\n"); - __afl_lto_mode = 1; - -} - diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c deleted file mode 100644 index bdafbe0b..00000000 --- a/llvm_mode/afl-llvm-rt.o.c +++ /dev/null @@ -1,1244 +0,0 @@ -/* - american fuzzy lop++ - LLVM instrumentation bootstrap - --------------------------------------------------- - - Written by Laszlo Szekeres and - Michal Zalewski - - LLVM integration design comes from Laszlo Szekeres. - - Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This code is the rewrite of afl-as.h's main_payload. - -*/ - -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif -#include "config.h" -#include "types.h" -#include "cmplog.h" -#include "llvm-ngram-coverage.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "llvm/Config/llvm-config.h" - -#ifdef __linux__ - #include "snapshot-inl.h" -#endif - -/* This is a somewhat ugly hack for the experimental 'trace-pc-guard' mode. - Basically, we need to make sure that the forkserver is initialized after - the LLVM-generated runtime initialization pass, not before. */ - -#ifndef MAP_FIXED_NOREPLACE - #ifdef MAP_EXCL - #define MAP_FIXED_NOREPLACE MAP_EXCL | MAP_FIXED - #else - #define MAP_FIXED_NOREPLACE MAP_FIXED - #endif -#endif - -#define CTOR_PRIO 3 - -#include -#include - -/* Globals needed by the injected instrumentation. The __afl_area_initial region - is used for instrumentation output before __afl_map_shm() has a chance to - run. It will end up as .comm, so it shouldn't be too wasteful. */ - -#if MAP_SIZE <= 65536 - #define MAP_INITIAL_SIZE 256000 -#else - #define MAP_INITIAL_SIZE MAP_SIZE -#endif - -u8 __afl_area_initial[MAP_INITIAL_SIZE]; -u8 * __afl_area_ptr = __afl_area_initial; -u8 * __afl_dictionary; -u8 * __afl_fuzz_ptr; -u32 __afl_fuzz_len_dummy; -u32 *__afl_fuzz_len = &__afl_fuzz_len_dummy; - -u32 __afl_final_loc; -u32 __afl_map_size = MAP_SIZE; -u32 __afl_dictionary_len; -u64 __afl_map_addr; - -#ifdef __ANDROID__ -PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX]; -u32 __afl_prev_ctx; -u32 __afl_cmp_counter; -#else -__thread PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX]; -__thread u32 __afl_prev_ctx; -__thread u32 __afl_cmp_counter; -#endif - -int __afl_sharedmem_fuzzing __attribute__((weak)); - -struct cmp_map *__afl_cmp_map; - -/* Running in persistent mode? */ - -static u8 is_persistent; - -/* Are we in sancov mode? */ - -static u8 _is_sancov; - -/* Error reporting to forkserver controller */ - -void send_forkserver_error(int error) { - - u32 status; - if (!error || error > 0xffff) return; - status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); - if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; - -} - -/* SHM fuzzing setup. */ - -static void __afl_map_shm_fuzz() { - - char *id_str = getenv(SHM_FUZZ_ENV_VAR); - - if (id_str) { - - u8 *map = NULL; - -#ifdef USEMMAP - const char * shm_file_path = id_str; - int shm_fd = -1; - unsigned char *shm_base = NULL; - - /* create the shared memory segment as if it was a file */ - shm_fd = shm_open(shm_file_path, O_RDWR, 0600); - if (shm_fd == -1) { - - fprintf(stderr, "shm_open() failed for fuzz\n"); - send_forkserver_error(FS_ERROR_SHM_OPEN); - exit(1); - - } - - map = - (u8 *)mmap(0, MAX_FILE + sizeof(u32), PROT_READ, MAP_SHARED, shm_fd, 0); - -#else - u32 shm_id = atoi(id_str); - map = (u8 *)shmat(shm_id, NULL, 0); - -#endif - - /* Whooooops. */ - - if (!map || map == (void *)-1) { - - perror("Could not access fuzzign shared memory"); - exit(1); - - } - - __afl_fuzz_len = (u32 *)map; - __afl_fuzz_ptr = map + sizeof(u32); - - if (getenv("AFL_DEBUG")) { - - fprintf(stderr, "DEBUG: successfully got fuzzing shared memory\n"); - - } - - } else { - - fprintf(stderr, "Error: variable for fuzzing shared memory is not set\n"); - exit(1); - - } - -} - -/* SHM setup. */ - -static void __afl_map_shm(void) { - - // we we are not running in afl ensure the map exists - if (!__afl_area_ptr) { __afl_area_ptr = __afl_area_initial; } - - char *id_str = getenv(SHM_ENV_VAR); - - if (__afl_final_loc) { - - if (__afl_final_loc % 8) - __afl_final_loc = (((__afl_final_loc + 7) >> 3) << 3); - __afl_map_size = __afl_final_loc; - - if (__afl_final_loc > MAP_SIZE) { - - char *ptr; - u32 val = 0; - if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) val = atoi(ptr); - if (val < __afl_final_loc) { - - if (__afl_final_loc > FS_OPT_MAX_MAPSIZE) { - - if (!getenv("AFL_QUIET")) - fprintf(stderr, - "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u " - "to be able to run this instrumented program!\n", - __afl_final_loc); - - if (id_str) { - - send_forkserver_error(FS_ERROR_MAP_SIZE); - exit(-1); - - } - - } else { - - if (!getenv("AFL_QUIET")) - fprintf(stderr, - "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u " - "to be able to run this instrumented program!\n", - __afl_final_loc); - - } - - } - - } - - } - - /* If we're running under AFL, attach to the appropriate region, replacing the - 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")) - 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, " - "max_size_forkserver %u/0x%x\n", - id_str == 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); - - if (id_str) { - - if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial) { - - if (__afl_map_addr) - munmap((void *)__afl_map_addr, __afl_final_loc); - else - free(__afl_area_ptr); - __afl_area_ptr = __afl_area_initial; - - } - -#ifdef USEMMAP - const char * shm_file_path = id_str; - int shm_fd = -1; - unsigned char *shm_base = NULL; - - /* create the shared memory segment as if it was a file */ - shm_fd = shm_open(shm_file_path, O_RDWR, 0600); - if (shm_fd == -1) { - - fprintf(stderr, "shm_open() failed\n"); - send_forkserver_error(FS_ERROR_SHM_OPEN); - exit(1); - - } - - /* map the shared memory segment to the address space of the process */ - if (__afl_map_addr) { - - shm_base = - mmap((void *)__afl_map_addr, __afl_map_size, PROT_READ | PROT_WRITE, - MAP_FIXED_NOREPLACE | MAP_SHARED, shm_fd, 0); - - } else { - - shm_base = mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, - shm_fd, 0); - - } - - if (shm_base == MAP_FAILED) { - - close(shm_fd); - shm_fd = -1; - - fprintf(stderr, "mmap() failed\n"); - if (__afl_map_addr) - send_forkserver_error(FS_ERROR_MAP_ADDR); - else - send_forkserver_error(FS_ERROR_MMAP); - exit(2); - - } - - __afl_area_ptr = shm_base; -#else - u32 shm_id = atoi(id_str); - - __afl_area_ptr = shmat(shm_id, (void *)__afl_map_addr, 0); - -#endif - - /* Whooooops. */ - - if (__afl_area_ptr == (void *)-1) { - - if (__afl_map_addr) - send_forkserver_error(FS_ERROR_MAP_ADDR); - else - send_forkserver_error(FS_ERROR_SHMAT); - _exit(1); - - } - - /* Write something into the bitmap so that even with low AFL_INST_RATIO, - our parent doesn't give up on us. */ - - __afl_area_ptr[0] = 1; - - } else if ((!__afl_area_ptr || __afl_area_ptr == __afl_area_initial) && - - __afl_map_addr) { - - __afl_area_ptr = - mmap((void *)__afl_map_addr, __afl_map_size, PROT_READ | PROT_WRITE, - MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); - - if (__afl_area_ptr == MAP_FAILED) { - - fprintf(stderr, "can not acquire mmap for address %p\n", - (void *)__afl_map_addr); - exit(1); - - } - - } else if (_is_sancov && __afl_area_ptr != __afl_area_initial) { - - free(__afl_area_ptr); - __afl_area_ptr = NULL; - if (__afl_final_loc > MAP_INITIAL_SIZE) - __afl_area_ptr = malloc(__afl_final_loc); - if (!__afl_area_ptr) __afl_area_ptr = __afl_area_initial; - - } - - id_str = getenv(CMPLOG_SHM_ENV_VAR); - - if (getenv("AFL_DEBUG")) { - - fprintf(stderr, "DEBUG: cmplog id_str %s\n", - id_str == NULL ? "" : id_str); - - } - - if (id_str) { - -#ifdef USEMMAP - const char * shm_file_path = id_str; - int shm_fd = -1; - unsigned char *shm_base = NULL; - - /* create the shared memory segment as if it was a file */ - shm_fd = shm_open(shm_file_path, O_RDWR, 0600); - if (shm_fd == -1) { - - fprintf(stderr, "shm_open() failed\n"); - exit(1); - - } - - /* map the shared memory segment to the address space of the process */ - shm_base = mmap(0, sizeof(struct cmp_map), PROT_READ | PROT_WRITE, - MAP_SHARED, shm_fd, 0); - if (shm_base == MAP_FAILED) { - - close(shm_fd); - shm_fd = -1; - - fprintf(stderr, "mmap() failed\n"); - exit(2); - - } - - __afl_cmp_map = shm_base; -#else - u32 shm_id = atoi(id_str); - - __afl_cmp_map = shmat(shm_id, NULL, 0); -#endif - - if (__afl_cmp_map == (void *)-1) _exit(1); - - } - -} - -#ifdef __linux__ -static void __afl_start_snapshots(void) { - - static u8 tmp[4] = {0, 0, 0, 0}; - s32 child_pid; - u32 status = 0; - u32 already_read_first = 0; - u32 was_killed; - - u8 child_stopped = 0; - - void (*old_sigchld_handler)(int) = 0; // = signal(SIGCHLD, SIG_DFL); - - /* Phone home and tell the parent that we're OK. If parent isn't there, - assume we're not running in forkserver mode and just execute program. */ - - status |= (FS_OPT_ENABLED | FS_OPT_SNAPSHOT); - if (__afl_sharedmem_fuzzing != 0) status |= FS_OPT_SHDMEM_FUZZ; - if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) - status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); - if (__afl_dictionary_len && __afl_dictionary) status |= FS_OPT_AUTODICT; - memcpy(tmp, &status, 4); - - if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; - - if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) { - - if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); - - if (getenv("AFL_DEBUG")) { - - fprintf(stderr, "target forkserver recv: %08x\n", was_killed); - - } - - if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == - (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { - - __afl_map_shm_fuzz(); - - } - - if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) == - (FS_OPT_ENABLED | FS_OPT_AUTODICT)) { - - // great lets pass the dictionary through the forkserver FD - u32 len = __afl_dictionary_len, offset = 0; - s32 ret; - - if (write(FORKSRV_FD + 1, &len, 4) != 4) { - - write(2, "Error: could not send dictionary len\n", - strlen("Error: could not send dictionary len\n")); - _exit(1); - - } - - while (len != 0) { - - ret = write(FORKSRV_FD + 1, __afl_dictionary + offset, len); - - if (ret < 1) { - - write(2, "Error: could not send dictionary\n", - strlen("Error: could not send dictionary\n")); - _exit(1); - - } - - len -= ret; - offset += ret; - - } - - } else { - - // uh this forkserver does not understand extended option passing - // or does not want the dictionary - if (!__afl_fuzz_ptr) already_read_first = 1; - - } - - } - - while (1) { - - int status; - - if (already_read_first) { - - already_read_first = 0; - - } else { - - /* Wait for parent by reading from the pipe. Abort if read fails. */ - if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); - - } - - #ifdef _AFL_DOCUMENT_MUTATIONS - if (__afl_fuzz_ptr) { - - static uint32_t counter = 0; - char fn[32]; - sprintf(fn, "%09u:forkserver", counter); - s32 fd_doc = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (fd_doc >= 0) { - - if (write(fd_doc, __afl_fuzz_ptr, *__afl_fuzz_len) != *__afl_fuzz_len) { - - fprintf(stderr, "write of mutation file failed: %s\n", fn); - unlink(fn); - - } - - close(fd_doc); - - } - - counter++; - - } - - #endif - - /* If we stopped the child in persistent mode, but there was a race - condition and afl-fuzz already issued SIGKILL, write off the old - process. */ - - if (child_stopped && was_killed) { - - child_stopped = 0; - if (waitpid(child_pid, &status, 0) < 0) _exit(1); - - } - - if (!child_stopped) { - - /* Once woken up, create a clone of our process. */ - - child_pid = fork(); - if (child_pid < 0) _exit(1); - - /* In child process: close fds, resume execution. */ - - if (!child_pid) { - - //(void)nice(-20); // does not seem to improve - - signal(SIGCHLD, old_sigchld_handler); - - close(FORKSRV_FD); - close(FORKSRV_FD + 1); - - if (!afl_snapshot_take(AFL_SNAPSHOT_MMAP | AFL_SNAPSHOT_FDS | - AFL_SNAPSHOT_REGS | AFL_SNAPSHOT_EXIT)) { - - raise(SIGSTOP); - - } - - __afl_area_ptr[0] = 1; - memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T)); - - return; - - } - - } else { - - /* Special handling for persistent mode: if the child is alive but - currently stopped, simply restart it with SIGCONT. */ - - kill(child_pid, SIGCONT); - child_stopped = 0; - - } - - /* In parent process: write PID to pipe, then wait for child. */ - - if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) _exit(1); - - if (waitpid(child_pid, &status, WUNTRACED) < 0) _exit(1); - - /* In persistent mode, the child stops itself with SIGSTOP to indicate - a successful run. In this case, we want to wake it up without forking - again. */ - - if (WIFSTOPPED(status)) child_stopped = 1; - - /* Relay wait status to pipe, then loop back. */ - - if (write(FORKSRV_FD + 1, &status, 4) != 4) _exit(1); - - } - -} - -#endif - -/* Fork server logic. */ - -static void __afl_start_forkserver(void) { - -#ifdef __linux__ - if (/*!is_persistent &&*/ !__afl_cmp_map && !getenv("AFL_NO_SNAPSHOT") && - afl_snapshot_init() >= 0) { - - __afl_start_snapshots(); - return; - - } - -#endif - - u8 tmp[4] = {0, 0, 0, 0}; - s32 child_pid; - u32 status = 0; - u32 already_read_first = 0; - u32 was_killed; - - u8 child_stopped = 0; - - void (*old_sigchld_handler)(int) = 0; // = signal(SIGCHLD, SIG_DFL); - - if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) - status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); - if (__afl_dictionary_len && __afl_dictionary) status |= FS_OPT_AUTODICT; - if (__afl_sharedmem_fuzzing != 0) status |= FS_OPT_SHDMEM_FUZZ; - if (status) status |= (FS_OPT_ENABLED); - memcpy(tmp, &status, 4); - - /* Phone home and tell the parent that we're OK. If parent isn't there, - assume we're not running in forkserver mode and just execute program. */ - - if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; - - if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) { - - if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); - - if (getenv("AFL_DEBUG")) { - - fprintf(stderr, "target forkserver recv: %08x\n", was_killed); - - } - - if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == - (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { - - __afl_map_shm_fuzz(); - - } - - if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) == - (FS_OPT_ENABLED | FS_OPT_AUTODICT)) { - - // great lets pass the dictionary through the forkserver FD - u32 len = __afl_dictionary_len, offset = 0; - s32 ret; - - if (write(FORKSRV_FD + 1, &len, 4) != 4) { - - write(2, "Error: could not send dictionary len\n", - strlen("Error: could not send dictionary len\n")); - _exit(1); - - } - - while (len != 0) { - - ret = write(FORKSRV_FD + 1, __afl_dictionary + offset, len); - - if (ret < 1) { - - write(2, "Error: could not send dictionary\n", - strlen("Error: could not send dictionary\n")); - _exit(1); - - } - - len -= ret; - offset += ret; - - } - - } else { - - // uh this forkserver does not understand extended option passing - // or does not want the dictionary - if (!__afl_fuzz_ptr) already_read_first = 1; - - } - - } - - while (1) { - - int status; - - /* Wait for parent by reading from the pipe. Abort if read fails. */ - - if (already_read_first) { - - already_read_first = 0; - - } else { - - if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); - - } - -#ifdef _AFL_DOCUMENT_MUTATIONS - if (__afl_fuzz_ptr) { - - static uint32_t counter = 0; - char fn[32]; - sprintf(fn, "%09u:forkserver", counter); - s32 fd_doc = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (fd_doc >= 0) { - - if (write(fd_doc, __afl_fuzz_ptr, *__afl_fuzz_len) != *__afl_fuzz_len) { - - fprintf(stderr, "write of mutation file failed: %s\n", fn); - unlink(fn); - - } - - close(fd_doc); - - } - - counter++; - - } - -#endif - - /* If we stopped the child in persistent mode, but there was a race - condition and afl-fuzz already issued SIGKILL, write off the old - process. */ - - if (child_stopped && was_killed) { - - child_stopped = 0; - if (waitpid(child_pid, &status, 0) < 0) _exit(1); - - } - - if (!child_stopped) { - - /* Once woken up, create a clone of our process. */ - - child_pid = fork(); - if (child_pid < 0) _exit(1); - - /* In child process: close fds, resume execution. */ - - if (!child_pid) { - - //(void)nice(-20); - - signal(SIGCHLD, old_sigchld_handler); - - close(FORKSRV_FD); - close(FORKSRV_FD + 1); - return; - - } - - } else { - - /* Special handling for persistent mode: if the child is alive but - currently stopped, simply restart it with SIGCONT. */ - - kill(child_pid, SIGCONT); - child_stopped = 0; - - } - - /* In parent process: write PID to pipe, then wait for child. */ - - if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) _exit(1); - - if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0) - _exit(1); - - /* In persistent mode, the child stops itself with SIGSTOP to indicate - a successful run. In this case, we want to wake it up without forking - again. */ - - if (WIFSTOPPED(status)) child_stopped = 1; - - /* Relay wait status to pipe, then loop back. */ - - if (write(FORKSRV_FD + 1, &status, 4) != 4) _exit(1); - - } - -} - -/* A simplified persistent mode handler, used as explained in - * llvm_mode/README.md. */ - -int __afl_persistent_loop(unsigned int max_cnt) { - - static u8 first_pass = 1; - static u32 cycle_cnt; - - if (first_pass) { - - /* Make sure that every iteration of __AFL_LOOP() starts with a clean slate. - On subsequent calls, the parent will take care of that, but on the first - iteration, it's our job to erase any trace of whatever happened - before the loop. */ - - if (is_persistent) { - - memset(__afl_area_ptr, 0, __afl_map_size); - __afl_area_ptr[0] = 1; - memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T)); - - } - - cycle_cnt = max_cnt; - first_pass = 0; - return 1; - - } - - if (is_persistent) { - - if (--cycle_cnt) { - - raise(SIGSTOP); - - __afl_area_ptr[0] = 1; - memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T)); - - return 1; - - } else { - - /* When exiting __AFL_LOOP(), make sure that the subsequent code that - follows the loop is not traced. We do that by pivoting back to the - dummy output region. */ - - __afl_area_ptr = __afl_area_initial; - - } - - } - - return 0; - -} - -/* This one can be called from user code when deferred forkserver mode - is enabled. */ - -void __afl_manual_init(void) { - - static u8 init_done; - - if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) { - - init_done = 1; - is_persistent = 0; - __afl_sharedmem_fuzzing = 0; - if (__afl_area_ptr == NULL) __afl_area_ptr = __afl_area_initial; - - if (getenv("AFL_DEBUG")) - fprintf(stderr, - "DEBUG: disabled instrumentation because of " - "AFL_DISABLE_LLVM_INSTRUMENTATION\n"); - - } - - if (!init_done) { - - __afl_start_forkserver(); - init_done = 1; - - } - -} - -/* Initialization of the forkserver - latest possible */ - -__attribute__((constructor())) void __afl_auto_init(void) { - - if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; - - if (getenv(DEFER_ENV_VAR)) return; - - __afl_manual_init(); - -} - -/* Initialization of the shmem - earliest possible because of LTO fixed mem. */ - -__attribute__((constructor(CTOR_PRIO))) void __afl_auto_early(void) { - - is_persistent = !!getenv(PERSIST_ENV_VAR); - - if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; - - __afl_map_shm(); - -} - -/* preset __afl_area_ptr #2 */ - -__attribute__((constructor(1))) void __afl_auto_second(void) { - - if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; - u8 *ptr; - - if (__afl_final_loc) { - - if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial) - free(__afl_area_ptr); - - if (__afl_map_addr) - ptr = (u8 *)mmap((void *)__afl_map_addr, __afl_final_loc, - PROT_READ | PROT_WRITE, - MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); - else - ptr = (u8 *)malloc(__afl_final_loc); - - if (ptr && (ssize_t)ptr != -1) __afl_area_ptr = ptr; - - } - -} - -/* 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; - - ptr = (u8 *)malloc(1024000); - - if (ptr && (ssize_t)ptr != -1) __afl_area_ptr = ptr; - -} - -/* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard. - It remains non-operational in the traditional, plugin-backed LLVM mode. - For more info about 'trace-pc-guard', see llvm_mode/README.md. - - The first function (__sanitizer_cov_trace_pc_guard) is called back on every - edge (as opposed to every basic block). */ - -void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { - - // For stability analysis, if you want to know to which function unstable - // edge IDs belong - uncomment, recompile+install llvm_mode, recompile - // the target. libunwind and libbacktrace are better solutions. - // Set AFL_DEBUG_CHILD_OUTPUT=1 and run afl-fuzz with 2>file to capture - // the backtrace output - /* - uint32_t unstable[] = { ... unstable edge IDs }; - uint32_t idx; - char bt[1024]; - for (idx = 0; i < sizeof(unstable)/sizeof(uint32_t); i++) { - - if (unstable[idx] == __afl_area_ptr[*guard]) { - - int bt_size = backtrace(bt, 256); - if (bt_size > 0) { - - char **bt_syms = backtrace_symbols(bt, bt_size); - if (bt_syms) { - - fprintf(stderr, "DEBUG: edge=%u caller=%s\n", unstable[idx], - bt_syms[0]); - free(bt_syms); - - } - - } - - } - - } - - */ - -#if (LLVM_VERSION_MAJOR < 9) - - __afl_area_ptr[*guard]++; - -#else - - __afl_area_ptr[*guard] = - __afl_area_ptr[*guard] + 1 + (__afl_area_ptr[*guard] == 255 ? 1 : 0); - -#endif - -} - -/* Init callback. Populates instrumentation IDs. Note that we're using - ID of 0 as a special value to indicate non-instrumented bits. That may - still touch the bitmap, but in a fairly harmless way. */ - -void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { - - u32 inst_ratio = 100; - char *x; - - _is_sancov = 1; - - if (getenv("AFL_DEBUG")) { - - fprintf(stderr, "Running __sanitizer_cov_trace_pc_guard_init: %p-%p\n", - start, stop); - - } - - if (start == stop || *start) return; - - x = getenv("AFL_INST_RATIO"); - if (x) inst_ratio = (u32)atoi(x); - - if (!inst_ratio || inst_ratio > 100) { - - fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n"); - abort(); - - } - - /* 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). */ - - *(start++) = R(MAP_SIZE - 1) + 1; - - while (start < stop) { - - if (R(100) < inst_ratio) - *start = ++__afl_final_loc; - else - *start = 0; - - start++; - - } - -} - -///// CmpLog instrumentation - -void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { - - if (unlikely(!__afl_cmp_map)) return; - - uintptr_t k = (uintptr_t)__builtin_return_address(0); - k = (k >> 4) ^ (k << 8); - k &= CMP_MAP_W - 1; - - __afl_cmp_map->headers[k].type = CMP_TYPE_INS; - - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; - // if (!__afl_cmp_map->headers[k].cnt) - // __afl_cmp_map->headers[k].cnt = __afl_cmp_counter++; - - __afl_cmp_map->headers[k].shape = 0; - - hits &= CMP_MAP_H - 1; - __afl_cmp_map->log[k][hits].v0 = arg1; - __afl_cmp_map->log[k][hits].v1 = arg2; - -} - -void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { - - if (unlikely(!__afl_cmp_map)) return; - - uintptr_t k = (uintptr_t)__builtin_return_address(0); - k = (k >> 4) ^ (k << 8); - k &= CMP_MAP_W - 1; - - __afl_cmp_map->headers[k].type = CMP_TYPE_INS; - - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; - - __afl_cmp_map->headers[k].shape = 1; - - hits &= CMP_MAP_H - 1; - __afl_cmp_map->log[k][hits].v0 = arg1; - __afl_cmp_map->log[k][hits].v1 = arg2; - -} - -void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { - - if (unlikely(!__afl_cmp_map)) return; - - uintptr_t k = (uintptr_t)__builtin_return_address(0); - k = (k >> 4) ^ (k << 8); - k &= CMP_MAP_W - 1; - - __afl_cmp_map->headers[k].type = CMP_TYPE_INS; - - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; - - __afl_cmp_map->headers[k].shape = 3; - - hits &= CMP_MAP_H - 1; - __afl_cmp_map->log[k][hits].v0 = arg1; - __afl_cmp_map->log[k][hits].v1 = arg2; - -} - -void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) { - - if (unlikely(!__afl_cmp_map)) return; - - uintptr_t k = (uintptr_t)__builtin_return_address(0); - k = (k >> 4) ^ (k << 8); - k &= CMP_MAP_W - 1; - - __afl_cmp_map->headers[k].type = CMP_TYPE_INS; - - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; - - __afl_cmp_map->headers[k].shape = 7; - - hits &= CMP_MAP_H - 1; - __afl_cmp_map->log[k][hits].v0 = arg1; - __afl_cmp_map->log[k][hits].v1 = arg2; - -} - -#if defined(__APPLE__) - #pragma weak __sanitizer_cov_trace_const_cmp1 = __cmplog_ins_hook1 - #pragma weak __sanitizer_cov_trace_const_cmp2 = __cmplog_ins_hook2 - #pragma weak __sanitizer_cov_trace_const_cmp4 = __cmplog_ins_hook4 - #pragma weak __sanitizer_cov_trace_const_cmp8 = __cmplog_ins_hook8 - - #pragma weak __sanitizer_cov_trace_cmp1 = __cmplog_ins_hook1 - #pragma weak __sanitizer_cov_trace_cmp2 = __cmplog_ins_hook2 - #pragma weak __sanitizer_cov_trace_cmp4 = __cmplog_ins_hook4 - #pragma weak __sanitizer_cov_trace_cmp8 = __cmplog_ins_hook8 -#else -void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) - __attribute__((alias("__cmplog_ins_hook1"))); -void __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2) - __attribute__((alias("__cmplog_ins_hook2"))); -void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2) - __attribute__((alias("__cmplog_ins_hook4"))); -void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2) - __attribute__((alias("__cmplog_ins_hook8"))); - -void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) - __attribute__((alias("__cmplog_ins_hook1"))); -void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) - __attribute__((alias("__cmplog_ins_hook2"))); -void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) - __attribute__((alias("__cmplog_ins_hook4"))); -void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) - __attribute__((alias("__cmplog_ins_hook8"))); -#endif /* defined(__APPLE__) */ - -void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { - - if (unlikely(!__afl_cmp_map)) return; - - for (uint64_t i = 0; i < cases[0]; i++) { - - uintptr_t k = (uintptr_t)__builtin_return_address(0) + i; - k = (k >> 4) ^ (k << 8); - k &= CMP_MAP_W - 1; - - __afl_cmp_map->headers[k].type = CMP_TYPE_INS; - - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; - - __afl_cmp_map->headers[k].shape = 7; - - hits &= CMP_MAP_H - 1; - __afl_cmp_map->log[k][hits].v0 = val; - __afl_cmp_map->log[k][hits].v1 = cases[i + 2]; - - } - -} - -// 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) { - - char *p = ptr; - char *page = (char *)((uintptr_t)p & ~(sysconf(_SC_PAGE_SIZE) - 1)); - - int r = msync(page, (p - page) + len, MS_ASYNC); - if (r < 0) return errno != ENOMEM; - return 1; - -} - -void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { - - if (unlikely(!__afl_cmp_map)) return; - - if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; - - uintptr_t k = (uintptr_t)__builtin_return_address(0); - k = (k >> 4) ^ (k << 8); - k &= CMP_MAP_W - 1; - - __afl_cmp_map->headers[k].type = CMP_TYPE_RTN; - - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; - - __afl_cmp_map->headers[k].shape = 31; - - hits &= CMP_MAP_RTN_H - 1; - __builtin_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0, - ptr1, 32); - __builtin_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1, - ptr2, 32); - -} - diff --git a/llvm_mode/cmplog-instructions-pass.cc b/llvm_mode/cmplog-instructions-pass.cc deleted file mode 100644 index d5de3dbb..00000000 --- a/llvm_mode/cmplog-instructions-pass.cc +++ /dev/null @@ -1,292 +0,0 @@ -/* - american fuzzy lop++ - LLVM CmpLog instrumentation - -------------------------------------------------- - - Written by Andrea Fioraldi - - Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - -*/ - -#include -#include -#include - -#include -#include -#include -#include -#include "llvm/Config/llvm-config.h" - -#include "llvm/ADT/Statistic.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Pass.h" -#include "llvm/Analysis/ValueTracking.h" - -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) - #include "llvm/IR/Verifier.h" - #include "llvm/IR/DebugInfo.h" -#else - #include "llvm/Analysis/Verifier.h" - #include "llvm/DebugInfo.h" - #define nullptr 0 -#endif - -#include -#include "afl-llvm-common.h" - -using namespace llvm; - -namespace { - -class CmpLogInstructions : public ModulePass { - - public: - static char ID; - CmpLogInstructions() : ModulePass(ID) { - - initInstrumentList(); - - } - - bool runOnModule(Module &M) override; - -#if LLVM_VERSION_MAJOR < 4 - const char *getPassName() const override { - -#else - StringRef getPassName() const override { - -#endif - return "cmplog instructions"; - - } - - private: - bool hookInstrs(Module &M); - -}; - -} // namespace - -char CmpLogInstructions::ID = 0; - -bool CmpLogInstructions::hookInstrs(Module &M) { - - std::vector icomps; - LLVMContext & C = M.getContext(); - - Type * VoidTy = Type::getVoidTy(C); - IntegerType *Int8Ty = IntegerType::getInt8Ty(C); - IntegerType *Int16Ty = IntegerType::getInt16Ty(C); - IntegerType *Int32Ty = IntegerType::getInt32Ty(C); - IntegerType *Int64Ty = IntegerType::getInt64Ty(C); - -#if LLVM_VERSION_MAJOR < 9 - Constant * -#else - FunctionCallee -#endif - c1 = M.getOrInsertFunction("__cmplog_ins_hook1", VoidTy, Int8Ty, Int8Ty -#if LLVM_VERSION_MAJOR < 5 - , - NULL -#endif - ); -#if LLVM_VERSION_MAJOR < 9 - Function *cmplogHookIns1 = cast(c1); -#else - FunctionCallee cmplogHookIns1 = c1; -#endif - -#if LLVM_VERSION_MAJOR < 9 - Constant * -#else - FunctionCallee -#endif - c2 = M.getOrInsertFunction("__cmplog_ins_hook2", VoidTy, Int16Ty, Int16Ty -#if LLVM_VERSION_MAJOR < 5 - , - NULL -#endif - ); -#if LLVM_VERSION_MAJOR < 9 - Function *cmplogHookIns2 = cast(c2); -#else - FunctionCallee cmplogHookIns2 = c2; -#endif - -#if LLVM_VERSION_MAJOR < 9 - Constant * -#else - FunctionCallee -#endif - c4 = M.getOrInsertFunction("__cmplog_ins_hook4", VoidTy, Int32Ty, Int32Ty -#if LLVM_VERSION_MAJOR < 5 - , - NULL -#endif - ); -#if LLVM_VERSION_MAJOR < 9 - Function *cmplogHookIns4 = cast(c4); -#else - FunctionCallee cmplogHookIns4 = c4; -#endif - -#if LLVM_VERSION_MAJOR < 9 - Constant * -#else - FunctionCallee -#endif - c8 = M.getOrInsertFunction("__cmplog_ins_hook8", VoidTy, Int64Ty, Int64Ty -#if LLVM_VERSION_MAJOR < 5 - , - NULL -#endif - ); -#if LLVM_VERSION_MAJOR < 9 - Function *cmplogHookIns8 = cast(c8); -#else - FunctionCallee cmplogHookIns8 = c8; -#endif - - /* iterate over all functions, bbs and instruction and add suitable calls */ - for (auto &F : M) { - - if (!isInInstrumentList(&F)) continue; - - for (auto &BB : F) { - - for (auto &IN : BB) { - - CmpInst *selectcmpInst = nullptr; - - if ((selectcmpInst = dyn_cast(&IN))) { - - if (selectcmpInst->getPredicate() == CmpInst::ICMP_EQ || - selectcmpInst->getPredicate() == CmpInst::ICMP_NE || - selectcmpInst->getPredicate() == CmpInst::ICMP_UGT || - selectcmpInst->getPredicate() == CmpInst::ICMP_SGT || - selectcmpInst->getPredicate() == CmpInst::ICMP_ULT || - selectcmpInst->getPredicate() == CmpInst::ICMP_SLT || - selectcmpInst->getPredicate() == CmpInst::ICMP_UGE || - selectcmpInst->getPredicate() == CmpInst::ICMP_SGE || - selectcmpInst->getPredicate() == CmpInst::ICMP_ULE || - selectcmpInst->getPredicate() == CmpInst::ICMP_SLE) { - - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); - - IntegerType *intTyOp0 = dyn_cast(op0->getType()); - IntegerType *intTyOp1 = dyn_cast(op1->getType()); - - /* this is probably not needed but we do it anyway */ - if (!intTyOp0 || !intTyOp1) { continue; } - - icomps.push_back(selectcmpInst); - - } - - } - - } - - } - - } - - if (!icomps.size()) return false; - if (!be_quiet) errs() << "Hooking " << icomps.size() << " cmp instructions\n"; - - for (auto &selectcmpInst : icomps) { - - IRBuilder<> IRB(selectcmpInst->getParent()); - IRB.SetInsertPoint(selectcmpInst); - - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); - - IntegerType *intTyOp0 = dyn_cast(op0->getType()); - IntegerType *intTyOp1 = dyn_cast(op1->getType()); - - unsigned max_size = intTyOp0->getBitWidth() > intTyOp1->getBitWidth() - ? intTyOp0->getBitWidth() - : intTyOp1->getBitWidth(); - - std::vector args; - args.push_back(op0); - args.push_back(op1); - - switch (max_size) { - - case 8: - IRB.CreateCall(cmplogHookIns1, args); - break; - case 16: - IRB.CreateCall(cmplogHookIns2, args); - break; - case 32: - IRB.CreateCall(cmplogHookIns4, args); - break; - case 64: - IRB.CreateCall(cmplogHookIns8, args); - break; - default: - break; - - } - - } - - return true; - -} - -bool CmpLogInstructions::runOnModule(Module &M) { - - if (getenv("AFL_QUIET") == NULL) - llvm::errs() - << "Running cmplog-instructions-pass by andreafioraldi@gmail.com\n"; - else - be_quiet = 1; - hookInstrs(M); - verifyModule(M); - - return true; - -} - -static void registerCmpLogInstructionsPass(const PassManagerBuilder &, - legacy::PassManagerBase &PM) { - - auto p = new CmpLogInstructions(); - PM.add(p); - -} - -static RegisterStandardPasses RegisterCmpLogInstructionsPass( - PassManagerBuilder::EP_OptimizerLast, registerCmpLogInstructionsPass); - -static RegisterStandardPasses RegisterCmpLogInstructionsPass0( - PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogInstructionsPass); - -#if LLVM_VERSION_MAJOR >= 11 -static RegisterStandardPasses RegisterCmpLogInstructionsPassLTO( - PassManagerBuilder::EP_FullLinkTimeOptimizationLast, - registerCmpLogInstructionsPass); -#endif - diff --git a/llvm_mode/cmplog-routines-pass.cc b/llvm_mode/cmplog-routines-pass.cc deleted file mode 100644 index c44f38c4..00000000 --- a/llvm_mode/cmplog-routines-pass.cc +++ /dev/null @@ -1,212 +0,0 @@ -/* - american fuzzy lop++ - LLVM CmpLog instrumentation - -------------------------------------------------- - - Written by Andrea Fioraldi - - Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - -*/ - -#include -#include -#include - -#include -#include -#include -#include -#include "llvm/Config/llvm-config.h" - -#include "llvm/ADT/Statistic.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Pass.h" -#include "llvm/Analysis/ValueTracking.h" - -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) - #include "llvm/IR/Verifier.h" - #include "llvm/IR/DebugInfo.h" -#else - #include "llvm/Analysis/Verifier.h" - #include "llvm/DebugInfo.h" - #define nullptr 0 -#endif - -#include -#include "afl-llvm-common.h" - -using namespace llvm; - -namespace { - -class CmpLogRoutines : public ModulePass { - - public: - static char ID; - CmpLogRoutines() : ModulePass(ID) { - - initInstrumentList(); - - } - - bool runOnModule(Module &M) override; - -#if LLVM_VERSION_MAJOR < 4 - const char *getPassName() const override { - -#else - StringRef getPassName() const override { - -#endif - return "cmplog routines"; - - } - - private: - bool hookRtns(Module &M); - -}; - -} // namespace - -char CmpLogRoutines::ID = 0; - -bool CmpLogRoutines::hookRtns(Module &M) { - - std::vector calls; - LLVMContext & C = M.getContext(); - - Type *VoidTy = Type::getVoidTy(C); - // PointerType *VoidPtrTy = PointerType::get(VoidTy, 0); - IntegerType *Int8Ty = IntegerType::getInt8Ty(C); - PointerType *i8PtrTy = PointerType::get(Int8Ty, 0); - -#if LLVM_VERSION_MAJOR < 9 - Constant * -#else - FunctionCallee -#endif - c = M.getOrInsertFunction("__cmplog_rtn_hook", VoidTy, i8PtrTy, i8PtrTy -#if LLVM_VERSION_MAJOR < 5 - , - NULL -#endif - ); -#if LLVM_VERSION_MAJOR < 9 - Function *cmplogHookFn = cast(c); -#else - FunctionCallee cmplogHookFn = c; -#endif - - /* iterate over all functions, bbs and instruction and add suitable calls */ - for (auto &F : M) { - - if (!isInInstrumentList(&F)) continue; - - for (auto &BB : F) { - - for (auto &IN : BB) { - - CallInst *callInst = nullptr; - - if ((callInst = dyn_cast(&IN))) { - - Function *Callee = callInst->getCalledFunction(); - if (!Callee) continue; - if (callInst->getCallingConv() != llvm::CallingConv::C) continue; - - FunctionType *FT = Callee->getFunctionType(); - - bool isPtrRtn = FT->getNumParams() >= 2 && - !FT->getReturnType()->isVoidTy() && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0)->isPointerTy(); - - if (!isPtrRtn) continue; - - calls.push_back(callInst); - - } - - } - - } - - } - - if (!calls.size()) return false; - if (!be_quiet) - errs() << "Hooking " << calls.size() - << " calls with pointers as arguments\n"; - - for (auto &callInst : calls) { - - Value *v1P = callInst->getArgOperand(0), *v2P = callInst->getArgOperand(1); - - IRBuilder<> IRB(callInst->getParent()); - IRB.SetInsertPoint(callInst); - - std::vector args; - Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); - Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); - args.push_back(v1Pcasted); - args.push_back(v2Pcasted); - - IRB.CreateCall(cmplogHookFn, args); - - // errs() << callInst->getCalledFunction()->getName() << "\n"; - - } - - return true; - -} - -bool CmpLogRoutines::runOnModule(Module &M) { - - if (getenv("AFL_QUIET") == NULL) - llvm::errs() - << "Running cmplog-routines-pass by andreafioraldi@gmail.com\n"; - else - be_quiet = 1; - hookRtns(M); - verifyModule(M); - - return true; - -} - -static void registerCmpLogRoutinesPass(const PassManagerBuilder &, - legacy::PassManagerBase &PM) { - - auto p = new CmpLogRoutines(); - PM.add(p); - -} - -static RegisterStandardPasses RegisterCmpLogRoutinesPass( - PassManagerBuilder::EP_OptimizerLast, registerCmpLogRoutinesPass); - -static RegisterStandardPasses RegisterCmpLogRoutinesPass0( - PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogRoutinesPass); - -#if LLVM_VERSION_MAJOR >= 11 -static RegisterStandardPasses RegisterCmpLogRoutinesPassLTO( - PassManagerBuilder::EP_FullLinkTimeOptimizationLast, - registerCmpLogRoutinesPass); -#endif - diff --git a/llvm_mode/compare-transform-pass.so.cc b/llvm_mode/compare-transform-pass.so.cc deleted file mode 100644 index acdd0f3b..00000000 --- a/llvm_mode/compare-transform-pass.so.cc +++ /dev/null @@ -1,587 +0,0 @@ -/* - * Copyright 2016 laf-intel - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include "llvm/Config/llvm-config.h" - -#include "llvm/ADT/Statistic.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Pass.h" -#include "llvm/Analysis/ValueTracking.h" - -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) - #include "llvm/IR/Verifier.h" - #include "llvm/IR/DebugInfo.h" -#else - #include "llvm/Analysis/Verifier.h" - #include "llvm/DebugInfo.h" - #define nullptr 0 -#endif - -#include -#include "afl-llvm-common.h" - -using namespace llvm; - -namespace { - -class CompareTransform : public ModulePass { - - public: - static char ID; - CompareTransform() : ModulePass(ID) { - - initInstrumentList(); - - } - - bool runOnModule(Module &M) override; - -#if LLVM_VERSION_MAJOR < 4 - const char *getPassName() const override { - -#else - StringRef getPassName() const override { - -#endif - return "transforms compare functions"; - - } - - private: - bool transformCmps(Module &M, const bool processStrcmp, - const bool processMemcmp, const bool processStrncmp, - const bool processStrcasecmp, - const bool processStrncasecmp); - -}; - -} // namespace - -char CompareTransform::ID = 0; - -bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, - const bool processMemcmp, - const bool processStrncmp, - const bool processStrcasecmp, - const bool processStrncasecmp) { - - DenseMap valueMap; - std::vector calls; - LLVMContext & C = M.getContext(); - IntegerType * Int8Ty = IntegerType::getInt8Ty(C); - IntegerType * Int32Ty = IntegerType::getInt32Ty(C); - IntegerType * Int64Ty = IntegerType::getInt64Ty(C); - -#if LLVM_VERSION_MAJOR < 9 - Constant * -#else - FunctionCallee -#endif - c = M.getOrInsertFunction("tolower", Int32Ty, Int32Ty -#if LLVM_VERSION_MAJOR < 5 - , - NULL -#endif - ); -#if LLVM_VERSION_MAJOR < 9 - Function *tolowerFn = cast(c); -#else - FunctionCallee tolowerFn = c; -#endif - - /* iterate over all functions, bbs and instruction and add suitable calls to - * strcmp/memcmp/strncmp/strcasecmp/strncasecmp */ - for (auto &F : M) { - - if (!isInInstrumentList(&F)) continue; - - for (auto &BB : F) { - - for (auto &IN : BB) { - - CallInst *callInst = nullptr; - - if ((callInst = dyn_cast(&IN))) { - - bool isStrcmp = processStrcmp; - bool isMemcmp = processMemcmp; - bool isStrncmp = processStrncmp; - bool isStrcasecmp = processStrcasecmp; - bool isStrncasecmp = processStrncasecmp; - bool isIntMemcpy = true; - - Function *Callee = callInst->getCalledFunction(); - if (!Callee) continue; - if (callInst->getCallingConv() != llvm::CallingConv::C) continue; - StringRef FuncName = Callee->getName(); - isStrcmp &= !FuncName.compare(StringRef("strcmp")); - isMemcmp &= !FuncName.compare(StringRef("memcmp")); - isStrncmp &= !FuncName.compare(StringRef("strncmp")); - isStrcasecmp &= !FuncName.compare(StringRef("strcasecmp")); - isStrncasecmp &= !FuncName.compare(StringRef("strncasecmp")); - isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); - - if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) - continue; - - /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function - * prototype */ - FunctionType *FT = Callee->getFunctionType(); - - isStrcmp &= - FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); - isStrcasecmp &= - FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); - isMemcmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0)->isPointerTy() && - FT->getParamType(1)->isPointerTy() && - FT->getParamType(2)->isIntegerTy(); - isStrncmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); - isStrncasecmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); - - if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) - continue; - - /* is a str{n,}{case,}cmp/memcmp, check if we have - * str{case,}cmp(x, "const") or str{case,}cmp("const", x) - * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, ..) - * memcmp(x, "const", ..) or memcmp("const", x, ..) */ - Value *Str1P = callInst->getArgOperand(0), - *Str2P = callInst->getArgOperand(1); - StringRef Str1, Str2; - bool HasStr1 = getConstantStringInfo(Str1P, Str1); - bool HasStr2 = getConstantStringInfo(Str2P, Str2); - - if (isIntMemcpy && HasStr2) { - - valueMap[Str1P] = new std::string(Str2.str()); - // fprintf(stderr, "saved %s for %p\n", Str2.str().c_str(), Str1P); - continue; - - } - - // not literal? maybe global or local variable - if (!(HasStr1 || HasStr2)) { - - auto *Ptr = dyn_cast(Str2P); - if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { - - if (auto *Var = dyn_cast(Ptr->getOperand(0))) { - - if (Var->hasInitializer()) { - - if (auto *Array = - dyn_cast(Var->getInitializer())) { - - HasStr2 = true; - Str2 = Array->getAsString(); - valueMap[Str2P] = new std::string(Str2.str()); - fprintf(stderr, "glo2 %s\n", Str2.str().c_str()); - - } - - } - - } - - } - - if (!HasStr2) { - - auto *Ptr = dyn_cast(Str1P); - if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { - - if (auto *Var = dyn_cast(Ptr->getOperand(0))) { - - if (Var->hasInitializer()) { - - if (auto *Array = dyn_cast( - Var->getInitializer())) { - - HasStr1 = true; - Str1 = Array->getAsString(); - valueMap[Str1P] = new std::string(Str1.str()); - // fprintf(stderr, "glo1 %s\n", Str1.str().c_str()); - - } - - } - - } - - } - - } else if (isIntMemcpy) { - - valueMap[Str1P] = new std::string(Str2.str()); - // fprintf(stderr, "saved\n"); - - } - - } - - if (isIntMemcpy) continue; - - if (!(HasStr1 || HasStr2)) { - - // do we have a saved local variable initialization? - std::string *val = valueMap[Str1P]; - if (val && !val->empty()) { - - Str1 = StringRef(*val); - HasStr1 = true; - // fprintf(stderr, "loaded1 %s\n", Str1.str().c_str()); - - } else { - - val = valueMap[Str2P]; - if (val && !val->empty()) { - - Str2 = StringRef(*val); - HasStr2 = true; - // fprintf(stderr, "loaded2 %s\n", Str2.str().c_str()); - - } - - } - - } - - /* handle cases of one string is const, one string is variable */ - if (!(HasStr1 || HasStr2)) continue; - - if (isMemcmp || isStrncmp || isStrncasecmp) { - - /* check if third operand is a constant integer - * strlen("constStr") and sizeof() are treated as constant */ - Value * op2 = callInst->getArgOperand(2); - ConstantInt *ilen = dyn_cast(op2); - if (ilen) { - - uint64_t len = ilen->getZExtValue(); - // if len is zero this is a pointless call but allow real - // implementation to worry about that - if (!len) continue; - - if (isMemcmp) { - - // if size of compare is larger than constant string this is - // likely a bug but allow real implementation to worry about - // that - uint64_t literalLength = HasStr1 ? Str1.size() : Str2.size(); - if (literalLength + 1 < ilen->getZExtValue()) continue; - - } - - } else if (isMemcmp) - - // this *may* supply a len greater than the constant string at - // runtime so similarly we don't want to have to handle that - continue; - - } - - calls.push_back(callInst); - - } - - } - - } - - } - - if (!calls.size()) return false; - if (!be_quiet) - errs() << "Replacing " << calls.size() - << " calls to strcmp/memcmp/strncmp/strcasecmp/strncasecmp\n"; - - for (auto &callInst : calls) { - - Value *Str1P = callInst->getArgOperand(0), - *Str2P = callInst->getArgOperand(1); - StringRef Str1, Str2, ConstStr; - std::string TmpConstStr; - Value * VarStr; - bool HasStr1 = getConstantStringInfo(Str1P, Str1); - bool HasStr2 = getConstantStringInfo(Str2P, Str2); - uint64_t constStrLen, unrollLen, constSizedLen = 0; - bool isMemcmp = - !callInst->getCalledFunction()->getName().compare(StringRef("memcmp")); - bool isSizedcmp = isMemcmp || - !callInst->getCalledFunction()->getName().compare( - StringRef("strncmp")) || - !callInst->getCalledFunction()->getName().compare( - StringRef("strncasecmp")); - Value *sizedValue = isSizedcmp ? callInst->getArgOperand(2) : NULL; - bool isConstSized = sizedValue && isa(sizedValue); - bool isCaseInsensitive = !callInst->getCalledFunction()->getName().compare( - StringRef("strcasecmp")) || - !callInst->getCalledFunction()->getName().compare( - StringRef("strncasecmp")); - - if (!(HasStr1 || HasStr2)) { - - // do we have a saved local or global variable initialization? - std::string *val = valueMap[Str1P]; - if (val && !val->empty()) { - - Str1 = StringRef(*val); - HasStr1 = true; - - } else { - - val = valueMap[Str2P]; - if (val && !val->empty()) { - - Str2 = StringRef(*val); - HasStr2 = true; - - } - - } - - } - - if (isConstSized) { - - constSizedLen = dyn_cast(sizedValue)->getZExtValue(); - - } - - if (HasStr1) { - - TmpConstStr = Str1.str(); - VarStr = Str2P; - - } else { - - TmpConstStr = Str2.str(); - VarStr = Str1P; - - } - - // add null termination character implicit in c strings - TmpConstStr.append("\0", 1); - - // in the unusual case the const str has embedded null - // characters, the string comparison functions should terminate - // at the first null - if (!isMemcmp) - TmpConstStr.assign(TmpConstStr, 0, TmpConstStr.find('\0') + 1); - - constStrLen = TmpConstStr.length(); - // prefer use of StringRef (in comparison to std::string a StringRef has - // built-in runtime bounds checking, which makes debugging easier) - ConstStr = StringRef(TmpConstStr); - - if (isConstSized) - unrollLen = constSizedLen < constStrLen ? constSizedLen : constStrLen; - else - unrollLen = constStrLen; - - if (!be_quiet) - errs() << callInst->getCalledFunction()->getName() << ": unroll len " - << unrollLen - << ((isSizedcmp && !isConstSized) ? ", variable n" : "") << ": " - << ConstStr << "\n"; - - /* split before the call instruction */ - BasicBlock *bb = callInst->getParent(); - BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(callInst)); - - BasicBlock *next_lenchk_bb = NULL; - if (isSizedcmp && !isConstSized) { - - next_lenchk_bb = - BasicBlock::Create(C, "len_check", end_bb->getParent(), end_bb); - BranchInst::Create(end_bb, next_lenchk_bb); - - } - - BasicBlock *next_cmp_bb = - BasicBlock::Create(C, "cmp_added", end_bb->getParent(), end_bb); - BranchInst::Create(end_bb, next_cmp_bb); - PHINode *PN = PHINode::Create( - Int32Ty, (next_lenchk_bb ? 2 : 1) * unrollLen + 1, "cmp_phi"); - -#if LLVM_VERSION_MAJOR < 8 - TerminatorInst *term = bb->getTerminator(); -#else - Instruction *term = bb->getTerminator(); -#endif - BranchInst::Create(next_lenchk_bb ? next_lenchk_bb : next_cmp_bb, bb); - term->eraseFromParent(); - - for (uint64_t i = 0; i < unrollLen; i++) { - - BasicBlock * cur_cmp_bb = next_cmp_bb, *cur_lenchk_bb = next_lenchk_bb; - unsigned char c; - - if (cur_lenchk_bb) { - - IRBuilder<> cur_lenchk_IRB(&*(cur_lenchk_bb->getFirstInsertionPt())); - Value * icmp = cur_lenchk_IRB.CreateICmpEQ( - sizedValue, ConstantInt::get(sizedValue->getType(), i)); - cur_lenchk_IRB.CreateCondBr(icmp, end_bb, cur_cmp_bb); - cur_lenchk_bb->getTerminator()->eraseFromParent(); - - PN->addIncoming(ConstantInt::get(Int32Ty, 0), cur_lenchk_bb); - - } - - if (isCaseInsensitive) - c = (unsigned char)(tolower((int)ConstStr[i]) & 0xff); - else - c = (unsigned char)ConstStr[i]; - - IRBuilder<> cur_cmp_IRB(&*(cur_cmp_bb->getFirstInsertionPt())); - - Value *v = ConstantInt::get(Int64Ty, i); - Value *ele = cur_cmp_IRB.CreateInBoundsGEP(VarStr, v, "empty"); - Value *load = cur_cmp_IRB.CreateLoad(ele); - - if (isCaseInsensitive) { - - // load >= 'A' && load <= 'Z' ? load | 0x020 : load - load = cur_cmp_IRB.CreateZExt(load, Int32Ty); - std::vector args; - args.push_back(load); - load = cur_cmp_IRB.CreateCall(tolowerFn, args); - load = cur_cmp_IRB.CreateTrunc(load, Int8Ty); - - } - - Value *isub; - if (HasStr1) - isub = cur_cmp_IRB.CreateSub(ConstantInt::get(Int8Ty, c), load); - else - isub = cur_cmp_IRB.CreateSub(load, ConstantInt::get(Int8Ty, c)); - - Value *sext = cur_cmp_IRB.CreateSExt(isub, Int32Ty); - PN->addIncoming(sext, cur_cmp_bb); - - if (i < unrollLen - 1) { - - if (cur_lenchk_bb) { - - next_lenchk_bb = - BasicBlock::Create(C, "len_check", end_bb->getParent(), end_bb); - BranchInst::Create(end_bb, next_lenchk_bb); - - } - - next_cmp_bb = - BasicBlock::Create(C, "cmp_added", end_bb->getParent(), end_bb); - BranchInst::Create(end_bb, next_cmp_bb); - - Value *icmp = - cur_cmp_IRB.CreateICmpEQ(isub, ConstantInt::get(Int8Ty, 0)); - cur_cmp_IRB.CreateCondBr( - icmp, next_lenchk_bb ? next_lenchk_bb : next_cmp_bb, end_bb); - cur_cmp_bb->getTerminator()->eraseFromParent(); - - } else { - - // IRB.CreateBr(end_bb); - - } - - // add offset to varstr - // create load - // create signed isub - // create icmp - // create jcc - // create next_bb - - } - - /* since the call is the first instruction of the bb it is safe to - * replace it with a phi instruction */ - BasicBlock::iterator ii(callInst); - ReplaceInstWithInst(callInst->getParent()->getInstList(), ii, PN); - - } - - return true; - -} - -bool CompareTransform::runOnModule(Module &M) { - - if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) - llvm::errs() << "Running compare-transform-pass by laf.intel@gmail.com, " - "extended by heiko@hexco.de\n"; - else - be_quiet = 1; - transformCmps(M, true, true, true, true, true); - verifyModule(M); - - return true; - -} - -static void registerCompTransPass(const PassManagerBuilder &, - legacy::PassManagerBase &PM) { - - auto p = new CompareTransform(); - PM.add(p); - -} - -static RegisterStandardPasses RegisterCompTransPass( - PassManagerBuilder::EP_OptimizerLast, registerCompTransPass); - -static RegisterStandardPasses RegisterCompTransPass0( - PassManagerBuilder::EP_EnabledOnOptLevel0, registerCompTransPass); - -#if LLVM_VERSION_MAJOR >= 11 -static RegisterStandardPasses RegisterCompTransPassLTO( - PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerCompTransPass); -#endif - diff --git a/llvm_mode/llvm-ngram-coverage.h b/llvm_mode/llvm-ngram-coverage.h deleted file mode 100644 index 12b666e9..00000000 --- a/llvm_mode/llvm-ngram-coverage.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef AFL_NGRAM_CONFIG_H -#define AFL_NGRAM_CONFIG_H - -#include "../config.h" - -#if (MAP_SIZE_POW2 <= 16) -typedef u16 PREV_LOC_T; -#elif (MAP_SIZE_POW2 <= 32) -typedef u32 PREV_LOC_T; -#else -typedef u64 PREV_LOC_T; -#endif - -/* Maximum ngram size */ -#define NGRAM_SIZE_MAX 16U - -#endif - diff --git a/llvm_mode/split-compares-pass.so.cc b/llvm_mode/split-compares-pass.so.cc deleted file mode 100644 index 2fb90e5e..00000000 --- a/llvm_mode/split-compares-pass.so.cc +++ /dev/null @@ -1,1356 +0,0 @@ -/* - * Copyright 2016 laf-intel - * extended for floating point by Heiko Eißfeldt - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include -#include -#include -#include - -#include "llvm/Config/llvm-config.h" - -#include "llvm/Pass.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/IR/Module.h" - -#include "llvm/IR/IRBuilder.h" -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) - #include "llvm/IR/Verifier.h" - #include "llvm/IR/DebugInfo.h" -#else - #include "llvm/Analysis/Verifier.h" - #include "llvm/DebugInfo.h" - #define nullptr 0 -#endif - -using namespace llvm; -#include "afl-llvm-common.h" - -namespace { - -class SplitComparesTransform : public ModulePass { - - public: - static char ID; - SplitComparesTransform() : ModulePass(ID) { - - initInstrumentList(); - - } - - bool runOnModule(Module &M) override; -#if LLVM_VERSION_MAJOR >= 4 - StringRef getPassName() const override { - -#else - const char *getPassName() const override { - -#endif - return "simplifies and splits ICMP instructions"; - - } - - private: - int enableFPSplit; - - size_t splitIntCompares(Module &M, unsigned bitw); - size_t splitFPCompares(Module &M); - bool simplifyCompares(Module &M); - bool simplifyFPCompares(Module &M); - bool simplifyIntSignedness(Module &M); - size_t nextPowerOfTwo(size_t in); - -}; - -} // namespace - -char SplitComparesTransform::ID = 0; - -/* This function splits FCMP instructions with xGE or xLE predicates into two - * FCMP instructions with predicate xGT or xLT and EQ */ -bool SplitComparesTransform::simplifyFPCompares(Module &M) { - - LLVMContext & C = M.getContext(); - std::vector fcomps; - IntegerType * Int1Ty = IntegerType::getInt1Ty(C); - - /* iterate over all functions, bbs and instruction and add - * all integer comparisons with >= and <= predicates to the icomps vector */ - for (auto &F : M) { - - if (!isInInstrumentList(&F)) continue; - - for (auto &BB : F) { - - for (auto &IN : BB) { - - CmpInst *selectcmpInst = nullptr; - - if ((selectcmpInst = dyn_cast(&IN))) { - - if (enableFPSplit && - (selectcmpInst->getPredicate() == CmpInst::FCMP_OGE || - selectcmpInst->getPredicate() == CmpInst::FCMP_UGE || - selectcmpInst->getPredicate() == CmpInst::FCMP_OLE || - selectcmpInst->getPredicate() == CmpInst::FCMP_ULE)) { - - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); - - Type *TyOp0 = op0->getType(); - Type *TyOp1 = op1->getType(); - - /* this is probably not needed but we do it anyway */ - if (TyOp0 != TyOp1) { continue; } - - if (TyOp0->isArrayTy() || TyOp0->isVectorTy()) { continue; } - - fcomps.push_back(selectcmpInst); - - } - - } - - } - - } - - } - - if (!fcomps.size()) { return false; } - - /* transform for floating point */ - for (auto &FcmpInst : fcomps) { - - BasicBlock *bb = FcmpInst->getParent(); - - auto op0 = FcmpInst->getOperand(0); - auto op1 = FcmpInst->getOperand(1); - - /* find out what the new predicate is going to be */ - auto pred = dyn_cast(FcmpInst)->getPredicate(); - CmpInst::Predicate new_pred; - switch (pred) { - - case CmpInst::FCMP_UGE: - new_pred = CmpInst::FCMP_UGT; - break; - case CmpInst::FCMP_OGE: - new_pred = CmpInst::FCMP_OGT; - break; - case CmpInst::FCMP_ULE: - new_pred = CmpInst::FCMP_ULT; - break; - case CmpInst::FCMP_OLE: - new_pred = CmpInst::FCMP_OLT; - break; - default: // keep the compiler happy - continue; - - } - - /* split before the fcmp instruction */ - BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(FcmpInst)); - - /* the old bb now contains a unconditional jump to the new one (end_bb) - * we need to delete it later */ - - /* create the FCMP instruction with new_pred and add it to the old basic - * block bb it is now at the position where the old FcmpInst was */ - Instruction *fcmp_np; - fcmp_np = CmpInst::Create(Instruction::FCmp, new_pred, op0, op1); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - fcmp_np); - - /* create a new basic block which holds the new EQ fcmp */ - Instruction *fcmp_eq; - /* insert middle_bb before end_bb */ - BasicBlock *middle_bb = - BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); - fcmp_eq = CmpInst::Create(Instruction::FCmp, CmpInst::FCMP_OEQ, op0, op1); - middle_bb->getInstList().push_back(fcmp_eq); - /* add an unconditional branch to the end of middle_bb with destination - * end_bb */ - BranchInst::Create(end_bb, middle_bb); - - /* replace the uncond branch with a conditional one, which depends on the - * new_pred fcmp. True goes to end, false to the middle (injected) bb */ - auto term = bb->getTerminator(); - BranchInst::Create(end_bb, middle_bb, fcmp_np, bb); - term->eraseFromParent(); - - /* replace the old FcmpInst (which is the first inst in end_bb) with a PHI - * inst to wire up the loose ends */ - PHINode *PN = PHINode::Create(Int1Ty, 2, ""); - /* the first result depends on the outcome of fcmp_eq */ - PN->addIncoming(fcmp_eq, middle_bb); - /* if the source was the original bb we know that the fcmp_np yielded true - * hence we can hardcode this value */ - PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); - /* replace the old FcmpInst with our new and shiny PHI inst */ - BasicBlock::iterator ii(FcmpInst); - ReplaceInstWithInst(FcmpInst->getParent()->getInstList(), ii, PN); - - } - - return true; - -} - -/* This function splits ICMP instructions with xGE or xLE predicates into two - * ICMP instructions with predicate xGT or xLT and EQ */ -bool SplitComparesTransform::simplifyCompares(Module &M) { - - LLVMContext & C = M.getContext(); - std::vector icomps; - IntegerType * Int1Ty = IntegerType::getInt1Ty(C); - - /* iterate over all functions, bbs and instruction and add - * all integer comparisons with >= and <= predicates to the icomps vector */ - for (auto &F : M) { - - if (!isInInstrumentList(&F)) continue; - - for (auto &BB : F) { - - for (auto &IN : BB) { - - CmpInst *selectcmpInst = nullptr; - - if ((selectcmpInst = dyn_cast(&IN))) { - - if (selectcmpInst->getPredicate() == CmpInst::ICMP_UGE || - selectcmpInst->getPredicate() == CmpInst::ICMP_SGE || - selectcmpInst->getPredicate() == CmpInst::ICMP_ULE || - selectcmpInst->getPredicate() == CmpInst::ICMP_SLE) { - - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); - - IntegerType *intTyOp0 = dyn_cast(op0->getType()); - IntegerType *intTyOp1 = dyn_cast(op1->getType()); - - /* this is probably not needed but we do it anyway */ - if (!intTyOp0 || !intTyOp1) { continue; } - - icomps.push_back(selectcmpInst); - - } - - } - - } - - } - - } - - if (!icomps.size()) { return false; } - - for (auto &IcmpInst : icomps) { - - BasicBlock *bb = IcmpInst->getParent(); - - auto op0 = IcmpInst->getOperand(0); - auto op1 = IcmpInst->getOperand(1); - - /* find out what the new predicate is going to be */ - auto pred = dyn_cast(IcmpInst)->getPredicate(); - CmpInst::Predicate new_pred; - switch (pred) { - - case CmpInst::ICMP_UGE: - new_pred = CmpInst::ICMP_UGT; - break; - case CmpInst::ICMP_SGE: - new_pred = CmpInst::ICMP_SGT; - break; - case CmpInst::ICMP_ULE: - new_pred = CmpInst::ICMP_ULT; - break; - case CmpInst::ICMP_SLE: - new_pred = CmpInst::ICMP_SLT; - break; - default: // keep the compiler happy - continue; - - } - - /* split before the icmp instruction */ - BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); - - /* the old bb now contains a unconditional jump to the new one (end_bb) - * we need to delete it later */ - - /* create the ICMP instruction with new_pred and add it to the old basic - * block bb it is now at the position where the old IcmpInst was */ - Instruction *icmp_np; - icmp_np = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - icmp_np); - - /* create a new basic block which holds the new EQ icmp */ - Instruction *icmp_eq; - /* insert middle_bb before end_bb */ - BasicBlock *middle_bb = - BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); - icmp_eq = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, op0, op1); - middle_bb->getInstList().push_back(icmp_eq); - /* add an unconditional branch to the end of middle_bb with destination - * end_bb */ - BranchInst::Create(end_bb, middle_bb); - - /* replace the uncond branch with a conditional one, which depends on the - * new_pred icmp. True goes to end, false to the middle (injected) bb */ - auto term = bb->getTerminator(); - BranchInst::Create(end_bb, middle_bb, icmp_np, bb); - term->eraseFromParent(); - - /* replace the old IcmpInst (which is the first inst in end_bb) with a PHI - * inst to wire up the loose ends */ - PHINode *PN = PHINode::Create(Int1Ty, 2, ""); - /* the first result depends on the outcome of icmp_eq */ - PN->addIncoming(icmp_eq, middle_bb); - /* if the source was the original bb we know that the icmp_np yielded true - * hence we can hardcode this value */ - PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); - /* replace the old IcmpInst with our new and shiny PHI inst */ - BasicBlock::iterator ii(IcmpInst); - ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); - - } - - return true; - -} - -/* this function transforms signed compares to equivalent unsigned compares */ -bool SplitComparesTransform::simplifyIntSignedness(Module &M) { - - LLVMContext & C = M.getContext(); - std::vector icomps; - IntegerType * Int1Ty = IntegerType::getInt1Ty(C); - - /* iterate over all functions, bbs and instructions and add - * all signed compares to icomps vector */ - for (auto &F : M) { - - if (!isInInstrumentList(&F)) continue; - - for (auto &BB : F) { - - for (auto &IN : BB) { - - CmpInst *selectcmpInst = nullptr; - - if ((selectcmpInst = dyn_cast(&IN))) { - - if (selectcmpInst->getPredicate() == CmpInst::ICMP_SGT || - selectcmpInst->getPredicate() == CmpInst::ICMP_SLT) { - - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); - - IntegerType *intTyOp0 = dyn_cast(op0->getType()); - IntegerType *intTyOp1 = dyn_cast(op1->getType()); - - /* see above */ - if (!intTyOp0 || !intTyOp1) { continue; } - - /* i think this is not possible but to lazy to look it up */ - if (intTyOp0->getBitWidth() != intTyOp1->getBitWidth()) { - - continue; - - } - - icomps.push_back(selectcmpInst); - - } - - } - - } - - } - - } - - if (!icomps.size()) { return false; } - - for (auto &IcmpInst : icomps) { - - BasicBlock *bb = IcmpInst->getParent(); - - auto op0 = IcmpInst->getOperand(0); - auto op1 = IcmpInst->getOperand(1); - - IntegerType *intTyOp0 = dyn_cast(op0->getType()); - unsigned bitw = intTyOp0->getBitWidth(); - IntegerType *IntType = IntegerType::get(C, bitw); - - /* get the new predicate */ - auto pred = dyn_cast(IcmpInst)->getPredicate(); - CmpInst::Predicate new_pred; - if (pred == CmpInst::ICMP_SGT) { - - new_pred = CmpInst::ICMP_UGT; - - } else { - - new_pred = CmpInst::ICMP_ULT; - - } - - BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); - - /* create a 1 bit compare for the sign bit. to do this shift and trunc - * the original operands so only the first bit remains.*/ - Instruction *s_op0, *t_op0, *s_op1, *t_op1, *icmp_sign_bit; - - s_op0 = BinaryOperator::Create(Instruction::LShr, op0, - ConstantInt::get(IntType, bitw - 1)); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op0); - t_op0 = new TruncInst(s_op0, Int1Ty); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_op0); - - s_op1 = BinaryOperator::Create(Instruction::LShr, op1, - ConstantInt::get(IntType, bitw - 1)); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op1); - t_op1 = new TruncInst(s_op1, Int1Ty); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_op1); - - /* compare of the sign bits */ - icmp_sign_bit = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_op0, t_op1); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - icmp_sign_bit); - - /* create a new basic block which is executed if the signedness bit is - * different */ - Instruction *icmp_inv_sig_cmp; - BasicBlock * sign_bb = - BasicBlock::Create(C, "sign", end_bb->getParent(), end_bb); - if (pred == CmpInst::ICMP_SGT) { - - /* if we check for > and the op0 positive and op1 negative then the final - * result is true. if op0 negative and op1 pos, the cmp must result - * in false - */ - icmp_inv_sig_cmp = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_op0, t_op1); - - } else { - - /* just the inverse of the above statement */ - icmp_inv_sig_cmp = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_op0, t_op1); - - } - - sign_bb->getInstList().push_back(icmp_inv_sig_cmp); - BranchInst::Create(end_bb, sign_bb); - - /* create a new bb which is executed if signedness is equal */ - Instruction *icmp_usign_cmp; - BasicBlock * middle_bb = - BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); - /* we can do a normal unsigned compare now */ - icmp_usign_cmp = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); - middle_bb->getInstList().push_back(icmp_usign_cmp); - BranchInst::Create(end_bb, middle_bb); - - auto term = bb->getTerminator(); - /* if the sign is eq do a normal unsigned cmp, else we have to check the - * signedness bit */ - BranchInst::Create(middle_bb, sign_bb, icmp_sign_bit, bb); - term->eraseFromParent(); - - PHINode *PN = PHINode::Create(Int1Ty, 2, ""); - - PN->addIncoming(icmp_usign_cmp, middle_bb); - PN->addIncoming(icmp_inv_sig_cmp, sign_bb); - - BasicBlock::iterator ii(IcmpInst); - ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); - - } - - return true; - -} - -size_t SplitComparesTransform::nextPowerOfTwo(size_t in) { - - --in; - in |= in >> 1; - in |= in >> 2; - in |= in >> 4; - // in |= in >> 8; - // in |= in >> 16; - return in + 1; - -} - -/* splits fcmps into two nested fcmps with sign compare and the rest */ -size_t SplitComparesTransform::splitFPCompares(Module &M) { - - size_t count = 0; - - LLVMContext &C = M.getContext(); - -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) - const DataLayout &dl = M.getDataLayout(); - - /* define unions with floating point and (sign, exponent, mantissa) triples - */ - if (dl.isLittleEndian()) { - - } else if (dl.isBigEndian()) { - - } else { - - return count; - - } - -#endif - - std::vector fcomps; - - /* get all EQ, NE, GT, and LT fcmps. if the other two - * functions were executed only these four predicates should exist */ - for (auto &F : M) { - - if (!isInInstrumentList(&F)) continue; - - for (auto &BB : F) { - - for (auto &IN : BB) { - - CmpInst *selectcmpInst = nullptr; - - if ((selectcmpInst = dyn_cast(&IN))) { - - if (selectcmpInst->getPredicate() == CmpInst::FCMP_OEQ || - selectcmpInst->getPredicate() == CmpInst::FCMP_ONE || - selectcmpInst->getPredicate() == CmpInst::FCMP_UNE || - selectcmpInst->getPredicate() == CmpInst::FCMP_UGT || - selectcmpInst->getPredicate() == CmpInst::FCMP_OGT || - selectcmpInst->getPredicate() == CmpInst::FCMP_ULT || - selectcmpInst->getPredicate() == CmpInst::FCMP_OLT) { - - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); - - Type *TyOp0 = op0->getType(); - Type *TyOp1 = op1->getType(); - - if (TyOp0 != TyOp1) { continue; } - - if (TyOp0->isArrayTy() || TyOp0->isVectorTy()) { continue; } - - fcomps.push_back(selectcmpInst); - - } - - } - - } - - } - - } - - if (!fcomps.size()) { return count; } - - IntegerType *Int1Ty = IntegerType::getInt1Ty(C); - - for (auto &FcmpInst : fcomps) { - - BasicBlock *bb = FcmpInst->getParent(); - - auto op0 = FcmpInst->getOperand(0); - auto op1 = FcmpInst->getOperand(1); - - unsigned op_size; - op_size = op0->getType()->getPrimitiveSizeInBits(); - - if (op_size != op1->getType()->getPrimitiveSizeInBits()) { continue; } - - const unsigned int sizeInBits = op0->getType()->getPrimitiveSizeInBits(); - const unsigned int precision = - sizeInBits == 32 - ? 24 - : sizeInBits == 64 - ? 53 - : sizeInBits == 128 ? 113 - : sizeInBits == 16 ? 11 - /* sizeInBits == 80 */ - : 65; - - const unsigned shiftR_exponent = precision - 1; - const unsigned long long mask_fraction = - (1ULL << (shiftR_exponent - 1)) | ((1ULL << (shiftR_exponent - 1)) - 1); - const unsigned long long mask_exponent = - (1ULL << (sizeInBits - precision)) - 1; - - // round up sizes to the next power of two - // this should help with integer compare splitting - size_t exTySizeBytes = ((sizeInBits - precision + 7) >> 3); - size_t frTySizeBytes = ((precision - 1ULL + 7) >> 3); - - IntegerType *IntExponentTy = - IntegerType::get(C, nextPowerOfTwo(exTySizeBytes) << 3); - IntegerType *IntFractionTy = - IntegerType::get(C, nextPowerOfTwo(frTySizeBytes) << 3); - - // errs() << "Fractions: IntFractionTy size " << - // IntFractionTy->getPrimitiveSizeInBits() << ", op_size " << op_size << - // ", mask " << mask_fraction << - // ", precision " << precision << "\n"; - - BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(FcmpInst)); - - /* create the integers from floats directly */ - Instruction *b_op0, *b_op1; - b_op0 = CastInst::Create(Instruction::BitCast, op0, - IntegerType::get(C, op_size)); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), b_op0); - - b_op1 = CastInst::Create(Instruction::BitCast, op1, - IntegerType::get(C, op_size)); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), b_op1); - - /* isolate signs of value of floating point type */ - - /* create a 1 bit compare for the sign bit. to do this shift and trunc - * the original operands so only the first bit remains.*/ - Instruction *s_s0, *t_s0, *s_s1, *t_s1, *icmp_sign_bit; - - s_s0 = - BinaryOperator::Create(Instruction::LShr, b_op0, - ConstantInt::get(b_op0->getType(), op_size - 1)); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_s0); - t_s0 = new TruncInst(s_s0, Int1Ty); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_s0); - - s_s1 = - BinaryOperator::Create(Instruction::LShr, b_op1, - ConstantInt::get(b_op1->getType(), op_size - 1)); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_s1); - t_s1 = new TruncInst(s_s1, Int1Ty); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), t_s1); - - /* compare of the sign bits */ - icmp_sign_bit = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_s0, t_s1); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - icmp_sign_bit); - - /* create a new basic block which is executed if the signedness bits are - * equal */ - BasicBlock *signequal_bb = - BasicBlock::Create(C, "signequal", end_bb->getParent(), end_bb); - - BranchInst::Create(end_bb, signequal_bb); - - /* create a new bb which is executed if exponents are satisfying the compare - */ - BasicBlock *middle_bb = - BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); - - BranchInst::Create(end_bb, middle_bb); - - auto term = bb->getTerminator(); - /* if the signs are different goto end_bb else to signequal_bb */ - BranchInst::Create(signequal_bb, end_bb, icmp_sign_bit, bb); - term->eraseFromParent(); - - /* insert code for equal signs */ - - /* isolate the exponents */ - Instruction *s_e0, *m_e0, *t_e0, *s_e1, *m_e1, *t_e1; - - s_e0 = BinaryOperator::Create( - Instruction::LShr, b_op0, - ConstantInt::get(b_op0->getType(), shiftR_exponent)); - s_e1 = BinaryOperator::Create( - Instruction::LShr, b_op1, - ConstantInt::get(b_op1->getType(), shiftR_exponent)); - signequal_bb->getInstList().insert( - BasicBlock::iterator(signequal_bb->getTerminator()), s_e0); - signequal_bb->getInstList().insert( - BasicBlock::iterator(signequal_bb->getTerminator()), s_e1); - - t_e0 = new TruncInst(s_e0, IntExponentTy); - t_e1 = new TruncInst(s_e1, IntExponentTy); - signequal_bb->getInstList().insert( - BasicBlock::iterator(signequal_bb->getTerminator()), t_e0); - signequal_bb->getInstList().insert( - BasicBlock::iterator(signequal_bb->getTerminator()), t_e1); - - if (sizeInBits - precision < exTySizeBytes * 8) { - - m_e0 = BinaryOperator::Create( - Instruction::And, t_e0, - ConstantInt::get(t_e0->getType(), mask_exponent)); - m_e1 = BinaryOperator::Create( - Instruction::And, t_e1, - ConstantInt::get(t_e1->getType(), mask_exponent)); - signequal_bb->getInstList().insert( - BasicBlock::iterator(signequal_bb->getTerminator()), m_e0); - signequal_bb->getInstList().insert( - BasicBlock::iterator(signequal_bb->getTerminator()), m_e1); - - } else { - - m_e0 = t_e0; - m_e1 = t_e1; - - } - - /* compare the exponents of the operands */ - Instruction *icmp_exponents_equal; - Instruction *icmp_exponent_result; - BasicBlock * signequal2_bb = signequal_bb; - switch (FcmpInst->getPredicate()) { - - case CmpInst::FCMP_OEQ: - icmp_exponent_result = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, m_e0, m_e1); - break; - case CmpInst::FCMP_ONE: - case CmpInst::FCMP_UNE: - icmp_exponent_result = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_NE, m_e0, m_e1); - break; - /* compare the exponents of the operands (signs are equal) - * if exponents are equal -> proceed to mantissa comparison - * else get result depending on sign - */ - case CmpInst::FCMP_OGT: - case CmpInst::FCMP_UGT: - Instruction *icmp_exponent; - icmp_exponents_equal = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, m_e0, m_e1); - signequal_bb->getInstList().insert( - BasicBlock::iterator(signequal_bb->getTerminator()), - icmp_exponents_equal); - - // shortcut for unequal exponents - signequal2_bb = signequal_bb->splitBasicBlock( - BasicBlock::iterator(signequal_bb->getTerminator())); - - /* if the exponents are equal goto middle_bb else to signequal2_bb */ - term = signequal_bb->getTerminator(); - BranchInst::Create(middle_bb, signequal2_bb, icmp_exponents_equal, - signequal_bb); - term->eraseFromParent(); - - icmp_exponent = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, m_e0, m_e1); - signequal2_bb->getInstList().insert( - BasicBlock::iterator(signequal2_bb->getTerminator()), - icmp_exponent); - icmp_exponent_result = - BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0); - break; - case CmpInst::FCMP_OLT: - case CmpInst::FCMP_ULT: - icmp_exponents_equal = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, m_e0, m_e1); - signequal_bb->getInstList().insert( - BasicBlock::iterator(signequal_bb->getTerminator()), - icmp_exponents_equal); - - // shortcut for unequal exponents - signequal2_bb = signequal_bb->splitBasicBlock( - BasicBlock::iterator(signequal_bb->getTerminator())); - - /* if the exponents are equal goto middle_bb else to signequal2_bb */ - term = signequal_bb->getTerminator(); - BranchInst::Create(middle_bb, signequal2_bb, icmp_exponents_equal, - signequal_bb); - term->eraseFromParent(); - - icmp_exponent = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, m_e0, m_e1); - signequal2_bb->getInstList().insert( - BasicBlock::iterator(signequal2_bb->getTerminator()), - icmp_exponent); - icmp_exponent_result = - BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0); - break; - default: - continue; - - } - - signequal2_bb->getInstList().insert( - BasicBlock::iterator(signequal2_bb->getTerminator()), - icmp_exponent_result); - - { - - term = signequal2_bb->getTerminator(); - - switch (FcmpInst->getPredicate()) { - - case CmpInst::FCMP_OEQ: - /* if the exponents are satifying the compare do a fraction cmp in - * middle_bb */ - BranchInst::Create(middle_bb, end_bb, icmp_exponent_result, - signequal2_bb); - break; - case CmpInst::FCMP_ONE: - case CmpInst::FCMP_UNE: - /* if the exponents are satifying the compare do a fraction cmp in - * middle_bb */ - BranchInst::Create(end_bb, middle_bb, icmp_exponent_result, - signequal2_bb); - break; - case CmpInst::FCMP_OGT: - case CmpInst::FCMP_UGT: - case CmpInst::FCMP_OLT: - case CmpInst::FCMP_ULT: - BranchInst::Create(end_bb, signequal2_bb); - break; - default: - continue; - - } - - term->eraseFromParent(); - - } - - /* isolate the mantissa aka fraction */ - Instruction *t_f0, *t_f1; - bool needTrunc = IntFractionTy->getPrimitiveSizeInBits() < op_size; - - if (precision - 1 < frTySizeBytes * 8) { - - Instruction *m_f0, *m_f1; - m_f0 = BinaryOperator::Create( - Instruction::And, b_op0, - ConstantInt::get(b_op0->getType(), mask_fraction)); - m_f1 = BinaryOperator::Create( - Instruction::And, b_op1, - ConstantInt::get(b_op1->getType(), mask_fraction)); - middle_bb->getInstList().insert( - BasicBlock::iterator(middle_bb->getTerminator()), m_f0); - middle_bb->getInstList().insert( - BasicBlock::iterator(middle_bb->getTerminator()), m_f1); - - if (needTrunc) { - - t_f0 = new TruncInst(m_f0, IntFractionTy); - t_f1 = new TruncInst(m_f1, IntFractionTy); - middle_bb->getInstList().insert( - BasicBlock::iterator(middle_bb->getTerminator()), t_f0); - middle_bb->getInstList().insert( - BasicBlock::iterator(middle_bb->getTerminator()), t_f1); - - } else { - - t_f0 = m_f0; - t_f1 = m_f1; - - } - - } else { - - if (needTrunc) { - - t_f0 = new TruncInst(b_op0, IntFractionTy); - t_f1 = new TruncInst(b_op1, IntFractionTy); - middle_bb->getInstList().insert( - BasicBlock::iterator(middle_bb->getTerminator()), t_f0); - middle_bb->getInstList().insert( - BasicBlock::iterator(middle_bb->getTerminator()), t_f1); - - } else { - - t_f0 = b_op0; - t_f1 = b_op1; - - } - - } - - /* compare the fractions of the operands */ - Instruction *icmp_fraction_result; - Instruction *icmp_fraction_result2; - BasicBlock * middle2_bb = middle_bb; - PHINode * PN2 = nullptr; - switch (FcmpInst->getPredicate()) { - - case CmpInst::FCMP_OEQ: - icmp_fraction_result = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_f0, t_f1); - middle2_bb->getInstList().insert( - BasicBlock::iterator(middle2_bb->getTerminator()), - icmp_fraction_result); - - break; - case CmpInst::FCMP_UNE: - case CmpInst::FCMP_ONE: - icmp_fraction_result = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_NE, t_f0, t_f1); - middle2_bb->getInstList().insert( - BasicBlock::iterator(middle2_bb->getTerminator()), - icmp_fraction_result); - - break; - case CmpInst::FCMP_OGT: - case CmpInst::FCMP_UGT: - case CmpInst::FCMP_OLT: - case CmpInst::FCMP_ULT: { - - middle2_bb = middle_bb->splitBasicBlock( - BasicBlock::iterator(middle_bb->getTerminator())); - - BasicBlock *negative_bb = BasicBlock::Create( - C, "negative_value", middle2_bb->getParent(), middle2_bb); - BasicBlock *positive_bb = BasicBlock::Create( - C, "positive_value", negative_bb->getParent(), negative_bb); - - if (FcmpInst->getPredicate() == CmpInst::FCMP_OGT || - FcmpInst->getPredicate() == CmpInst::FCMP_UGT) { - - negative_bb->getInstList().push_back( - icmp_fraction_result = CmpInst::Create( - Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1)); - positive_bb->getInstList().push_back( - icmp_fraction_result2 = CmpInst::Create( - Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1)); - - } else { - - negative_bb->getInstList().push_back( - icmp_fraction_result = CmpInst::Create( - Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1)); - positive_bb->getInstList().push_back( - icmp_fraction_result2 = CmpInst::Create( - Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1)); - - } - - BranchInst::Create(middle2_bb, negative_bb); - BranchInst::Create(middle2_bb, positive_bb); - - term = middle_bb->getTerminator(); - BranchInst::Create(negative_bb, positive_bb, t_s0, middle_bb); - term->eraseFromParent(); - - PN2 = PHINode::Create(Int1Ty, 2, ""); - PN2->addIncoming(icmp_fraction_result, negative_bb); - PN2->addIncoming(icmp_fraction_result2, positive_bb); - middle2_bb->getInstList().insert( - BasicBlock::iterator(middle2_bb->getTerminator()), PN2); - - } break; - - default: - continue; - - } - - PHINode *PN = PHINode::Create(Int1Ty, 3, ""); - - switch (FcmpInst->getPredicate()) { - - case CmpInst::FCMP_OEQ: - /* unequal signs cannot be equal values */ - /* goto false branch */ - PN->addIncoming(ConstantInt::get(Int1Ty, 0), bb); - /* unequal exponents cannot be equal values, too */ - PN->addIncoming(ConstantInt::get(Int1Ty, 0), signequal_bb); - /* fractions comparison */ - PN->addIncoming(icmp_fraction_result, middle2_bb); - break; - case CmpInst::FCMP_ONE: - case CmpInst::FCMP_UNE: - /* unequal signs are unequal values */ - /* goto true branch */ - PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); - /* unequal exponents are unequal values, too */ - PN->addIncoming(icmp_exponent_result, signequal_bb); - /* fractions comparison */ - PN->addIncoming(icmp_fraction_result, middle2_bb); - break; - case CmpInst::FCMP_OGT: - case CmpInst::FCMP_UGT: - /* if op1 is negative goto true branch, - else go on comparing */ - PN->addIncoming(t_s1, bb); - PN->addIncoming(icmp_exponent_result, signequal2_bb); - PN->addIncoming(PN2, middle2_bb); - break; - case CmpInst::FCMP_OLT: - case CmpInst::FCMP_ULT: - /* if op0 is negative goto true branch, - else go on comparing */ - PN->addIncoming(t_s0, bb); - PN->addIncoming(icmp_exponent_result, signequal2_bb); - PN->addIncoming(PN2, middle2_bb); - break; - default: - continue; - - } - - BasicBlock::iterator ii(FcmpInst); - ReplaceInstWithInst(FcmpInst->getParent()->getInstList(), ii, PN); - ++count; - - } - - return count; - -} - -/* splits icmps of size bitw into two nested icmps with bitw/2 size each */ -size_t SplitComparesTransform::splitIntCompares(Module &M, unsigned bitw) { - - size_t count = 0; - - LLVMContext &C = M.getContext(); - - IntegerType *Int1Ty = IntegerType::getInt1Ty(C); - IntegerType *OldIntType = IntegerType::get(C, bitw); - IntegerType *NewIntType = IntegerType::get(C, bitw / 2); - - std::vector icomps; - - if (bitw % 2) { return 0; } - - /* not supported yet */ - if (bitw > 64) { return 0; } - - /* get all EQ, NE, UGT, and ULT icmps of width bitw. if the - * functions simplifyCompares() and simplifyIntSignedness() - * were executed only these four predicates should exist */ - for (auto &F : M) { - - if (!isInInstrumentList(&F)) continue; - - for (auto &BB : F) { - - for (auto &IN : BB) { - - CmpInst *selectcmpInst = nullptr; - - if ((selectcmpInst = dyn_cast(&IN))) { - - if (selectcmpInst->getPredicate() == CmpInst::ICMP_EQ || - selectcmpInst->getPredicate() == CmpInst::ICMP_NE || - selectcmpInst->getPredicate() == CmpInst::ICMP_UGT || - selectcmpInst->getPredicate() == CmpInst::ICMP_ULT) { - - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); - - IntegerType *intTyOp0 = dyn_cast(op0->getType()); - IntegerType *intTyOp1 = dyn_cast(op1->getType()); - - if (!intTyOp0 || !intTyOp1) { continue; } - - /* check if the bitwidths are the one we are looking for */ - if (intTyOp0->getBitWidth() != bitw || - intTyOp1->getBitWidth() != bitw) { - - continue; - - } - - icomps.push_back(selectcmpInst); - - } - - } - - } - - } - - } - - if (!icomps.size()) { return 0; } - - for (auto &IcmpInst : icomps) { - - BasicBlock *bb = IcmpInst->getParent(); - - auto op0 = IcmpInst->getOperand(0); - auto op1 = IcmpInst->getOperand(1); - - auto pred = dyn_cast(IcmpInst)->getPredicate(); - - BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); - - /* create the comparison of the top halves of the original operands */ - Instruction *s_op0, *op0_high, *s_op1, *op1_high, *icmp_high; - - s_op0 = BinaryOperator::Create(Instruction::LShr, op0, - ConstantInt::get(OldIntType, bitw / 2)); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op0); - op0_high = new TruncInst(s_op0, NewIntType); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - op0_high); - - s_op1 = BinaryOperator::Create(Instruction::LShr, op1, - ConstantInt::get(OldIntType, bitw / 2)); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), s_op1); - op1_high = new TruncInst(s_op1, NewIntType); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - op1_high); - - icmp_high = CmpInst::Create(Instruction::ICmp, pred, op0_high, op1_high); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - icmp_high); - - /* now we have to destinguish between == != and > < */ - if (pred == CmpInst::ICMP_EQ || pred == CmpInst::ICMP_NE) { - - /* transformation for == and != icmps */ - - /* create a compare for the lower half of the original operands */ - Instruction *op0_low, *op1_low, *icmp_low; - BasicBlock * cmp_low_bb = - BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); - - op0_low = new TruncInst(op0, NewIntType); - cmp_low_bb->getInstList().push_back(op0_low); - - op1_low = new TruncInst(op1, NewIntType); - cmp_low_bb->getInstList().push_back(op1_low); - - icmp_low = CmpInst::Create(Instruction::ICmp, pred, op0_low, op1_low); - cmp_low_bb->getInstList().push_back(icmp_low); - BranchInst::Create(end_bb, cmp_low_bb); - - /* dependent on the cmp of the high parts go to the end or go on with - * the comparison */ - auto term = bb->getTerminator(); - if (pred == CmpInst::ICMP_EQ) { - - BranchInst::Create(cmp_low_bb, end_bb, icmp_high, bb); - - } else { - - /* CmpInst::ICMP_NE */ - BranchInst::Create(end_bb, cmp_low_bb, icmp_high, bb); - - } - - term->eraseFromParent(); - - /* create the PHI and connect the edges accordingly */ - PHINode *PN = PHINode::Create(Int1Ty, 2, ""); - PN->addIncoming(icmp_low, cmp_low_bb); - if (pred == CmpInst::ICMP_EQ) { - - PN->addIncoming(ConstantInt::get(Int1Ty, 0), bb); - - } else { - - /* CmpInst::ICMP_NE */ - PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); - - } - - /* replace the old icmp with the new PHI */ - BasicBlock::iterator ii(IcmpInst); - ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); - - } else { - - /* CmpInst::ICMP_UGT and CmpInst::ICMP_ULT */ - /* transformations for < and > */ - - /* create a basic block which checks for the inverse predicate. - * if this is true we can go to the end if not we have to go to the - * bb which checks the lower half of the operands */ - Instruction *icmp_inv_cmp, *op0_low, *op1_low, *icmp_low; - BasicBlock * inv_cmp_bb = - BasicBlock::Create(C, "inv_cmp", end_bb->getParent(), end_bb); - if (pred == CmpInst::ICMP_UGT) { - - icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, - op0_high, op1_high); - - } else { - - icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, - op0_high, op1_high); - - } - - inv_cmp_bb->getInstList().push_back(icmp_inv_cmp); - - auto term = bb->getTerminator(); - term->eraseFromParent(); - BranchInst::Create(end_bb, inv_cmp_bb, icmp_high, bb); - - /* create a bb which handles the cmp of the lower halves */ - BasicBlock *cmp_low_bb = - BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); - op0_low = new TruncInst(op0, NewIntType); - cmp_low_bb->getInstList().push_back(op0_low); - op1_low = new TruncInst(op1, NewIntType); - cmp_low_bb->getInstList().push_back(op1_low); - - icmp_low = CmpInst::Create(Instruction::ICmp, pred, op0_low, op1_low); - cmp_low_bb->getInstList().push_back(icmp_low); - BranchInst::Create(end_bb, cmp_low_bb); - - BranchInst::Create(end_bb, cmp_low_bb, icmp_inv_cmp, inv_cmp_bb); - - PHINode *PN = PHINode::Create(Int1Ty, 3); - PN->addIncoming(icmp_low, cmp_low_bb); - PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); - PN->addIncoming(ConstantInt::get(Int1Ty, 0), inv_cmp_bb); - - BasicBlock::iterator ii(IcmpInst); - ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); - - } - - ++count; - - } - - return count; - -} - -bool SplitComparesTransform::runOnModule(Module &M) { - - int bitw = 64; - size_t count; - - char *bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW"); - if (!bitw_env) bitw_env = getenv("LAF_SPLIT_COMPARES_BITW"); - if (bitw_env) { bitw = atoi(bitw_env); } - - enableFPSplit = getenv("AFL_LLVM_LAF_SPLIT_FLOATS") != NULL; - - if ((isatty(2) && getenv("AFL_QUIET") == NULL) || - getenv("AFL_DEBUG") != NULL) { - - errs() << "Split-compare-pass by laf.intel@gmail.com, extended by " - "heiko@hexco.de\n"; - - } else { - - be_quiet = 1; - - } - - if (enableFPSplit) { - - count = splitFPCompares(M); - - if (!be_quiet) { - - errs() << "Split-floatingpoint-compare-pass: " << count - << " FP comparisons split\n"; - - } - - simplifyFPCompares(M); - - } - - simplifyCompares(M); - - simplifyIntSignedness(M); - - switch (bitw) { - - case 64: - count = splitIntCompares(M, bitw); - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " split\n"; - - bitw >>= 1; -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) - [[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */ -#endif - case 32: - count = splitIntCompares(M, bitw); - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " split\n"; - - bitw >>= 1; -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) - [[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */ -#endif - case 16: - count = splitIntCompares(M, bitw); - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " split\n"; - - bitw >>= 1; - break; - - default: - if (!be_quiet) errs() << "NOT Running split-compare-pass \n"; - return false; - break; - - } - - verifyModule(M); - return true; - -} - -static void registerSplitComparesPass(const PassManagerBuilder &, - legacy::PassManagerBase &PM) { - - PM.add(new SplitComparesTransform()); - -} - -static RegisterStandardPasses RegisterSplitComparesPass( - PassManagerBuilder::EP_OptimizerLast, registerSplitComparesPass); - -static RegisterStandardPasses RegisterSplitComparesTransPass0( - PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitComparesPass); - -#if LLVM_VERSION_MAJOR >= 11 -static RegisterStandardPasses RegisterSplitComparesTransPassLTO( - PassManagerBuilder::EP_FullLinkTimeOptimizationLast, - registerSplitComparesPass); -#endif - diff --git a/llvm_mode/split-switches-pass.so.cc b/llvm_mode/split-switches-pass.so.cc deleted file mode 100644 index a79d4114..00000000 --- a/llvm_mode/split-switches-pass.so.cc +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright 2016 laf-intel - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include -#include -#include -#include - -#include "llvm/Config/llvm-config.h" - -#include "llvm/ADT/Statistic.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Pass.h" -#include "llvm/Analysis/ValueTracking.h" - -#include "llvm/IR/IRBuilder.h" -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) - #include "llvm/IR/Verifier.h" - #include "llvm/IR/DebugInfo.h" -#else - #include "llvm/Analysis/Verifier.h" - #include "llvm/DebugInfo.h" - #define nullptr 0 -#endif - -#include -#include "afl-llvm-common.h" - -using namespace llvm; - -namespace { - -class SplitSwitchesTransform : public ModulePass { - - public: - static char ID; - SplitSwitchesTransform() : ModulePass(ID) { - - initInstrumentList(); - - } - - bool runOnModule(Module &M) override; - -#if LLVM_VERSION_MAJOR >= 4 - StringRef getPassName() const override { - -#else - const char *getPassName() const override { - -#endif - return "splits switch constructs"; - - } - - struct CaseExpr { - - ConstantInt *Val; - BasicBlock * BB; - - CaseExpr(ConstantInt *val = nullptr, BasicBlock *bb = nullptr) - : Val(val), BB(bb) { - - } - - }; - - typedef std::vector CaseVector; - - private: - bool splitSwitches(Module &M); - bool transformCmps(Module &M, const bool processStrcmp, - const bool processMemcmp); - BasicBlock *switchConvert(CaseVector Cases, std::vector bytesChecked, - BasicBlock *OrigBlock, BasicBlock *NewDefault, - Value *Val, unsigned level); - -}; - -} // namespace - -char SplitSwitchesTransform::ID = 0; - -/* switchConvert - Transform simple list of Cases into list of CaseRange's */ -BasicBlock *SplitSwitchesTransform::switchConvert( - CaseVector Cases, std::vector bytesChecked, BasicBlock *OrigBlock, - BasicBlock *NewDefault, Value *Val, unsigned level) { - - unsigned ValTypeBitWidth = Cases[0].Val->getBitWidth(); - IntegerType *ValType = - IntegerType::get(OrigBlock->getContext(), ValTypeBitWidth); - IntegerType * ByteType = IntegerType::get(OrigBlock->getContext(), 8); - unsigned BytesInValue = bytesChecked.size(); - std::vector setSizes; - std::vector > byteSets(BytesInValue, std::set()); - - assert(ValTypeBitWidth >= 8 && ValTypeBitWidth <= 64); - - /* for each of the possible cases we iterate over all bytes of the values - * build a set of possible values at each byte position in byteSets */ - for (CaseExpr &Case : Cases) { - - for (unsigned i = 0; i < BytesInValue; i++) { - - uint8_t byte = (Case.Val->getZExtValue() >> (i * 8)) & 0xFF; - byteSets[i].insert(byte); - - } - - } - - /* find the index of the first byte position that was not yet checked. then - * save the number of possible values at that byte position */ - unsigned smallestIndex = 0; - unsigned smallestSize = 257; - for (unsigned i = 0; i < byteSets.size(); i++) { - - if (bytesChecked[i]) continue; - if (byteSets[i].size() < smallestSize) { - - smallestIndex = i; - smallestSize = byteSets[i].size(); - - } - - } - - assert(bytesChecked[smallestIndex] == false); - - /* there are only smallestSize different bytes at index smallestIndex */ - - Instruction *Shift, *Trunc; - Function * F = OrigBlock->getParent(); - BasicBlock * NewNode = BasicBlock::Create(Val->getContext(), "NodeBlock", F); - Shift = BinaryOperator::Create(Instruction::LShr, Val, - ConstantInt::get(ValType, smallestIndex * 8)); - NewNode->getInstList().push_back(Shift); - - if (ValTypeBitWidth > 8) { - - Trunc = new TruncInst(Shift, ByteType); - NewNode->getInstList().push_back(Trunc); - - } else { - - /* not necessary to trunc */ - Trunc = Shift; - - } - - /* this is a trivial case, we can directly check for the byte, - * if the byte is not found go to default. if the byte was found - * mark the byte as checked. if this was the last byte to check - * we can finally execute the block belonging to this case */ - - if (smallestSize == 1) { - - uint8_t byte = *(byteSets[smallestIndex].begin()); - - /* insert instructions to check whether the value we are switching on is - * equal to byte */ - ICmpInst *Comp = - new ICmpInst(ICmpInst::ICMP_EQ, Trunc, ConstantInt::get(ByteType, byte), - "byteMatch"); - NewNode->getInstList().push_back(Comp); - - bytesChecked[smallestIndex] = true; - bool allBytesAreChecked = true; - - for (std::vector::iterator BCI = bytesChecked.begin(), - E = bytesChecked.end(); - BCI != E; ++BCI) { - - if (!*BCI) { - - allBytesAreChecked = false; - break; - - } - - } - - // if (std::all_of(bytesChecked.begin(), bytesChecked.end(), - // [](bool b) { return b; })) { - - if (allBytesAreChecked) { - - assert(Cases.size() == 1); - BranchInst::Create(Cases[0].BB, NewDefault, Comp, NewNode); - - /* we have to update the phi nodes! */ - for (BasicBlock::iterator I = Cases[0].BB->begin(); - I != Cases[0].BB->end(); ++I) { - - if (!isa(&*I)) { continue; } - PHINode *PN = cast(I); - - /* Only update the first occurrence. */ - unsigned Idx = 0, E = PN->getNumIncomingValues(); - for (; Idx != E; ++Idx) { - - if (PN->getIncomingBlock(Idx) == OrigBlock) { - - PN->setIncomingBlock(Idx, NewNode); - break; - - } - - } - - } - - } else { - - BasicBlock *BB = switchConvert(Cases, bytesChecked, OrigBlock, NewDefault, - Val, level + 1); - BranchInst::Create(BB, NewDefault, Comp, NewNode); - - } - - } - - /* there is no byte which we can directly check on, split the tree */ - else { - - std::vector byteVector; - std::copy(byteSets[smallestIndex].begin(), byteSets[smallestIndex].end(), - std::back_inserter(byteVector)); - std::sort(byteVector.begin(), byteVector.end()); - uint8_t pivot = byteVector[byteVector.size() / 2]; - - /* we already chose to divide the cases based on the value of byte at index - * smallestIndex the pivot value determines the threshold for the decicion; - * if a case value - * is smaller at this byte index move it to the LHS vector, otherwise to the - * RHS vector */ - - CaseVector LHSCases, RHSCases; - - for (CaseExpr &Case : Cases) { - - uint8_t byte = (Case.Val->getZExtValue() >> (smallestIndex * 8)) & 0xFF; - - if (byte < pivot) { - - LHSCases.push_back(Case); - - } else { - - RHSCases.push_back(Case); - - } - - } - - BasicBlock *LBB, *RBB; - LBB = switchConvert(LHSCases, bytesChecked, OrigBlock, NewDefault, Val, - level + 1); - RBB = switchConvert(RHSCases, bytesChecked, OrigBlock, NewDefault, Val, - level + 1); - - /* insert instructions to check whether the value we are switching on is - * equal to byte */ - ICmpInst *Comp = - new ICmpInst(ICmpInst::ICMP_ULT, Trunc, - ConstantInt::get(ByteType, pivot), "byteMatch"); - NewNode->getInstList().push_back(Comp); - BranchInst::Create(LBB, RBB, Comp, NewNode); - - } - - return NewNode; - -} - -bool SplitSwitchesTransform::splitSwitches(Module &M) { - -#if (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 7) - LLVMContext &C = M.getContext(); -#endif - - std::vector switches; - - /* iterate over all functions, bbs and instruction and add - * all switches to switches vector for later processing */ - for (auto &F : M) { - - if (!isInInstrumentList(&F)) continue; - - for (auto &BB : F) { - - SwitchInst *switchInst = nullptr; - - if ((switchInst = dyn_cast(BB.getTerminator()))) { - - if (switchInst->getNumCases() < 1) continue; - switches.push_back(switchInst); - - } - - } - - } - - if (!switches.size()) return false; - if (!be_quiet) - errs() << "Rewriting " << switches.size() << " switch statements " - << "\n"; - - for (auto &SI : switches) { - - BasicBlock *CurBlock = SI->getParent(); - BasicBlock *OrigBlock = CurBlock; - Function * F = CurBlock->getParent(); - /* this is the value we are switching on */ - Value * Val = SI->getCondition(); - BasicBlock *Default = SI->getDefaultDest(); - unsigned bitw = Val->getType()->getIntegerBitWidth(); - - if (!be_quiet) - errs() << "switch: " << SI->getNumCases() << " cases " << bitw - << " bit\n"; - - /* If there is only the default destination or the condition checks 8 bit or - * less, don't bother with the code below. */ - if (!SI->getNumCases() || bitw <= 8) { - - if (!be_quiet) errs() << "skip trivial switch..\n"; - continue; - - } - - /* Create a new, empty default block so that the new hierarchy of - * if-then statements go to this and the PHI nodes are happy. - * if the default block is set as an unreachable we avoid creating one - * because will never be a valid target.*/ - BasicBlock *NewDefault = nullptr; - NewDefault = BasicBlock::Create(SI->getContext(), "NewDefault", F, Default); - BranchInst::Create(Default, NewDefault); - - /* Prepare cases vector. */ - CaseVector Cases; - for (SwitchInst::CaseIt i = SI->case_begin(), e = SI->case_end(); i != e; - ++i) -#if LLVM_VERSION_MAJOR < 5 - Cases.push_back(CaseExpr(i.getCaseValue(), i.getCaseSuccessor())); -#else - Cases.push_back(CaseExpr(i->getCaseValue(), i->getCaseSuccessor())); -#endif - /* bugfix thanks to pbst - * round up bytesChecked (in case getBitWidth() % 8 != 0) */ - std::vector bytesChecked((7 + Cases[0].Val->getBitWidth()) / 8, - false); - BasicBlock * SwitchBlock = - switchConvert(Cases, bytesChecked, OrigBlock, NewDefault, Val, 0); - - /* Branch to our shiny new if-then stuff... */ - BranchInst::Create(SwitchBlock, OrigBlock); - - /* We are now done with the switch instruction, delete it. */ - CurBlock->getInstList().erase(SI); - - /* we have to update the phi nodes! */ - for (BasicBlock::iterator I = Default->begin(); I != Default->end(); ++I) { - - if (!isa(&*I)) { continue; } - PHINode *PN = cast(I); - - /* Only update the first occurrence. */ - unsigned Idx = 0, E = PN->getNumIncomingValues(); - for (; Idx != E; ++Idx) { - - if (PN->getIncomingBlock(Idx) == OrigBlock) { - - PN->setIncomingBlock(Idx, NewDefault); - break; - - } - - } - - } - - } - - verifyModule(M); - return true; - -} - -bool SplitSwitchesTransform::runOnModule(Module &M) { - - if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) - llvm::errs() << "Running split-switches-pass by laf.intel@gmail.com\n"; - else - be_quiet = 1; - splitSwitches(M); - verifyModule(M); - - return true; - -} - -static void registerSplitSwitchesTransPass(const PassManagerBuilder &, - legacy::PassManagerBase &PM) { - - auto p = new SplitSwitchesTransform(); - PM.add(p); - -} - -static RegisterStandardPasses RegisterSplitSwitchesTransPass( - PassManagerBuilder::EP_OptimizerLast, registerSplitSwitchesTransPass); - -static RegisterStandardPasses RegisterSplitSwitchesTransPass0( - PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitSwitchesTransPass); - -#if LLVM_VERSION_MAJOR >= 11 -static RegisterStandardPasses RegisterSplitSwitchesTransPassLTO( - PassManagerBuilder::EP_FullLinkTimeOptimizationLast, - registerSplitSwitchesTransPass); -#endif - diff --git a/src/afl-cc.c b/src/afl-cc.c new file mode 100644 index 00000000..e11ce40a --- /dev/null +++ b/src/afl-cc.c @@ -0,0 +1,1544 @@ +/* + american fuzzy lop++ - compiler instrumentation wrapper + ------------------------------------------------------- + + Written by Michal Zalewski, Laszlo Szekeres and Marc Heuse + + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + */ + +#define AFL_MAIN + +#include "common.h" +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" +#include "llvm-ngram-coverage.h" + +#include +#include +#include +#include +#include +#include +#include + +#if (LLVM_MAJOR - 0 == 0) + #undef LLVM_MAJOR +#endif +#if !defined(LLVM_MAJOR) + #define LLVM_MAJOR 0 +#endif + +static u8 * obj_path; /* Path to runtime libraries */ +static u8 **cc_params; /* Parameters passed to the real CC */ +static u32 cc_par_cnt = 1; /* Param count, including argv0 */ +static u8 llvm_fullpath[PATH_MAX]; +static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode, + compiler_mode, plusplus_mode; +static u8 have_gcc, have_llvm, have_gcc_plugin, have_lto; +static u8 *lto_flag = AFL_CLANG_FLTO, *argvnull; +static u8 debug; +static u8 cwd[4096]; +static u8 cmplog_mode; +u8 use_stdin; /* dummy */ +// static u8 *march_opt = CFLAGS_OPT; + +enum { + + INSTURMENT_DEFAULT = 0, + INSTRUMENT_CLASSIC = 1, + INSTRUMENT_AFL = 1, + INSTRUMENT_PCGUARD = 2, + INSTRUMENT_INSTRIM = 3, + INSTRUMENT_CFG = 3, + INSTRUMENT_LTO = 4, + INSTRUMENT_OPT_CTX = 8, + INSTRUMENT_OPT_NGRAM = 16 + +}; + +char instrument_mode_string[18][18] = { + + "DEFAULT", "CLASSIC", "PCGUARD", "CFG", "LTO", "", "", "", "CTX", "", + "", "", "", "", "", "", "NGRAM", "" + +}; + +enum { + + UNSET = 0, + LTO = 1, + LLVM = 2, + GCC_PLUGIN = 3, + GCC = 4 + +}; + +char compiler_mode_string[6][12] = { + + "AUTOSELECT", "LLVM-LTO", "LLVM", "GCC_PLUGIN", + "GCC", "" + +}; + +u8 *getthecwd() { + + static u8 fail[] = ""; + if (getcwd(cwd, sizeof(cwd)) == NULL) return fail; + return cwd; + +} + +/* Try to find the runtime libraries. If that fails, abort. */ + +static u8 *find_object(u8 *obj, u8 *argv0) { + + u8 *afl_path = getenv("AFL_PATH"); + u8 *slash = NULL, *tmp; + + if (afl_path) { + +#ifdef __ANDROID__ + tmp = alloc_printf("%s/%s", afl_path, obj); +#else + tmp = alloc_printf("%s/%s", afl_path, obj); +#endif + + if (!access(tmp, R_OK)) { + + obj_path = afl_path; + return tmp; + + } + + ck_free(tmp); + + } + + if (argv0) slash = strrchr(argv0, '/'); + + if (slash) { + + u8 *dir; + + *slash = 0; + dir = ck_strdup(argv0); + *slash = '/'; + +#ifdef __ANDROID__ + tmp = alloc_printf("%s/%s", dir, obj); +#else + tmp = alloc_printf("%s/%s", dir, obj); +#endif + + if (!access(tmp, R_OK)) { + + obj_path = dir; + return tmp; + + } + + ck_free(tmp); + ck_free(dir); + + } + + tmp = alloc_printf("%s/%s", AFL_PATH, obj); +#ifdef __ANDROID__ + if (!access(tmp, R_OK)) { + +#else + if (!access(tmp, R_OK)) { + +#endif + + obj_path = AFL_PATH; + return tmp; + + } + + ck_free(tmp); + return NULL; + +} + +/* Try to find the runtime libraries. If that fails, abort. */ + +static void find_obj(u8 *argv0) { + + u8 *afl_path = getenv("AFL_PATH"); + u8 *slash, *tmp; + + if (afl_path) { + +#ifdef __ANDROID__ + tmp = alloc_printf("%s/afl-compiler-rt.so", afl_path); +#else + tmp = alloc_printf("%s/afl-compiler-rt.o", afl_path); +#endif + + if (!access(tmp, R_OK)) { + + obj_path = afl_path; + ck_free(tmp); + return; + + } + + ck_free(tmp); + + } + + slash = strrchr(argv0, '/'); + + if (slash) { + + u8 *dir; + + *slash = 0; + dir = ck_strdup(argv0); + *slash = '/'; + +#ifdef __ANDROID__ + tmp = alloc_printf("%s/afl-compiler-rt.so", dir); +#else + tmp = alloc_printf("%s/afl-compiler-rt.o", dir); +#endif + + if (!access(tmp, R_OK)) { + + obj_path = dir; + ck_free(tmp); + return; + + } + + ck_free(tmp); + ck_free(dir); + + } + +#ifdef __ANDROID__ + if (!access(AFL_PATH "/afl-compiler-rt.so", R_OK)) { + +#else + if (!access(AFL_PATH "/afl-compiler-rt.o", R_OK)) { + +#endif + + obj_path = AFL_PATH; + return; + + } + + FATAL( + "Unable to find 'afl-compiler-rt.o' or 'afl-llvm-pass.so'. Please set " + "AFL_PATH"); + +} + +/* Copy argv to cc_params, making the necessary edits. */ + +static void edit_params(u32 argc, char **argv, char **envp) { + + u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, shared_linking = 0, + preprocessor_only = 0, have_unroll = 0, have_o = 0, have_pic = 0; + u8 *name; + + cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); + + name = strrchr(argv[0], '/'); + if (!name) + name = argv[0]; + else + ++name; + + if (lto_mode) { + + if (lto_flag[0] != '-') + FATAL( + "Using afl-clang-lto is not possible because Makefile magic did not " + "identify the correct -flto flag"); + else + compiler_mode = LTO; + + } + + if (plusplus_mode) { + + u8 *alt_cxx = getenv("AFL_CXX"); + + if (!alt_cxx) { + + if (compiler_mode >= GCC_PLUGIN) { + + alt_cxx = "g++"; + + } else { + + if (USE_BINDIR) + snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang++", + LLVM_BINDIR); + else + snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANGPP_BIN); + alt_cxx = llvm_fullpath; + + } + + } + + cc_params[0] = alt_cxx; + + } else { + + u8 *alt_cc = getenv("AFL_CC"); + + if (!alt_cc) { + + if (compiler_mode >= GCC_PLUGIN) { + + alt_cc = "gcc"; + + } else { + + if (USE_BINDIR) + snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang", + LLVM_BINDIR); + else + snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANGPP_BIN); + alt_cc = llvm_fullpath; + + } + + } + + cc_params[0] = alt_cc; + + } + + if (compiler_mode == GCC) { + + cc_params[cc_par_cnt++] = "-B"; + cc_params[cc_par_cnt++] = obj_path; + + } + + if (compiler_mode == GCC_PLUGIN) { + + char *fplugin_arg = + alloc_printf("-fplugin=%s", find_object("afl-gcc-pass.so", argvnull)); + cc_params[cc_par_cnt++] = fplugin_arg; + + } + + if (compiler_mode == LLVM || compiler_mode == LTO) { + + cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; + + if (lto_mode && plusplus_mode) + cc_params[cc_par_cnt++] = "-lc++"; // needed by fuzzbench, early + + if (lto_mode) { + + if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-lto-instrumentlist.so", obj_path); + + } + + } + + if (getenv("AFL_LLVM_DICT2FILE")) { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-dict2file.so", obj_path); + + } + + // laf + if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { + + if (lto_mode) { + + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-switches-pass.so", obj_path); + + } + + } + + if (getenv("LAF_TRANSFORM_COMPARES") || + getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { + + if (lto_mode) { + + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/compare-transform-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/compare-transform-pass.so", obj_path); + + } + + } + + if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || + getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { + + if (lto_mode) { + + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/split-compares-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-compares-pass.so", obj_path); + + } + + } + + // /laf + + unsetenv("AFL_LD"); + unsetenv("AFL_LD_CALLER"); + if (cmplog_mode) { + + if (lto_mode) { + + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/cmplog-routines-pass.so", obj_path); + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/cmplog-instructions-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/cmplog-routines-pass.so", obj_path); + + // reuse split switches from laf + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-switches-pass.so", obj_path); + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/cmplog-instructions-pass.so", obj_path); + + } + + cc_params[cc_par_cnt++] = "-fno-inline"; + + } + + if (lto_mode) { + + u8 *ld_path = strdup(AFL_REAL_LD); + if (!*ld_path) ld_path = "ld.lld"; +#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 12 + cc_params[cc_par_cnt++] = alloc_printf("--ld-path=%s", ld_path); +#else + cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", ld_path); +#endif + + cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; + + if (instrument_mode == INSTRUMENT_CFG) + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path); + else + + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path); + cc_params[cc_par_cnt++] = lto_flag; + + } else { + + if (instrument_mode == INSTRUMENT_PCGUARD) { + +#if LLVM_MAJOR >= 4 + cc_params[cc_par_cnt++] = + "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default +#else + FATAL("pcguard instrumentation requires llvm 4.0.1+"); +#endif + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + if (instrument_mode == INSTRUMENT_CFG) + cc_params[cc_par_cnt++] = + alloc_printf("%s/libLLVMInsTrim.so", obj_path); + else + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-pass.so", obj_path); + + } + + } + + // cc_params[cc_par_cnt++] = "-Qunused-arguments"; + + // in case LLVM is installed not via a package manager or "make install" + // e.g. compiled download or compiled from github then its ./lib directory + // might not be in the search path. Add it if so. + u8 *libdir = strdup(LLVM_LIBDIR); + if (plusplus_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) && + strncmp(libdir, "/lib", 4)) { + + cc_params[cc_par_cnt++] = "-rpath"; + cc_params[cc_par_cnt++] = libdir; + + } else { + + free(libdir); + + } + + u32 idx; + if (lto_mode && argc > 1) { + + for (idx = 1; idx < argc; idx++) { + + if (!strncasecmp(argv[idx], "-fpic", 5)) have_pic = 1; + + } + + if (!have_pic) cc_params[cc_par_cnt++] = "-fPIC"; + + } + + } + + /* Detect stray -v calls from ./configure scripts. */ + + while (--argc) { + + u8 *cur = *(++argv); + + if (!strncmp(cur, "--afl", 5)) continue; + if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue; + if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; + if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined")) + continue; + + if (!strcmp(cur, "-m32")) bit_mode = 32; + if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32; + if (!strcmp(cur, "-m64")) bit_mode = 64; + + if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory")) + asan_set = 1; + + if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; + + if (!strcmp(cur, "-x")) x_set = 1; + if (!strcmp(cur, "-E")) preprocessor_only = 1; + if (!strcmp(cur, "-shared")) shared_linking = 1; + + if (!strncmp(cur, "-O", 2)) have_o = 1; + if (!strncmp(cur, "-f", 2) && strstr(cur, "unroll-loop")) have_unroll = 1; + + cc_params[cc_par_cnt++] = cur; + + } + + if (getenv("AFL_HARDEN")) { + + cc_params[cc_par_cnt++] = "-fstack-protector-all"; + + if (!fortify_set) cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2"; + + } + + if (!asan_set) { + + if (getenv("AFL_USE_ASAN")) { + + if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive"); + + if (getenv("AFL_HARDEN")) + FATAL("ASAN and AFL_HARDEN are mutually exclusive"); + + cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; + cc_params[cc_par_cnt++] = "-fsanitize=address"; + + } else if (getenv("AFL_USE_MSAN")) { + + if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive"); + + if (getenv("AFL_HARDEN")) + FATAL("MSAN and AFL_HARDEN are mutually exclusive"); + + cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; + cc_params[cc_par_cnt++] = "-fsanitize=memory"; + + } + + } + + if (getenv("AFL_USE_UBSAN")) { + + cc_params[cc_par_cnt++] = "-fsanitize=undefined"; + cc_params[cc_par_cnt++] = "-fsanitize-undefined-trap-on-error"; + cc_params[cc_par_cnt++] = "-fno-sanitize-recover=all"; + + } + + if (getenv("AFL_USE_CFISAN")) { + + if (!lto_mode) { + + uint32_t i = 0, found = 0; + while (envp[i] != NULL && !found) + if (strncmp("-flto", envp[i++], 5) == 0) found = 1; + if (!found) cc_params[cc_par_cnt++] = "-flto"; + + } + + cc_params[cc_par_cnt++] = "-fsanitize=cfi"; + cc_params[cc_par_cnt++] = "-fvisibility=hidden"; + + } + + if (!getenv("AFL_DONT_OPTIMIZE")) { + + cc_params[cc_par_cnt++] = "-g"; + if (!have_o) cc_params[cc_par_cnt++] = "-O3"; + if (!have_unroll) cc_params[cc_par_cnt++] = "-funroll-loops"; + // if (strlen(march_opt) > 1 && march_opt[0] == '-') + // cc_params[cc_par_cnt++] = march_opt; + + } + + if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || + getenv("LAF_TRANSFORM_COMPARES") || lto_mode) { + + cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-bcmp"; + cc_params[cc_par_cnt++] = "-fno-builtin-strstr"; + cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr"; + + } + +#if defined(USEMMAP) && !defined(__HAIKU__) + cc_params[cc_par_cnt++] = "-lrt"; +#endif + + cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; + cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; + cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; + + /* When the user tries to use persistent or deferred forkserver modes by + appending a single line to the program, we want to reliably inject a + signature into the binary (to be picked up by afl-fuzz) and we want + to call a function from the runtime .o file. This is unnecessarily + painful for three reasons: + + 1) We need to convince the compiler not to optimize out the signature. + This is done with __attribute__((used)). + + 2) We need to convince the linker, when called with -Wl,--gc-sections, + not to do the same. This is done by forcing an assignment to a + 'volatile' pointer. + + 3) We need to declare __afl_persistent_loop() in the global namespace, + but doing this within a method in a class is hard - :: and extern "C" + are forbidden and __attribute__((alias(...))) doesn't work. Hence the + __asm__ aliasing trick. + + */ + + cc_params[cc_par_cnt++] = + "-D__AFL_FUZZ_INIT()=" + "int __afl_sharedmem_fuzzing = 1;" + "extern unsigned int *__afl_fuzz_len;" + "extern unsigned char *__afl_fuzz_ptr;" + "unsigned char __afl_fuzz_alt[1024000];" + "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; + cc_params[cc_par_cnt++] = + "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " + "__afl_fuzz_alt_ptr)"; + cc_params[cc_par_cnt++] = + "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : " + "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1024000)) == 0xffffffff " + "? 0 : *__afl_fuzz_len)"; + + cc_params[cc_par_cnt++] = + "-D__AFL_LOOP(_A)=" + "({ static volatile char *_B __attribute__((used)); " + " _B = (char*)\"" PERSIST_SIG + "\"; " +#ifdef __APPLE__ + "__attribute__((visibility(\"default\"))) " + "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " +#else + "__attribute__((visibility(\"default\"))) " + "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " +#endif /* ^__APPLE__ */ + "_L(_A); })"; + + cc_params[cc_par_cnt++] = + "-D__AFL_INIT()=" + "do { static volatile char *_A __attribute__((used)); " + " _A = (char*)\"" DEFER_SIG + "\"; " +#ifdef __APPLE__ + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"___afl_manual_init\"); " +#else + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"__afl_manual_init\"); " +#endif /* ^__APPLE__ */ + "_I(); } while (0)"; + + if (x_set) { + + cc_params[cc_par_cnt++] = "-x"; + cc_params[cc_par_cnt++] = "none"; + + } + + if (preprocessor_only) { + + /* In the preprocessor_only case (-E), we are not actually compiling at + all but requesting the compiler to output preprocessed sources only. + We must not add the runtime in this case because the compiler will + simply output its binary content back on stdout, breaking any build + systems that rely on a separate source preprocessing step. */ + cc_params[cc_par_cnt] = NULL; + return; + + } + +#ifndef __ANDROID__ + + if (compiler_mode != GCC) { + + switch (bit_mode) { + + case 0: + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-compiler-rt.o", obj_path); + if (lto_mode) + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); + break; + + case 32: + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-compiler-rt-32.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m32 is not supported by your compiler"); + if (lto_mode) { + + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m32 is not supported by your compiler"); + + } + + break; + + case 64: + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-compiler-rt-64.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m64 is not supported by your compiler"); + if (lto_mode) { + + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m64 is not supported by your compiler"); + + } + + break; + + } + + #ifndef __APPLE__ + if (!shared_linking) + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); + #endif + + } + +#endif + + cc_params[cc_par_cnt] = NULL; + +} + +/* Main entry point */ + +int main(int argc, char **argv, char **envp) { + + int i; + char *callname = argv[0], *ptr = NULL; + + if (getenv("AFL_DEBUG")) { + + debug = 1; + if (strcmp(getenv("AFL_DEBUG"), "0") == 0) unsetenv("AFL_DEBUG"); + + } else if (getenv("AFL_QUIET")) + + be_quiet = 1; + + if ((ptr = strrchr(callname, '/')) != NULL) callname = ptr + 1; + argvnull = (u8 *)argv[0]; + check_environment_vars(envp); + + if ((ptr = find_object("as", argv[0])) != NULL) { + + have_gcc = 1; + ck_free(ptr); + + } + +#if (LLVM_MAJOR > 2) + + if ((ptr = find_object("SanitizerCoverageLTO.so", argv[0])) != NULL) { + + have_lto = 1; + ck_free(ptr); + + } + + if ((ptr = find_object("cmplog-routines-pass.so", argv[0])) != NULL) { + + have_llvm = 1; + ck_free(ptr); + + } + +#endif + + if ((ptr = find_object("afl-gcc-pass.so", argv[0])) != NULL) { + + have_gcc_plugin = 1; + ck_free(ptr); + + } + +#if (LLVM_MAJOR > 2) + + if (strncmp(callname, "afl-clang-fast", 14) == 0) { + + compiler_mode = LLVM; + + } else if (strncmp(callname, "afl-clang-lto", 13) == 0 || + + strncmp(callname, "afl-lto", 7) == 0) { + + compiler_mode = LTO; + + } else + +#endif + if (strncmp(callname, "afl-gcc-fast", 12) == 0 || + + strncmp(callname, "afl-g++-fast", 12) == 0) { + + compiler_mode = GCC_PLUGIN; + + } else if (strncmp(callname, "afl-gcc", 7) == 0 || + + strncmp(callname, "afl-g++", 7) == 0) { + + compiler_mode = GCC; + + } + + if ((ptr = getenv("AFL_CC_COMPILER"))) { + + if (compiler_mode) { + + WARNF( + "\"AFL_CC_COMPILER\" is set but a specific compiler was already " + "selected by command line parameter or symlink, ignoring the " + "environment variable!"); + + } else { + + if (strncasecmp(ptr, "LTO", 3) == 0) { + + compiler_mode = LTO; + + } else if (strncasecmp(ptr, "LLVM", 4) == 0) { + + compiler_mode = LLVM; + + } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || + + strncasecmp(ptr, "GCC-P", 5) == 0 || + strncasecmp(ptr, "GCCP", 4) == 0) { + + compiler_mode = GCC_PLUGIN; + + } else if (strcasecmp(ptr, "GCC") == 0) { + + compiler_mode = GCC; + + } else + + FATAL("Unknown AFL_CC_COMPILER mode: %s\n", ptr); + + } + + } + + for (i = 1; i < argc; i++) { + + if (strncmp(argv[i], "--afl", 5) == 0) { + + if (compiler_mode) + WARNF( + "--afl-... compiler mode supersedes the AFL_CC_COMPILER and " + "symlink compiler selection!"); + + ptr = argv[i]; + ptr += 5; + while (*ptr == '-') + ptr++; + + if (strncasecmp(ptr, "LTO", 3) == 0) { + + compiler_mode = LTO; + + } else if (strncasecmp(ptr, "LLVM", 4) == 0) { + + compiler_mode = LLVM; + + } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || + + strncasecmp(ptr, "GCC-P", 5) == 0 || + strncasecmp(ptr, "GCCP", 4) == 0) { + + compiler_mode = GCC_PLUGIN; + + } else if (strcasecmp(ptr, "GCC") == 0) { + + compiler_mode = GCC; + + } else + + FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]); + + } + + } + + if (strlen(callname) > 2 && + (strncmp(callname + strlen(callname) - 2, "++", 2) == 0 || + strstr(callname, "-g++") != NULL)) + plusplus_mode = 1; + + if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") || + getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) { + + if (instrument_mode == 0) + instrument_mode = INSTRUMENT_PCGUARD; + else if (instrument_mode != INSTRUMENT_PCGUARD) + FATAL("you can not set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together"); + + } + + if ((getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) && + getenv("AFL_DONT_OPTIMIZE")) + WARNF( + "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined " + "for file matching, only function matching!"); + + if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || + getenv("INSTRIM_LIB")) { + + if (instrument_mode == 0) + instrument_mode = INSTRUMENT_CFG; + else if (instrument_mode != INSTRUMENT_CFG) + FATAL( + "you can not set AFL_LLVM_INSTRUMENT and AFL_LLVM_INSTRIM together"); + + } + + if (getenv("AFL_LLVM_CTX")) instrument_opt_mode |= INSTRUMENT_OPT_CTX; + + if (getenv("AFL_LLVM_NGRAM_SIZE")) { + + instrument_opt_mode |= INSTRUMENT_OPT_NGRAM; + ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE")); + if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) + FATAL( + "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX " + "(%u)", + NGRAM_SIZE_MAX); + + } + + if (getenv("AFL_LLVM_INSTRUMENT")) { + + u8 *ptr = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); + + while (ptr) { + + if (strncasecmp(ptr, "afl", strlen("afl")) == 0 || + strncasecmp(ptr, "classic", strlen("classic")) == 0) { + + if (instrument_mode == INSTRUMENT_LTO) { + + instrument_mode = INSTRUMENT_CLASSIC; + lto_mode = 1; + + } else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL) + + instrument_mode = INSTRUMENT_AFL; + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + + } + + if (strncasecmp(ptr, "pc-guard", strlen("pc-guard")) == 0 || + strncasecmp(ptr, "pcguard", strlen("pcguard")) == 0) { + + if (!instrument_mode || instrument_mode == INSTRUMENT_PCGUARD) + instrument_mode = INSTRUMENT_PCGUARD; + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + + } + + if (strncasecmp(ptr, "cfg", strlen("cfg")) == 0 || + strncasecmp(ptr, "instrim", strlen("instrim")) == 0) { + + if (instrument_mode == INSTRUMENT_LTO) { + + instrument_mode = INSTRUMENT_CFG; + lto_mode = 1; + + } else if (!instrument_mode || instrument_mode == INSTRUMENT_CFG) + + instrument_mode = INSTRUMENT_CFG; + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + + } + + if (strncasecmp(ptr, "lto", strlen("lto")) == 0) { + + lto_mode = 1; + if (!instrument_mode || instrument_mode == INSTRUMENT_LTO) + instrument_mode = INSTRUMENT_LTO; + else if (instrument_mode != INSTRUMENT_CFG) + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + + } + + if (strncasecmp(ptr, "ctx", strlen("ctx")) == 0) { + + instrument_opt_mode |= INSTRUMENT_OPT_CTX; + setenv("AFL_LLVM_CTX", "1", 1); + + } + + if (strncasecmp(ptr, "ngram", strlen("ngram")) == 0) { + + ptr += strlen("ngram"); + while (*ptr && (*ptr < '0' || *ptr > '9')) + ptr++; + + if (!*ptr) { + + if ((ptr = getenv("AFL_LLVM_NGRAM_SIZE")) != NULL) + FATAL( + "you must set the NGRAM size with (e.g. for value 2) " + "AFL_LLVM_INSTRUMENT=ngram-2"); + + } + + ngram_size = atoi(ptr); + if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) + FATAL( + "NGRAM instrumentation option must be between 2 and " + "NGRAM_SIZE_MAX " + "(%u)", + NGRAM_SIZE_MAX); + instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM); + ptr = alloc_printf("%u", ngram_size); + setenv("AFL_LLVM_NGRAM_SIZE", ptr, 1); + + } + + ptr = strtok(NULL, ":,;"); + + } + + } + + if (!compiler_mode) { + + // lto is not a default because outside of afl-cc RANLIB and AR have to + // be set to llvm versions so this would work + if (have_llvm) + compiler_mode = LLVM; + else if (have_gcc_plugin) + compiler_mode = GCC_PLUGIN; + else if (have_gcc) + compiler_mode = GCC; + else if (have_lto) + compiler_mode = LTO; + else + FATAL("no compiler mode available"); + + } + + if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) { + + char *fp; + fp = realpath(argv[0], NULL); + + printf("afl-cc" VERSION + " by Michal Zalewski, Laszlo Szekeres, Marc Heuse\n"); + + SAYF( + "\n" + "afl-cc/afl-c++ [options]\n" + "\n" + "This is a helper application for afl-fuzz. It serves as a drop-in " + "replacement\n" + "for gcc and clang, letting you recompile third-party code with the " + "required\n" + "runtime instrumentation. A common use pattern would be one of the " + "following:\n\n" + + " CC=afl-cc CXX=afl-c++ ./configure --disable-shared\n" + " cmake -DCMAKE_C_COMPILERC=afl-cc -DCMAKE_CXX_COMPILER=afl-c++ .\n" + " CC=afl-cc CXX=afl-c++ meson\n\n"); + + SAYF( + " |---------------- FEATURES " + "---------------|\n" + "MODES: NCC PERSIST SNAP DICT LAF " + "CMPLOG SELECT\n" + " [LTO] llvm LTO: %s%s\n" + " PCGUARD DEFAULT yes yes yes yes yes yes " + " yes\n" + " CLASSIC yes yes yes yes yes yes " + " yes\n" + " [LLVM] llvm: %s%s\n" + " PCGUARD %s yes yes yes module yes yes " + "extern\n" + " CLASSIC %s no yes yes module yes yes " + "yes\n" + " - NORMAL\n" + " - CTX\n" + " - NGRAM-{2-16}\n" + " INSTRIM no yes yes module yes yes " + " yes\n" + " - NORMAL\n" + " - CTX\n" + " - NGRAM-{2-16}\n" + " [GCC_PLUGIN] gcc plugin: %s%s\n" + " CLASSIC DEFAULT no yes yes no no no " + " simple\n" + " [GCC] simple gcc: %s%s\n" + " CLASSIC DEFAULT no no no no no no " + " no\n\n", + have_lto ? "AVAILABLE" : "unavailable!", + compiler_mode == LTO ? " [SELECTED]" : "", + have_llvm ? "AVAILABLE" : "unavailable!", + compiler_mode == LLVM ? " [SELECTED]" : "", + LLVM_MAJOR > 6 ? "DEFAULT" : " ", + LLVM_MAJOR > 6 ? " " : "DEFAULT", + have_gcc_plugin ? "AVAILABLE" : "unavailable!", + compiler_mode == GCC_PLUGIN ? " [SELECTED]" : "", + have_gcc ? "AVAILABLE" : "unavailable!", + compiler_mode == GCC ? " [SELECTED]" : ""); + + SAYF( + "Modes:\n" + " To select the compiler mode use a symlink version (e.g. " + "afl-clang-fast), set\n" + " the environment variable AFL_CC_COMPILER to a mode (e.g. LLVM) or " + "use the\n" + " command line parameter --afl-MODE (e.g. --afl-llvm). If none is " + "selected,\n" + " afl-cc will select the best available (LLVM -> GCC_PLUGIN -> GCC).\n" + " The best is LTO but it often needs RANLIB and AR settings outside " + "of afl-cc.\n\n"); + + SAYF( + "Sub-Modes: (set via env AFL_LLVM_INSTRUMENT, afl-cc selects the best " + "available)\n" + " PCGUARD: Dominator tree instrumentation (best!) (README.llvm.md)\n" + " CLASSIC: decision target instrumentation (README.llvm.md)\n" + " CTX: CLASSIC + callee context (instrumentation/README.ctx.md)\n" + " NGRAM-x: CLASSIC + previous path " + "((instrumentation/README.ngram.md)\n" + " INSTRIM: Dominator tree (for LLVM <= 6.0) " + "(instrumentation/README.instrim.md)\n\n"); + + SAYF( + "Features: (see documentation links)\n" + " NCC: non-colliding coverage [automatic] (that is an amazing " + "thing!)\n" + " (instrumentation/README.lto.md)\n" + " PERSIST: persistent mode support [code] (huge speed increase!)\n" + " (instrumentation/README.persistent_mode.md)\n" + " SNAP: linux lkm snapshot module support [automatic] (speed " + "increase)\n" + " (https://github.com/AFLplusplus/AFL-Snapshot-LKM/)\n" + " DICT: dictionary in the target [yes=automatic or llvm module " + "pass]\n" + " (instrumentation/README.lto.md + " + "instrumentation/README.llvm.md)\n" + " LAF: comparison splitting [env] " + "(instrumentation/README.laf-intel.md)\n" + " CMPLOG: input2state exploration [env] " + "(instrumentation/README.cmplog.md)\n" + " SELECT: selective instrumentation (allow/deny) on filename or " + "function [env]\n" + " (instrumentation/README.instrument_list.md)\n\n"); + + if (argc < 2 || strncmp(argv[1], "-hh", 3)) { + + SAYF( + "To see all environment variables for the configuration of afl-cc " + "use \"-hh\".\n"); + + } else { + + SAYF( + "Environment variables used:\n" + " AFL_CC: path to the C compiler to use\n" + " AFL_CXX: path to the C++ compiler to use\n" + " AFL_DEBUG: enable developer debugging output\n" + " AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" + " AFL_HARDEN: adds code hardening to catch memory bugs\n" + " AFL_INST_RATIO: percentage of branches to instrument\n" +#if LLVM_MAJOR < 9 + " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" +#else + " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" +#endif + " AFL_LLVM_DICT2FILE: generate an afl dictionary based on found " + "comparisons\n" + " AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n" + " AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n" + " AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n" + " AFL_LLVM_LAF_SPLIT_SWITCHES: cascaded comparisons on switches\n" + " AFL_LLVM_LAF_SPLIT_FLOATS: cascaded comparisons on floats\n" + " AFL_LLVM_LAF_TRANSFORM_COMPARES: cascade comparisons for string " + "functions\n" + " AFL_LLVM_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: enable " + "instrument allow/\n" + " deny listing (selective instrumentation)\n" + " AFL_NO_BUILTIN: no builtins for string compare functions (for " + "libtokencap.so)\n" + " AFL_PATH: path to instrumenting pass and runtime " + "(afl-compiler-rt.*o)\n" + " AFL_LLVM_DOCUMENT_IDS: document edge IDs given to which function " + "(LTO only)\n" + " AFL_QUIET: suppress verbose output\n" + " AFL_USE_ASAN: activate address sanitizer\n" + " AFL_USE_CFISAN: activate control flow sanitizer\n" + " AFL_USE_MSAN: activate memory sanitizer\n" + " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n", + BIN_PATH, BIN_PATH); + + SAYF( + "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " + "variables:\n" + " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen mutator)\n" + " AFL_LLVM_INSTRUMENT: set instrumentation mode: CLASSIC, INSTRIM, " + "PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-16\n" + " You can also use the old environment variables instead:\n" + " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n" + " AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n" + " AFL_LLVM_INSTRIM_LOOPHEAD: optimize loop tracing for speed " + "(option to INSTRIM)\n" + " AFL_LLVM_CTX: use context sensitive coverage (for CLASSIC and " + "INSTRIM)\n" + " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " + "CLASSIC and INSTRIM)\n"); + +#ifdef AFL_CLANG_FLTO + SAYF( + "\nLTO/afl-clang-lto specific environment variables:\n" + "AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), e.g. " + "0x10000\n" + "AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding " + "functions they are in into this file\n" + "AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " + "global var\n" + "AFL_LLVM_LTO_STARTID: from which ID to start counting from for a " + "bb\n" + "AFL_REAL_LD: use this lld linker instead of the compiled in path\n" + "\nafl-clang-lto was built with linker target \"%s\" and LTO flags " + "\"%s\"\n" + "If anything fails - be sure to read README.lto.md!\n", + AFL_REAL_LD, AFL_CLANG_FLTO); +#endif + + } + + SAYF( + "For any information on the available instrumentations and options " + "please \n" + "consult the README.md, especially section 3.1 about instrumenting " + "targets.\n\n"); + +#if (LLVM_MAJOR > 2) + if (have_lto) + SAYF("afl-cc LTO with ld=%s %s\n", AFL_REAL_LD, AFL_CLANG_FLTO); + if (have_llvm) + SAYF("afl-cc LLVM version %d with the the binary path \"%s\".\n", + LLVM_MAJOR, LLVM_BINDIR); + if (have_lto || have_llvm) SAYF("\n"); +#endif + + SAYF( + "Do not be overwhelmed :) afl-cc uses good defaults if no options are " + "selected.\n" + "Read the documentation for FEATURES though, all are good but few are " + "defaults.\n\n"); + + exit(1); + + } + + if (compiler_mode == LTO) { + + if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO || + instrument_mode == INSTRUMENT_CFG) { + + lto_mode = 1; + if (!instrument_mode) { + + instrument_mode = INSTRUMENT_CFG; + ptr = instrument_mode_string[instrument_mode]; + + } + + } else if (instrument_mode == INSTRUMENT_LTO || + + instrument_mode == INSTRUMENT_CLASSIC) { + + lto_mode = 1; + + } else { + + if (!be_quiet) + WARNF("afl-clang-lto called with mode %s, using that mode instead", + instrument_mode_string[instrument_mode]); + + } + + } + + if (instrument_mode == 0 && compiler_mode < GCC_PLUGIN) { + +#if LLVM_MAJOR <= 6 + instrument_mode = INSTRUMENT_AFL; +#else + if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { + + instrument_mode = INSTRUMENT_AFL; + WARNF( + "switching to classic instrumentation because " + "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD. Use " + "-fsanitize-coverage-allowlist=allowlist.txt or " + "-fsanitize-coverage-blocklist=denylist.txt if you want to use " + "PCGUARD. Requires llvm 12+. See https://clang.llvm.org/docs/ " + "SanitizerCoverage.html#partially-disabling-instrumentation"); + + } else + + instrument_mode = INSTRUMENT_PCGUARD; +#endif + + } + + if (instrument_opt_mode && compiler_mode != LLVM) + FATAL("CTX and NGRAM can only be used in LLVM mode"); + + if (!instrument_opt_mode) { + + if (lto_mode && instrument_mode == INSTRUMENT_CFG) + instrument_mode = INSTRUMENT_PCGUARD; + ptr = instrument_mode_string[instrument_mode]; + + } else { + + if (instrument_opt_mode == INSTRUMENT_OPT_CTX) + + ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]); + else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM) + ptr = alloc_printf("%s + NGRAM-%u", + instrument_mode_string[instrument_mode], ngram_size); + else + ptr = alloc_printf("%s + CTX + NGRAM-%u", + instrument_mode_string[instrument_mode], ngram_size); + + } + +#ifndef AFL_CLANG_FLTO + if (lto_mode) + FATAL( + "instrumentation mode LTO specified but LLVM support not available " + "(requires LLVM 11 or higher)"); +#endif + + if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC && + instrument_mode != INSTRUMENT_CFG) + FATAL( + "CTX and NGRAM instrumentation options can only be used with CFG " + "(recommended) and CLASSIC instrumentation modes!"); + + if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO")) + FATAL( + "AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set " + "together"); + + if (instrument_mode == INSTRUMENT_PCGUARD && + (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST"))) + FATAL( + "Instrumentation type PCGUARD does not support " + "AFL_LLVM_ALLOWLIST/DENYLIST! Use " + "-fsanitize-coverage-allowlist=allowlist.txt or " + "-fsanitize-coverage-blocklist=denylist.txt instead (requires llvm " + "12+), see " + "https://clang.llvm.org/docs/" + "SanitizerCoverage.html#partially-disabling-instrumentation"); + + u8 *ptr2; + + if ((ptr2 = getenv("AFL_LLVM_DICT2FILE")) != NULL && *ptr2 != '/') + FATAL("AFL_LLVM_DICT2FILE must be set to an absolute file path"); + + if ((isatty(2) && !be_quiet) || debug) { + + SAYF(cCYA + "afl-cc " VERSION cRST + " by Michal Zalewski, Laszlo Szekeres, Marc Heuse - mode: %s-%s\n", + compiler_mode_string[compiler_mode], ptr); + + } + + if (!be_quiet && !lto_mode && + ((ptr2 = getenv("AFL_MAP_SIZE")) || (ptr2 = getenv("AFL_MAPSIZE")))) { + + u32 map_size = atoi(ptr2); + if (map_size != MAP_SIZE) + WARNF("AFL_MAP_SIZE is not supported by afl-clang-fast"); + + } + + if (debug) { + + SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + for (i = 0; i < argc; i++) + SAYF(" \"%s\"", argv[i]); + SAYF("\n"); + + } + + if (getenv("AFL_LLVM_LAF_ALL")) { + + setenv("AFL_LLVM_LAF_SPLIT_SWITCHES", "1", 1); + setenv("AFL_LLVM_LAF_SPLIT_COMPARES", "1", 1); + setenv("AFL_LLVM_LAF_SPLIT_FLOATS", "1", 1); + setenv("AFL_LLVM_LAF_TRANSFORM_COMPARES", "1", 1); + + } + + cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG"); + if (!be_quiet && cmplog_mode) + printf("CmpLog mode by \n"); + +#ifndef __ANDROID__ + find_obj(argv[0]); +#endif + + edit_params(argc, argv, envp); + + if (debug) { + + SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + for (i = 0; i < cc_par_cnt; i++) + SAYF(" \"%s\"", cc_params[i]); + SAYF("\n"); + + } + + execvp(cc_params[0], (char **)cc_params); + + FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]); + + return 0; + +} + diff --git a/src/afl-gcc.c b/src/afl-gcc.c deleted file mode 100644 index 97564aea..00000000 --- a/src/afl-gcc.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - american fuzzy lop++ - wrapper for GCC and clang - ------------------------------------------------ - - Originally written by Michal Zalewski - - Now maintained by Marc Heuse , - Heiko Eißfeldt and - Andrea Fioraldi - - Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This program is a drop-in replacement for GCC or clang. The most common way - of using it is to pass the path to afl-gcc or afl-clang via CC when invoking - ./configure. - - (Of course, use CXX and point it to afl-g++ / afl-clang++ for C++ code.) - - The wrapper needs to know the path to afl-as (renamed to 'as'). The default - is /usr/local/lib/afl/. A convenient way to specify alternative directories - would be to set AFL_PATH. - - If AFL_HARDEN is set, the wrapper will compile the target app with various - hardening options that may help detect memory management issues more - reliably. You can also specify AFL_USE_ASAN to enable ASAN. - - If you want to call a non-default compiler as a next step of the chain, - specify its location via AFL_CC or AFL_CXX. - - */ - -#define AFL_MAIN - -#include "config.h" -#include "types.h" -#include "debug.h" -#include "alloc-inl.h" - -#include -#include -#include -#include - -static u8 * as_path; /* Path to the AFL 'as' wrapper */ -static u8 **cc_params; /* Parameters passed to the real CC */ -static u32 cc_par_cnt = 1; /* Param count, including argv0 */ -static u8 be_quiet, /* Quiet mode */ - clang_mode; /* Invoked as afl-clang*? */ - -/* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived - from argv[0]. If that fails, abort. */ - -static void find_as(u8 *argv0) { - - u8 *afl_path = getenv("AFL_PATH"); - u8 *slash, *tmp; - - if (afl_path) { - - tmp = alloc_printf("%s/as", afl_path); - - if (!access(tmp, X_OK)) { - - as_path = afl_path; - ck_free(tmp); - return; - - } - - ck_free(tmp); - - } - - slash = strrchr(argv0, '/'); - - if (slash) { - - u8 *dir; - - *slash = 0; - dir = ck_strdup(argv0); - *slash = '/'; - - tmp = alloc_printf("%s/afl-as", dir); - - if (!access(tmp, X_OK)) { - - as_path = dir; - ck_free(tmp); - return; - - } - - ck_free(tmp); - ck_free(dir); - - } - - if (!access(AFL_PATH "/as", X_OK)) { - - as_path = AFL_PATH; - return; - - } - - FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH"); - -} - -/* Copy argv to cc_params, making the necessary edits. */ - -static void edit_params(u32 argc, char **argv) { - - u8 fortify_set = 0, asan_set = 0; - u8 *name; - -#if defined(__FreeBSD__) && defined(WORD_SIZE_64) - u8 m32_set = 0; -#endif - - cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); - - name = strrchr(argv[0], '/'); - if (!name) { - - name = argv[0]; - - /* This should never happen but fixes a scan-build warning */ - if (!name) { FATAL("Empty argv set"); } - - } else { - - ++name; - - } - - if (!strncmp(name, "afl-clang", 9)) { - - clang_mode = 1; - - setenv(CLANG_ENV_VAR, "1", 1); - - if (!strcmp(name, "afl-clang++")) { - - u8 *alt_cxx = getenv("AFL_CXX"); - cc_params[0] = alt_cxx && *alt_cxx ? alt_cxx : (u8 *)"clang++"; - - } else if (!strcmp(name, "afl-clang")) { - - u8 *alt_cc = getenv("AFL_CC"); - cc_params[0] = alt_cc && *alt_cc ? alt_cc : (u8 *)"clang"; - - } else { - - fprintf(stderr, "Name of the binary: %s\n", argv[0]); - FATAL("Name of the binary is not a known name, expected afl-clang(++)"); - - } - - } else { - - /* With GCJ and Eclipse installed, you can actually compile Java! The - instrumentation will work (amazingly). Alas, unhandled exceptions do - not call abort(), so afl-fuzz would need to be modified to equate - non-zero exit codes with crash conditions when working with Java - binaries. Meh. */ - -#ifdef __APPLE__ - - if (!strcmp(name, "afl-g++")) { - - cc_params[0] = getenv("AFL_CXX"); - - } else if (!strcmp(name, "afl-gcj")) { - - cc_params[0] = getenv("AFL_GCJ"); - - } else if (!strcmp(name, "afl-gcc")) { - - cc_params[0] = getenv("AFL_CC"); - - } else { - - fprintf(stderr, "Name of the binary: %s\n", argv[0]); - FATAL("Name of the binary is not a known name, expected afl-gcc/g++/gcj"); - - } - - if (!cc_params[0]) { - - SAYF("\n" cLRD "[-] " cRST - "On Apple systems, 'gcc' is usually just a wrapper for clang. " - "Please use the\n" - " 'afl-clang' utility instead of 'afl-gcc'. If you really have " - "GCC installed,\n" - " set AFL_CC or AFL_CXX to specify the correct path to that " - "compiler.\n"); - - FATAL("AFL_CC or AFL_CXX required on MacOS X"); - - } - -#else - - if (!strcmp(name, "afl-g++")) { - - u8 *alt_cxx = getenv("AFL_CXX"); - cc_params[0] = alt_cxx && *alt_cxx ? alt_cxx : (u8 *)"g++"; - - } else if (!strcmp(name, "afl-gcj")) { - - u8 *alt_cc = getenv("AFL_GCJ"); - cc_params[0] = alt_cc && *alt_cc ? alt_cc : (u8 *)"gcj"; - - } else if (!strcmp(name, "afl-gcc")) { - - u8 *alt_cc = getenv("AFL_CC"); - cc_params[0] = alt_cc && *alt_cc ? alt_cc : (u8 *)"gcc"; - - } else { - - fprintf(stderr, "Name of the binary: %s\n", argv[0]); - FATAL("Name of the binary is not a known name, expected afl-gcc/g++/gcj"); - - } - -#endif /* __APPLE__ */ - - } - - while (--argc) { - - u8 *cur = *(++argv); - - if (!strncmp(cur, "-B", 2)) { - - if (!be_quiet) { WARNF("-B is already set, overriding"); } - - if (!cur[2] && argc > 1) { - - argc--; - argv++; - - } - - continue; - - } - - if (!strcmp(cur, "-integrated-as")) { continue; } - - if (!strcmp(cur, "-pipe")) { continue; } - -#if defined(__FreeBSD__) && defined(WORD_SIZE_64) - if (!strcmp(cur, "-m32")) m32_set = 1; -#endif - - if (!strcmp(cur, "-fsanitize=address") || - !strcmp(cur, "-fsanitize=memory")) { - - asan_set = 1; - - } - - if (strstr(cur, "FORTIFY_SOURCE")) { fortify_set = 1; } - - cc_params[cc_par_cnt++] = cur; - - } - - cc_params[cc_par_cnt++] = "-B"; - cc_params[cc_par_cnt++] = as_path; - - if (clang_mode) { cc_params[cc_par_cnt++] = "-no-integrated-as"; } - - if (getenv("AFL_HARDEN")) { - - cc_params[cc_par_cnt++] = "-fstack-protector-all"; - - if (!fortify_set) { cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2"; } - - } - - if (asan_set) { - - /* Pass this on to afl-as to adjust map density. */ - - setenv("AFL_USE_ASAN", "1", 1); - - } else if (getenv("AFL_USE_ASAN")) { - - if (getenv("AFL_USE_MSAN")) { - - FATAL("ASAN and MSAN are mutually exclusive"); - - } - - if (getenv("AFL_HARDEN")) { - - FATAL("ASAN and AFL_HARDEN are mutually exclusive"); - - } - - cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; - cc_params[cc_par_cnt++] = "-fsanitize=address"; - - } else if (getenv("AFL_USE_MSAN")) { - - if (getenv("AFL_USE_ASAN")) { - - FATAL("ASAN and MSAN are mutually exclusive"); - - } - - if (getenv("AFL_HARDEN")) { - - FATAL("MSAN and AFL_HARDEN are mutually exclusive"); - - } - - cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; - cc_params[cc_par_cnt++] = "-fsanitize=memory"; - - } - - if (getenv("AFL_USE_UBSAN")) { - - cc_params[cc_par_cnt++] = "-fsanitize=undefined"; - cc_params[cc_par_cnt++] = "-fsanitize-undefined-trap-on-error"; - cc_params[cc_par_cnt++] = "-fno-sanitize-recover=all"; - - } - -#if defined(USEMMAP) && !defined(__HAIKU__) - cc_params[cc_par_cnt++] = "-lrt"; -#endif - - if (!getenv("AFL_DONT_OPTIMIZE")) { - -#if defined(__FreeBSD__) && defined(WORD_SIZE_64) - - /* On 64-bit FreeBSD systems, clang -g -m32 is broken, but -m32 itself - works OK. This has nothing to do with us, but let's avoid triggering - that bug. */ - - if (!clang_mode || !m32_set) cc_params[cc_par_cnt++] = "-g"; - -#else - - cc_params[cc_par_cnt++] = "-g"; - -#endif - - cc_params[cc_par_cnt++] = "-O3"; - cc_params[cc_par_cnt++] = "-funroll-loops"; - - /* Two indicators that you're building for fuzzing; one of them is - AFL-specific, the other is shared with libfuzzer. */ - - cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; - cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; - - } - - if (getenv("AFL_NO_BUILTIN")) { - - cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-bcmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strstr"; - cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr"; - - } - - cc_params[cc_par_cnt] = NULL; - -} - -/* Main entry point */ - -int main(int argc, char **argv) { - - char *env_info = - "Environment variables used by afl-gcc:\n" - "AFL_CC: path to the C compiler to use\n" - "AFL_CXX: path to the C++ compiler to use\n" - "AFL_GCJ: path to the java compiler to use\n" - "AFL_PATH: path to the instrumenting assembler\n" - "AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" - "AFL_NO_BUILTIN: compile for use with libtokencap.so\n" - "AFL_QUIET: suppress verbose output\n" - "AFL_CAL_FAST: speed up the initial calibration\n" - "AFL_HARDEN: adds code hardening to catch memory bugs\n" - "AFL_USE_ASAN: activate address sanitizer\n" - "AFL_USE_MSAN: activate memory sanitizer\n" - "AFL_USE_UBSAN: activate undefined behaviour sanitizer\n" - - "\nEnvironment variables used by afl-as (called by afl-gcc):\n" - "AFL_AS: path to the assembler to use\n" - "TMPDIR: set the directory for temporary files of afl-as\n" - "TEMP: fall back path to directory for temporary files\n" - "TMP: fall back path to directory for temporary files\n" - "AFL_INST_RATIO: percentage of branches to instrument\n" - "AFL_QUIET: suppress verbose output\n" - "AFL_KEEP_ASSEMBLY: leave instrumented assembly files\n" - "AFL_AS_FORCE_INSTRUMENT: force instrumentation for asm sources\n"; - - if (argc == 2 && strncmp(argv[1], "-h", 2) == 0) { - - printf("afl-cc" VERSION " by Michal Zalewski\n\n"); - printf("%s \n\n", argv[0]); - printf("afl-gcc has no command line options\n\n%s\n", env_info); - printf( - "NOTE: afl-gcc is deprecated, llvm_mode is much faster and has more " - "options\n"); - return -1; - - } - - if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { - - SAYF(cCYA "afl-cc" VERSION cRST " by Michal Zalewski\n"); - SAYF(cYEL "[!] " cBRI "NOTE: " cRST - "afl-gcc is deprecated, llvm_mode is much faster and has more " - "options\n"); - - } else { - - be_quiet = 1; - - } - - if (argc < 2) { - - SAYF( - "\n" - "This is a helper application for afl-fuzz. It serves as a drop-in " - "replacement\n" - "for gcc or clang, letting you recompile third-party code with the " - "required\n" - "runtime instrumentation. A common use pattern would be one of the " - "following:\n\n" - - " CC=%s/afl-gcc ./configure\n" - " CXX=%s/afl-g++ ./configure\n\n%s" - - , - BIN_PATH, BIN_PATH, env_info); - - exit(1); - - } - - u8 *ptr; - if (!be_quiet && - ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE")))) { - - u32 map_size = atoi(ptr); - if (map_size != MAP_SIZE) { - - WARNF("AFL_MAP_SIZE is not supported by afl-gcc"); - - } - - } - - find_as(argv[0]); - - edit_params(argc, argv); - - execvp(cc_params[0], (char **)cc_params); - - FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]); - - return 0; - -} - diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c new file mode 100644 index 00000000..771e2d0d --- /dev/null +++ b/src/afl-ld-lto.c @@ -0,0 +1,358 @@ +/* + american fuzzy lop++ - wrapper for llvm 11+ lld + ----------------------------------------------- + + Written by Marc Heuse for afl++ + + Maintained by Marc Heuse , + Heiko Eißfeldt + Andrea Fioraldi + Dominik Maier + + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + The sole purpose of this wrapper is to preprocess clang LTO files when + linking with lld and performing the instrumentation on the whole program. + +*/ + +#define AFL_MAIN +#define _GNU_SOURCE + +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define MAX_PARAM_COUNT 4096 + +static u8 **ld_params; /* Parameters passed to the real 'ld' */ + +static u8 *afl_path = AFL_PATH; +static u8 *real_ld = AFL_REAL_LD; + +static u8 be_quiet, /* Quiet mode (no stderr output) */ + debug, /* AFL_DEBUG */ + passthrough, /* AFL_LD_PASSTHROUGH - no link+optimize*/ + just_version; /* Just show version? */ + +static u32 ld_param_cnt = 1; /* Number of params to 'ld' */ + +/* Examine and modify parameters to pass to 'ld', 'llvm-link' and 'llmv-ar'. + Note that the file name is always the last parameter passed by GCC, + so we exploit this property to keep the code "simple". */ +static void edit_params(int argc, char **argv) { + + u32 i, instrim = 0, gold_pos = 0, gold_present = 0, rt_present = 0, + rt_lto_present = 0, inst_present = 0; + char *ptr; + + ld_params = ck_alloc(4096 * sizeof(u8 *)); + + ld_params[0] = (u8 *)real_ld; + + if (!passthrough) { + + for (i = 1; i < argc; i++) { + + if (strstr(argv[i], "/afl-llvm-rt-lto.o") != NULL) rt_lto_present = 1; + if (strstr(argv[i], "/afl-llvm-rt.o") != NULL) rt_present = 1; + if (strstr(argv[i], "/afl-llvm-lto-instr") != NULL) inst_present = 1; + + } + + for (i = 1; i < argc && !gold_pos; i++) { + + if (strcmp(argv[i], "-plugin") == 0) { + + if (strncmp(argv[i], "-plugin=", strlen("-plugin=")) == 0) { + + if (strcasestr(argv[i], "LLVMgold.so") != NULL) + gold_present = gold_pos = i + 1; + + } else if (i < argc && strcasestr(argv[i + 1], "LLVMgold.so") != NULL) { + + gold_present = gold_pos = i + 2; + + } + + } + + } + + if (!gold_pos) { + + for (i = 1; i + 1 < argc && !gold_pos; i++) { + + if (argv[i][0] != '-') { + + if (argv[i - 1][0] == '-') { + + switch (argv[i - 1][1]) { + + case 'b': + break; + case 'd': + break; + case 'e': + break; + case 'F': + break; + case 'f': + break; + case 'I': + break; + case 'l': + break; + case 'L': + break; + case 'm': + break; + case 'o': + break; + case 'O': + break; + case 'p': + if (index(argv[i - 1], '=') == NULL) gold_pos = i; + break; + case 'R': + break; + case 'T': + break; + case 'u': + break; + case 'y': + break; + case 'z': + break; + case '-': { + + if (strcmp(argv[i - 1], "--oformat") == 0) break; + if (strcmp(argv[i - 1], "--output") == 0) break; + if (strncmp(argv[i - 1], "--opt-remarks-", 14) == 0) break; + gold_pos = i; + break; + + } + + default: + gold_pos = i; + + } + + } else + + gold_pos = i; + + } + + } + + } + + if (!gold_pos) gold_pos = 1; + + } + + if (getenv("AFL_LLVM_INSTRIM")) + instrim = 1; + else if ((ptr = getenv("AFL_LLVM_INSTRUMENT")) && + (strcasestr(ptr, "CFG") == 0 || strcasestr(ptr, "INSTRIM") == 0)) + instrim = 1; + + if (debug) + SAYF(cMGN "[D] " cRST + "passthrough=%s instrim=%d, gold_pos=%d, gold_present=%s " + "inst_present=%s rt_present=%s rt_lto_present=%s\n", + passthrough ? "true" : "false", instrim, gold_pos, + gold_present ? "true" : "false", inst_present ? "true" : "false", + rt_present ? "true" : "false", rt_lto_present ? "true" : "false"); + + for (i = 1; i < argc; i++) { + + if (ld_param_cnt >= MAX_PARAM_COUNT) + FATAL( + "Too many command line parameters because of unpacking .a archives, " + "this would need to be done by hand ... sorry! :-("); + + if (strcmp(argv[i], "--afl") == 0) { + + if (!be_quiet) OKF("afl++ test command line flag detected, exiting."); + exit(0); + + } + + if (i == gold_pos && !passthrough) { + + ld_params[ld_param_cnt++] = alloc_printf("-L%s/../lib", LLVM_BINDIR); + + if (!gold_present) { + + ld_params[ld_param_cnt++] = "-plugin"; + ld_params[ld_param_cnt++] = + alloc_printf("%s/../lib/LLVMgold.so", LLVM_BINDIR); + + } + + ld_params[ld_param_cnt++] = "--allow-multiple-definition"; + + if (!inst_present) { + + if (instrim) + ld_params[ld_param_cnt++] = + alloc_printf("-mllvm=-load=%s/afl-llvm-lto-instrim.so", afl_path); + else + ld_params[ld_param_cnt++] = alloc_printf( + "-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", afl_path); + + } + + if (!rt_present) + ld_params[ld_param_cnt++] = alloc_printf("%s/afl-llvm-rt.o", afl_path); + if (!rt_lto_present) + ld_params[ld_param_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto.o", afl_path); + + } + + ld_params[ld_param_cnt++] = argv[i]; + + } + + ld_params[ld_param_cnt] = NULL; + +} + +/* Main entry point */ + +int main(int argc, char **argv) { + + s32 pid, i, status; + u8 * ptr; + char thecwd[PATH_MAX]; + + if ((ptr = getenv("AFL_LD_CALLER")) != NULL) { + + FATAL("ld loop detected! Set AFL_REAL_LD!\n"); + + } + + if (isatty(2) && !getenv("AFL_QUIET") && !getenv("AFL_DEBUG")) { + + SAYF(cCYA "afl-ld-to" VERSION cRST + " by Marc \"vanHauser\" Heuse \n"); + + } else + + be_quiet = 1; + + if (getenv("AFL_DEBUG") != NULL) debug = 1; + if (getenv("AFL_PATH") != NULL) afl_path = getenv("AFL_PATH"); + if (getenv("AFL_LD_PASSTHROUGH") != NULL) passthrough = 1; + if (getenv("AFL_REAL_LD") != NULL) real_ld = getenv("AFL_REAL_LD"); + + if (!afl_path || !*afl_path) afl_path = "/usr/local/lib/afl"; + + setenv("AFL_LD_CALLER", "1", 1); + + if (debug) { + + if (getcwd(thecwd, sizeof(thecwd)) != 0) strcpy(thecwd, "."); + + SAYF(cMGN "[D] " cRST "cd \"%s\";", thecwd); + for (i = 0; i < argc; i++) + SAYF(" \"%s\"", argv[i]); + SAYF("\n"); + + } + + if (argc < 2) { + + SAYF( + "\n" + "This is a helper application for afl-clang-lto. It is a wrapper " + "around GNU " + "llvm's 'lld',\n" + "executed by the toolchain whenever using " + "afl-clang-lto/afl-clang-lto++.\n" + "You probably don't want to run this program directly but rather pass " + "it as LD parameter to configure scripts\n\n" + + "Environment variables:\n" + " AFL_LD_PASSTHROUGH do not link+optimize == no instrumentation\n" + " AFL_REAL_LD point to the real llvm 11 lld if necessary\n" + + "\nafl-ld-to was compiled with the fixed real 'ld' of %s and the " + "binary path of %s\n\n", + real_ld, LLVM_BINDIR); + + exit(1); + + } + + edit_params(argc, argv); // here most of the magic happens :-) + + if (debug) { + + SAYF(cMGN "[D]" cRST " cd \"%s\";", thecwd); + for (i = 0; i < ld_param_cnt; i++) + SAYF(" \"%s\"", ld_params[i]); + SAYF("\n"); + + } + + if (!(pid = fork())) { + + if (strlen(real_ld) > 1) execvp(real_ld, (char **)ld_params); + execvp("ld", (char **)ld_params); // fallback + FATAL("Oops, failed to execute 'ld' - check your PATH"); + + } + + if (pid < 0) PFATAL("fork() failed"); + + if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed"); + if (debug) SAYF(cMGN "[D] " cRST "linker result: %d\n", status); + + if (!just_version) { + + if (status == 0) { + + if (!be_quiet) OKF("Linker was successful"); + + } else { + + SAYF(cLRD "[-] " cRST + "Linker failed, please investigate and send a bug report. Most " + "likely an 'ld' option is incompatible with %s.\n", + AFL_CLANG_FLTO); + + } + + } + + exit(WEXITSTATUS(status)); + +} + -- cgit 1.4.1 From 4b3ad5f037ee9a36aa057bf55a69acca1f573922 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 5 Sep 2020 12:32:10 +0200 Subject: add cull queue, -i subdir traversal --- GNUmakefile | 230 ++++++++++++++++++++++++-------------------------- GNUmakefile.llvm | 7 +- README.md | 179 +++++++++++++++++++++++---------------- include/afl-fuzz.h | 6 +- src/afl-fuzz-extras.c | 113 ++++++++++++++++++++----- src/afl-fuzz-init.c | 108 +++++++++++++++++++++--- src/afl-fuzz-one.c | 31 +------ src/afl-fuzz-queue.c | 7 -- src/afl-fuzz.c | 33 +++++--- 9 files changed, 437 insertions(+), 277 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index e1f6da95..0046a481 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -24,30 +24,31 @@ BIN_PATH = $(PREFIX)/bin HELPER_PATH = $(PREFIX)/lib/afl DOC_PATH = $(PREFIX)/share/doc/afl MISC_PATH = $(PREFIX)/share/afl -MAN_PATH = $(PREFIX)/share/man/man8 +MAN_PATH = $(PREFIX)/man/man8 PROGNAME = afl VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2) # PROGS intentionally omit afl-as, which gets installed elsewhere. -PROGS = afl-gcc afl-g++ afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze +PROGS = afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze SH_PROGS = afl-plot afl-cmin afl-cmin.bash afl-whatsup afl-system-config MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8) afl-as.8 +ASAN_OPTIONS=detect_leaks=0 ifeq "$(findstring android, $(shell $(CC) --version 2>/dev/null))" "" - ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" +ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" CFLAGS_FLTO ?= -flto=full - else - ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" +else + ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" CFLAGS_FLTO ?= -flto=thin - else - ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + else + ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" CFLAGS_FLTO ?= -flto - endif endif endif endif +endif ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -fno-move-loop-invariants -fdisable-tree-cunrolli -x c - -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" SPECIAL_PERFORMANCE += -fno-move-loop-invariants -fdisable-tree-cunrolli @@ -61,10 +62,7 @@ ifneq "$(shell uname)" "Darwin" endif endif # OS X does not like _FORTIFY_SOURCE=2 - # _FORTIFY_SOURCE=2 does not like -O0 - ifndef DEBUG - CFLAGS_OPT += -D_FORTIFY_SOURCE=2 - endif + CFLAGS_OPT += -D_FORTIFY_SOURCE=2 endif ifeq "$(shell uname)" "SunOS" @@ -206,10 +204,7 @@ else endif ifneq "$(filter Linux GNU%,$(shell uname))" "" - # _FORTIFY_SOURCE=2 does not like -O0 - ifndef DEBUG override CFLAGS += -D_FORTIFY_SOURCE=2 - endif LDFLAGS += -ldl -lrt endif @@ -223,11 +218,7 @@ ifneq "$(findstring NetBSD, $(shell uname))" "" LDFLAGS += -lpthread endif -ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" - TEST_CC = afl-gcc -else - TEST_CC = afl-clang -endif +TEST_CC = afl-gcc COMM_HDR = include/alloc-inl.h include/config.h include/debug.h include/types.h @@ -277,28 +268,47 @@ ifdef TEST_MMAP LDFLAGS += -Wno-deprecated-declarations endif -all: test_x86 test_shm test_python ready $(PROGS) afl-as test_build all_done +.PHONY: all +all: test_x86 test_shm test_python ready $(PROGS) afl-as llvm gcc_plugin test_build all_done + +.PHONY: llvm +llvm: + -$(MAKE) -f GNUmakefile.llvm + @test -e afl-cc || { echo "[-] Compiling afl-cc failed. You seem not to have a working compiler." ; exit 1; } -man: afl-gcc all $(MANPAGES) +.PHONY: gcc_plugin +gcc_plugin: + -$(MAKE) -f GNUmakefile.gcc_plugin +.PHONY: man +man: $(MANPAGES) + +.PHONY: test +test: tests + +.PHONY: tests tests: source-only @cd test ; ./test-all.sh @rm -f test/errors +.PHONY: performance-tests performance-tests: performance-test +.PHONY: test-performance test-performance: performance-test +.PHONY: performance-test performance-test: source-only @cd test ; ./test-performance.sh # hint: make targets are also listed in the top level README.md +.PHONY: help help: @echo "HELP --- the following make targets exist:" @echo "==========================================" @echo "all: just the main afl++ binaries" @echo "binary-only: everything for binary-only fuzzing: qemu_mode, unicorn_mode, libdislocator, libtokencap" - @echo "source-only: everything for source code fuzzing: llvm_mode, gcc_plugin, libdislocator, libtokencap" + @echo "source-only: everything for source code fuzzing: gcc_plugin, libdislocator, libtokencap" @echo "distrib: everything (for both binary-only and source code fuzzing)" @echo "man: creates simple man pages from the help option of the programs" @echo "install: installs everything you have compiled with the build option above" @@ -322,8 +332,8 @@ help: @echo "==========================================" @echo e.g.: make ASAN_BUILD=1 +.PHONY: test_x86 ifndef AFL_NO_X86 - test_x86: @echo "[*] Checking for the default compiler cc..." @type $(CC) >/dev/null || ( echo; echo "Oops, looks like there is no compiler '"$(CC)"' in your path."; echo; echo "Don't panic! You can restart with '"$(_)" CC='."; echo; exit 1 ) @@ -332,148 +342,129 @@ test_x86: @echo "[*] Checking for the ability to compile x86 code..." @echo 'main() { __asm__("xorb %al, %al"); }' | $(CC) $(CFLAGS) -w -x c - -o .test1 || ( echo; echo "Oops, looks like your compiler can't generate x86 code."; echo; echo "Don't panic! You can use the LLVM or QEMU mode, but see docs/INSTALL first."; echo "(To ignore this error, set AFL_NO_X86=1 and try again.)"; echo; exit 1 ) @rm -f .test1 - else - test_x86: @echo "[!] Note: skipping x86 compilation checks (AFL_NO_X86 set)." - endif - +.PHONY: test_shm ifeq "$(SHMAT_OK)" "1" - test_shm: @echo "[+] shmat seems to be working." @rm -f .test2 - else - test_shm: @echo "[-] shmat seems not to be working, switching to mmap implementation" - endif - +.PHONY: test_python ifeq "$(PYTHON_OK)" "1" - test_python: @rm -f .test 2> /dev/null @echo "[+] $(PYTHON_VERSION) support seems to be working." - else - test_python: @echo "[-] You seem to need to install the package python3-dev, python2-dev or python-dev (and perhaps python[23]-apt), but it is optional so we continue" - endif - +.PHONY: ready ready: @echo "[+] Everything seems to be working, ready to compile." -afl-g++: afl-gcc - -afl-gcc: src/afl-gcc.c $(COMM_HDR) | test_x86 - $(CC) $(CFLAGS) $(CPPFLAGS) src/$@.c -o $@ $(LDFLAGS) - set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $$i; done - afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86 - $(CC) $(CFLAGS) $(CPPFLAGS) src/$@.c -o $@ $(LDFLAGS) - ln -sf afl-as as + $(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS) + @ln -sf afl-as as src/afl-performance.o : $(COMM_HDR) src/afl-performance.c include/hash.h - $(CC) $(CFLAGS) $(CPPFLAGS) -Iinclude $(SPECIAL_PERFORMANCE) -O3 -fno-unroll-loops -c src/afl-performance.c -o src/afl-performance.o + $(CC) -Iinclude $(SPECIAL_PERFORMANCE) -O3 -fno-unroll-loops -c src/afl-performance.c -o src/afl-performance.o src/afl-common.o : $(COMM_HDR) src/afl-common.c include/common.h - $(CC) $(CFLAGS) $(CFLAGS_FLTO) $(CPPFLAGS) -c src/afl-common.c -o src/afl-common.o + $(CC) $(CFLAGS) $(CFLAGS_FLTO) -c src/afl-common.c -o src/afl-common.o src/afl-forkserver.o : $(COMM_HDR) src/afl-forkserver.c include/forkserver.h - $(CC) $(CFLAGS) $(CFLAGS_FLTO) $(CPPFLAGS) -c src/afl-forkserver.c -o src/afl-forkserver.o + $(CC) $(CFLAGS) $(CFLAGS_FLTO) -c src/afl-forkserver.c -o src/afl-forkserver.o src/afl-sharedmem.o : $(COMM_HDR) src/afl-sharedmem.c include/sharedmem.h - $(CC) $(CFLAGS) $(CFLAGS_FLTO) $(CPPFLAGS) -c src/afl-sharedmem.c -o src/afl-sharedmem.o + $(CC) $(CFLAGS) $(CFLAGS_FLTO) -c src/afl-sharedmem.c -o src/afl-sharedmem.o afl-fuzz: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o | test_x86 - $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) $(CPPFLAGS) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(LDFLAGS) + $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(LDFLAGS) afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86 - $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(CPPFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(LDFLAGS) + $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(LDFLAGS) afl-tmin: src/afl-tmin.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86 - $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(CPPFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS) + $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS) afl-analyze: src/afl-analyze.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o $(COMM_HDR) | test_x86 - $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(CPPFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o -o $@ $(LDFLAGS) + $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o -o $@ $(LDFLAGS) afl-gotcpu: src/afl-gotcpu.c src/afl-common.o $(COMM_HDR) | test_x86 - $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(CPPFLAGS) src/$@.c src/afl-common.o -o $@ $(LDFLAGS) + $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o -o $@ $(LDFLAGS) +.PHONY: document +document: afl-fuzz-document # document all mutations and only do one run (use with only one input file!) -document: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-performance.o | test_x86 - $(CC) -D_DEBUG=\"1\" -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) $(CPPFLAGS) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.c src/afl-performance.o -o afl-fuzz-document $(PYFLAGS) $(LDFLAGS) +afl-fuzz-document: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-performance.o | test_x86 + $(CC) -D_DEBUG=\"1\" -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.c src/afl-performance.o -o afl-fuzz-document $(PYFLAGS) $(LDFLAGS) test/unittests/unit_maybe_alloc.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_maybe_alloc.c $(AFL_FUZZ_FILES) - @$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -c test/unittests/unit_maybe_alloc.c -o test/unittests/unit_maybe_alloc.o + @$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_maybe_alloc.c -o test/unittests/unit_maybe_alloc.o unit_maybe_alloc: test/unittests/unit_maybe_alloc.o - @$(CC) $(CFLAGS) $(CPPFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_maybe_alloc.o -o test/unittests/unit_maybe_alloc $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka + @$(CC) $(CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_maybe_alloc.o -o test/unittests/unit_maybe_alloc $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka ./test/unittests/unit_maybe_alloc test/unittests/unit_hash.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_hash.c $(AFL_FUZZ_FILES) src/afl-performance.o - @$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -c test/unittests/unit_hash.c -o test/unittests/unit_hash.o + @$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_hash.c -o test/unittests/unit_hash.o unit_hash: test/unittests/unit_hash.o src/afl-performance.o - @$(CC) $(CFLAGS) $(CPPFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf $^ -o test/unittests/unit_hash $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka + @$(CC) $(CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf $^ -o test/unittests/unit_hash $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka ./test/unittests/unit_hash test/unittests/unit_rand.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_rand.c $(AFL_FUZZ_FILES) src/afl-performance.o - @$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -c test/unittests/unit_rand.c -o test/unittests/unit_rand.o + @$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_rand.c -o test/unittests/unit_rand.o unit_rand: test/unittests/unit_rand.o src/afl-common.o src/afl-performance.o - @$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf $^ -o test/unittests/unit_rand $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka + @$(CC) $(CFLAGS) $(ASAN_CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf $^ -o test/unittests/unit_rand $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka ./test/unittests/unit_rand test/unittests/unit_list.o : $(COMM_HDR) include/list.h test/unittests/unit_list.c $(AFL_FUZZ_FILES) - @$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -c test/unittests/unit_list.c -o test/unittests/unit_list.o + @$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_list.c -o test/unittests/unit_list.o unit_list: test/unittests/unit_list.o - @$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_list.o -o test/unittests/unit_list $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka + @$(CC) $(CFLAGS) $(ASAN_CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_list.o -o test/unittests/unit_list $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka ./test/unittests/unit_list test/unittests/unit_preallocable.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_preallocable.c $(AFL_FUZZ_FILES) - @$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -c test/unittests/unit_preallocable.c -o test/unittests/unit_preallocable.o + @$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_preallocable.c -o test/unittests/unit_preallocable.o unit_preallocable: test/unittests/unit_preallocable.o - @$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_preallocable.o -o test/unittests/unit_preallocable $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka + @$(CC) $(CFLAGS) $(ASAN_CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_preallocable.o -o test/unittests/unit_preallocable $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka ./test/unittests/unit_preallocable +.PHONY: unit_clean unit_clean: @rm -f ./test/unittests/unit_preallocable ./test/unittests/unit_list ./test/unittests/unit_maybe_alloc test/unittests/*.o +.PHONY: unit ifneq "$(shell uname)" "Darwin" - -unit: unit_maybe_alloc unit_preallocable unit_list unit_clean unit_rand unit_hash - +unit: unit_maybe_alloc unit_preallocable unit_list unit_clean unit_rand unit_hash else - unit: @echo [-] unit tests are skipped on Darwin \(lacks GNU linker feature --wrap\) - endif +.PHONY: code-format code-format: ./.custom-format.py -i src/*.c ./.custom-format.py -i include/*.h ./.custom-format.py -i libdislocator/*.c ./.custom-format.py -i libtokencap/*.c - ./.custom-format.py -i llvm_mode/*.c - ./.custom-format.py -i llvm_mode/*.h - ./.custom-format.py -i llvm_mode/*.cc - ./.custom-format.py -i gcc_plugin/*.c - @#./.custom-format.py -i gcc_plugin/*.h - ./.custom-format.py -i gcc_plugin/*.cc + ./.custom-format.py -i instrumentation/*.h + ./.custom-format.py -i instrumentation/*.cc + ./.custom-format.py -i instrumentation/*.c ./.custom-format.py -i custom_mutators/*/*.c @#./.custom-format.py -i custom_mutators/*/*.h # destroys input.h :-( ./.custom-format.py -i examples/*/*.c @@ -489,38 +480,40 @@ code-format: ./.custom-format.py -i *.c +.PHONY: test_build ifndef AFL_NO_X86 - -test_build: afl-gcc afl-as afl-showmap +test_build: afl-cc afl-as afl-showmap @echo "[*] Testing the CC wrapper and instrumentation output..." - @unset AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_DEBUG=1 AFL_INST_RATIO=100 AFL_AS_FORCE_INSTRUMENT=1 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS) 2>&1 | grep 'afl-as' >/dev/null || (echo "Oops, afl-as did not get called from "$(TEST_CC)". This is normally achieved by "$(CC)" honoring the -B option."; exit 1 ) + @unset AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_DEBUG=1 AFL_INST_RATIO=100 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS) 2>&1 | grep 'afl-as' >/dev/null || (echo "Oops, afl-as did not get called from "$(TEST_CC)". This is normally achieved by "$(CC)" honoring the -B option."; exit 1 ) ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr @rm -f test-instr @cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi + @echo @echo "[+] All right, the instrumentation seems to be working!" - else - -test_build: afl-gcc afl-as afl-showmap +test_build: afl-cc afl-as afl-showmap @echo "[!] Note: skipping build tests (you may need to use LLVM or QEMU mode)." - endif - +.PHONY: all_done all_done: test_build - @if [ ! "`type clang 2>/dev/null`" = "" ]; then echo "[+] LLVM users: see llvm_mode/README.md for a faster alternative to afl-gcc."; fi + @test -e afl-cc && echo "[+] Main compiler 'afl-cc' successfully built!" || { echo "[-] Main compiler 'afl-cc' failed to built, set up a working build environment first!" ; exit 1 ; } + @test -e cmplog-instructions-pass.so && echo "[+] LLVM mode for 'afl-cc' successfully built!" || echo "[-] LLVM mode for 'afl-cc' failed to built, likely you either have not llvm installed or you have not set LLVM_CONFIG pointing to e.g. llvm-config-11. See instrumenation/README.llvm.md how to do this. Highly recommended!" + @test -e SanitizerCoverageLTO.so && echo "[+] LLVM LTO mode for 'afl-cc' successfully built!" || echo "[-] LLVM LTO mode for 'afl-cc' failed to built, this would need LLVM 11+, see instrumentation/README.lto.md how to build it" + @test -e afl-gcc-pass.so && echo "[+] gcc_plugin for 'afl-cc' successfully built!" || echo "[-] gcc_plugin for 'afl-cc' failed to built, unless you really need it that is fine - or read instrumentation/README.gcc_plugin.md how to build it" @echo "[+] All done! Be sure to review the README.md - it's pretty short and useful." @if [ "`uname`" = "Darwin" ]; then printf "\nWARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of\nfork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox\n(virtualbox.org) to put AFL inside a Linux or *BSD VM.\n\n"; fi @! tty <&1 >/dev/null || printf "\033[0;30mNOTE: If you can read this, your terminal probably uses white background.\nThis will make the UI hard to read. See docs/status_screen.md for advice.\033[0m\n" 2>/dev/null .NOTPARALLEL: clean all +.PHONY: clean clean: - rm -f $(PROGS) libradamsa.so afl-fuzz-document afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast ld *.so *.8 test/unittests/*.o test/unittests/unit_maybe_alloc test/unittests/preallocable .afl-* + rm -f $(PROGS) libradamsa.so afl-fuzz-document afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-g++-fast ld *.so *.8 test/unittests/*.o test/unittests/unit_maybe_alloc test/unittests/preallocable .afl-* afl-gcc afl-g++ rm -rf out_dir qemu_mode/qemu-3.1.1 *.dSYM */*.dSYM - -$(MAKE) -C llvm_mode clean - -$(MAKE) -C gcc_plugin clean + -$(MAKE) -f GNUmakefile.llvm clean + -$(MAKE) -f GNUmakefile.gcc_plugin clean $(MAKE) -C libdislocator clean $(MAKE) -C libtokencap clean $(MAKE) -C examples/afl_network_proxy clean @@ -530,20 +523,22 @@ clean: $(MAKE) -C qemu_mode/libcompcov clean rm -rf qemu_mode/qemu-3.1.1 ifeq "$(IN_REPO)" "1" - test -e unicorn_mode/unicornafl/Makefile && $(MAKE) -C unicorn_mode/unicornafl clean || true + test -d unicorn_mode/unicornafl && $(MAKE) -C unicorn_mode/unicornafl clean || true else rm -rf qemu_mode/qemu-3.1.1.tar.xz rm -rf unicorn_mode/unicornafl endif +.PHONY: deepclean deepclean: clean rm -rf qemu_mode/qemu-3.1.1.tar.xz rm -rf unicorn_mode/unicornafl git reset --hard >/dev/null 2>&1 || true +.PHONY: distrib distrib: all - -$(MAKE) -C llvm_mode - -$(MAKE) -C gcc_plugin + -$(MAKE) -f GNUmakefile.llvm + -$(MAKE) -f GNUmakefile.gcc_plugin $(MAKE) -C libdislocator $(MAKE) -C libtokencap $(MAKE) -C examples/afl_network_proxy @@ -552,6 +547,7 @@ distrib: all -cd qemu_mode && sh ./build_qemu_support.sh cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh +.PHONY: binary-only binary-only: all $(MAKE) -C libdislocator $(MAKE) -C libtokencap @@ -561,9 +557,10 @@ binary-only: all -cd qemu_mode && sh ./build_qemu_support.sh cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh +.PHONY: source-only source-only: all - -$(MAKE) -C llvm_mode - -$(MAKE) -C gcc_plugin + -$(MAKE) -f GNUmakefile.llvm + -$(MAKE) -f GNUmakefile.gcc_plugin $(MAKE) -C libdislocator $(MAKE) -C libtokencap @#$(MAKE) -C examples/afl_network_proxy @@ -573,8 +570,7 @@ source-only: all %.8: % @echo .TH $* 8 $(BUILD_DATE) "afl++" > $@ @echo .SH NAME >> $@ - @printf "%s" ".B $* \- " >> $@ - @./$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> $@ + @echo .B $* >> $@ @echo >> $@ @echo .SH SYNOPSIS >> $@ @./$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> $@ @@ -590,30 +586,28 @@ source-only: all @echo .SH LICENSE >> $@ @echo Apache License Version 2.0, January 2004 >> $@ +.PHONY: install install: all $(MANPAGES) - install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH) - rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh + @install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH) + @rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh + @rm -f $${DESTDIR}$(BIN_PATH)/afl-as install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH) - rm -f $${DESTDIR}$(BIN_PATH)/afl-as - if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi - if [ -f afl-gcc-fast ]; then set e; install -m 755 afl-gcc-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-gcc-fast $${DESTDIR}$(BIN_PATH)/afl-g++-fast; install -m 755 afl-gcc-pass.so afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH); fi - if [ -f afl-clang-fast ]; then $(MAKE) -C llvm_mode install; fi - if [ -f libdislocator.so ]; then set -e; install -m 755 libdislocator.so $${DESTDIR}$(HELPER_PATH); fi - if [ -f libtokencap.so ]; then set -e; install -m 755 libtokencap.so $${DESTDIR}$(HELPER_PATH); fi - if [ -f libcompcov.so ]; then set -e; install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH); fi - if [ -f afl-fuzz-document ]; then set -e; install -m 755 afl-fuzz-document $${DESTDIR}$(BIN_PATH); fi - if [ -f socketfuzz32.so -o -f socketfuzz64.so ]; then $(MAKE) -C examples/socket_fuzzing install; fi - if [ -f argvfuzz32.so -o -f argvfuzz64.so ]; then $(MAKE) -C examples/argv_fuzzing install; fi - if [ -f examples/afl_network_proxy/afl-network-server ]; then $(MAKE) -C examples/afl_network_proxy install; fi - if [ -f libAFLDriver.a ]; then install -m 644 libAFLDriver.a $${DESTDIR}$(HELPER_PATH); fi - if [ -f libAFLQemuDriver.a ]; then install -m 644 libAFLQemuDriver.a $${DESTDIR}$(HELPER_PATH); fi - - set -e; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-g++ - set -e; if [ -f afl-clang-fast ] ; then ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang++ ; else ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang++; fi - - mkdir -m 0755 -p ${DESTDIR}$(MAN_PATH) + @if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi + @if [ -f libdislocator.so ]; then set -e; install -m 755 libdislocator.so $${DESTDIR}$(HELPER_PATH); fi + @if [ -f libtokencap.so ]; then set -e; install -m 755 libtokencap.so $${DESTDIR}$(HELPER_PATH); fi + @if [ -f libcompcov.so ]; then set -e; install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH); fi + @if [ -f afl-fuzz-document ]; then set -e; install -m 755 afl-fuzz-document $${DESTDIR}$(BIN_PATH); fi + @if [ -f socketfuzz32.so -o -f socketfuzz64.so ]; then $(MAKE) -C examples/socket_fuzzing install; fi + @if [ -f argvfuzz32.so -o -f argvfuzz64.so ]; then $(MAKE) -C examples/argv_fuzzing install; fi + @if [ -f examples/afl_network_proxy/afl-network-server ]; then $(MAKE) -C examples/afl_network_proxy install; fi + @if [ -f examples/aflpp_driver/libAFLDriver.a ]; then install -m 644 examples/aflpp_driver/libAFLDriver.a $${DESTDIR}$(HELPER_PATH); fi + @if [ -f examples/aflpp_driver/libAFLQemuDriver.a ]; then install -m 644 examples/aflpp_driver/libAFLQemuDriver.a $${DESTDIR}$(HELPER_PATH); fi + -$(MAKE) -f GNUmakefile.llvm install + -$(MAKE) -f GNUmakefile.gcc_plugin install + ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-gcc + ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-g++ + @mkdir -m 0755 -p ${DESTDIR}$(MAN_PATH) install -m0644 *.8 ${DESTDIR}$(MAN_PATH) - install -m 755 afl-as $${DESTDIR}$(HELPER_PATH) ln -sf afl-as $${DESTDIR}$(HELPER_PATH)/as install -m 644 docs/*.md $${DESTDIR}$(DOC_PATH) diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index d4502319..d76e0b28 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -419,17 +419,14 @@ document: ./afl-compiler-rt.o: instrumentation/afl-compiler-rt.o.c | test_deps $(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -fPIC -c $< -o $@ - ln -sf ./afl-compiler-rt.o ./afl-llvm-rt.o ./afl-compiler-rt-32.o: instrumentation/afl-compiler-rt.o.c | test_deps @printf "[*] Building 32-bit variant of the runtime (-m32)... " @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi - @test -e ./afl-compiler-rt-32.o && ln -sf ./afl-compiler-rt-32.o ./afl-llvm-rt-32.o ./afl-compiler-rt-64.o: instrumentation/afl-compiler-rt.o.c | test_deps @printf "[*] Building 64-bit variant of the runtime (-m64)... " @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi - @test -e ./afl-compiler-rt-64.o && ln -sf ./afl-compiler-rt-64.o ./afl-llvm-rt-64.o .PHONY: test_build test_build: $(PROGS) @@ -454,8 +451,8 @@ install: all @if [ -f ./afl-compiler-rt.o ]; then set -e; install -m 755 ./afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH); fi @if [ -f ./afl-lto ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto++; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ./afl-llvm-lto-instrumentation.so ./afl-llvm-rt-lto*.o ./afl-llvm-lto-instrumentlist.so $${DESTDIR}$(HELPER_PATH); fi @if [ -f ./afl-ld-lto ]; then set -e; install -m 755 ./afl-ld-lto $${DESTDIR}$(BIN_PATH); fi - @if [ -f ./afl-compiler-rt-32.o ]; then set -e; install -m 755 ./afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-32.o; fi - @if [ -f ./afl-compiler-rt-64.o ]; then set -e; install -m 755 ./afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-64.o; fi + @if [ -f ./afl-compiler-rt-32.o ]; then set -e; install -m 755 ./afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH); fi + @if [ -f ./afl-compiler-rt-64.o ]; then set -e; install -m 755 ./afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH); fi @if [ -f ./compare-transform-pass.so ]; then set -e; install -m 755 ./*.so $${DESTDIR}$(HELPER_PATH); fi @if [ -f ./compare-transform-pass.so ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-fast ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang-fast++ ; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang++ ; fi @if [ -f ./SanitizerCoverageLTO.so ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang-lto++ ; fi diff --git a/README.md b/README.md index 4cad6b47..96b34260 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ ![Travis State](https://api.travis-ci.com/AFLplusplus/AFLplusplus.svg?branch=stable) - Release Version: [2.68c](https://github.com/AFLplusplus/AFLplusplus/releases) + Release Version: [2.67c](https://github.com/AFLplusplus/AFLplusplus/releases) - Github Version: 3.00a + Github Version: 2.67d Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) @@ -22,6 +22,26 @@ afl++ is a superior fork to Google's afl - more speed, more and better mutations, more and better instrumentation, custom module support, etc. +## Major changes in afl++ 3.0 + +With afl++ 3.0 we introduced changes that break some previous afl and afl++ +behaviours: + + * There are no llvm_mode and gcc_plugin subdirectories anymore and there is + only one compiler: afl-cc. All previous compilers now symlink to this one + compiler. All instrumentation source code is now in the `instrumentation/` + folder. + * qemu_mode got upgraded to QEMU 5.1, but to be able to build this a current + ninja build tool version and python3 setuptools are required. + qemu_mode also got new options like snapshotting, instrumenting specific + shared libraries, etc. and QEMU 5.1 supports more CPU targets so this is + worth it. + * When instrumenting targets, afl-cc will not supersede optimizations. This + allows to fuzz targets as same as they are built for debug or release. + * afl-fuzz' `-i` option now descends into subdirectories. + * afl-fuzz will skip over empty dictionaries and too large test cases instead + of failing. + ## Contents 1. [Features](#important-features-of-afl) @@ -39,7 +59,7 @@ with laf-intel and redqueen, unicorn mode, gcc plugin, full *BSD, Solaris and Android support and much, much, much more. - | Feature/Instrumentation | afl-gcc | llvm_mode | gcc_plugin | qemu_mode | unicorn_mode | + | Feature/Instrumentation | afl-gcc | llvm | gcc_plugin | qemu_mode | unicorn_mode | | -------------------------|:-------:|:---------:|:----------:|:----------------:|:------------:| | NeverZero | x86[_64]| x(1) | (2) | x | x | | Persistent Mode | | x | x | x86[_64]/arm[64] | x | @@ -47,9 +67,8 @@ | CmpLog | | x | | x86[_64]/arm[64] | | | Selective Instrumentation| | x | x | (x)(3) | | | Non-Colliding Coverage | | x(4) | | (x)(5) | | - | InsTrim | | x | | | | | Ngram prev_loc Coverage | | x(6) | | | | - | Context Coverage | | x | | | | + | Context Coverage | | x(6) | | | | | Auto Dictionary | | x(7) | | | | | Snapshot LKM Support | | x | | (x)(5) | | @@ -59,11 +78,11 @@ 4. with pcguard mode and LTO mode for LLVM >= 11 5. upcoming, development in the branch 6. not compatible with LTO instrumentation and needs at least LLVM >= 4.1 - 7. only in LTO mode with LLVM >= 11 + 7. automatic in LTO mode with LLVM >= 11, an extra pass for all LLVM version that writes to a file to use with afl-fuzz' `-x` Among others, the following features and patches have been integrated: - * NeverZero patch for afl-gcc, llvm_mode, qemu_mode and unicorn_mode which prevents a wrapping map value to zero, increases coverage + * NeverZero patch for afl-gcc, instrumentation, qemu_mode and unicorn_mode which prevents a wrapping map value to zero, increases coverage * Persistent mode, deferred forkserver and in-memory fuzzing for qemu_mode * Unicorn mode which allows fuzzing of binaries from completely different platforms (integration provided by domenukk) * The new CmpLog instrumentation for LLVM and QEMU inspired by [Redqueen](https://www.syssec.ruhr-uni-bochum.de/media/emma/veroeffentlichungen/2018/12/17/NDSS19-Redqueen.pdf) @@ -71,10 +90,9 @@ * AFLfast's power schedules by Marcel Böhme: [https://github.com/mboehme/aflfast](https://github.com/mboehme/aflfast) * The MOpt mutator: [https://github.com/puppet-meteor/MOpt-AFL](https://github.com/puppet-meteor/MOpt-AFL) * LLVM mode Ngram coverage by Adrian Herrera [https://github.com/adrianherrera/afl-ngram-pass](https://github.com/adrianherrera/afl-ngram-pass) - * InsTrim, a CFG llvm_mode instrumentation implementation: [https://github.com/csienslab/instrim](https://github.com/csienslab/instrim) * C. Holler's afl-fuzz Python mutator module: [https://github.com/choller/afl](https://github.com/choller/afl) * Custom mutator by a library (instead of Python) by kyakdan - * LAF-Intel/CompCov support for llvm_mode, qemu_mode and unicorn_mode (with enhanced capabilities) + * LAF-Intel/CompCov support for instrumentation, qemu_mode and unicorn_mode (with enhanced capabilities) * Radamsa and honggfuzz mutators (as custom mutators). * QBDI mode to fuzz android native libraries via Quarkslab's [QBDI](https://github.com/QBDI/QBDI) framework * Frida and ptrace mode to fuzz binary-only libraries, etc. @@ -88,7 +106,7 @@ send a mail to . See [docs/QuickStartGuide.md](docs/QuickStartGuide.md) if you don't have time to - read this file. + read this file - however this is not recommended! ## Branches @@ -105,13 +123,14 @@ ## Help wanted -We are happy to be part of [Google Summer of Code 2020](https://summerofcode.withgoogle.com/organizations/5100744400699392/)! :-) +We were happy to be part of [Google Summer of Code 2020](https://summerofcode.withgoogle.com/organizations/5100744400699392/) +and we will try to participate again in 2021! We have several ideas we would like to see in AFL++ to make it even better. However, we already work on so many things that we do not have the time for all the big ideas. -This can be your way to support and contribute to AFL++ - extend it to +This can be your way to support and contribute to AFL++ - extend it to do something cool. We have an idea list in [docs/ideas.md](docs/ideas.md). @@ -132,7 +151,7 @@ This image is automatically generated when a push to the stable repo happens. You will find your target source code in /src in the container. If you want to build afl++ yourself you have many options. -The easiest is to build and install everything: +The easiest choice is to build and install everything: ```shell sudo apt install build-essential libtool-bin python3-dev automake flex bison libglib2.0-dev libpixman-1-dev clang python3-setuptools llvm @@ -142,9 +161,9 @@ sudo make install It is recommended to install the newest available gcc, clang and llvm-dev possible in your distribution! -Note that "make distrib" also builds llvm_mode, qemu_mode, unicorn_mode and +Note that "make distrib" also builds instrumentation, qemu_mode, unicorn_mode and more. If you just want plain afl++ then do "make all", however compiling and -using at least llvm_mode is highly recommended for much better results - +using at least instrumentation is highly recommended for much better results - hence in this case ```shell @@ -156,7 +175,7 @@ These build targets exist: * all: just the main afl++ binaries * binary-only: everything for binary-only fuzzing: qemu_mode, unicorn_mode, libdislocator, libtokencap -* source-only: everything for source code fuzzing: llvm_mode, libdislocator, libtokencap +* source-only: everything for source code fuzzing: instrumentation, libdislocator, libtokencap * distrib: everything (for both binary-only and source code fuzzing) * man: creates simple man pages from the help option of the programs * install: installs everything you have compiled with the build options above @@ -212,18 +231,19 @@ If you have a binary-only target please skip to [#Instrumenting binary-only apps Fuzzing source code is a three-step process. -1. compile the target with a special compiler that prepares the target to be +1. Compile the target with a special compiler that prepares the target to be fuzzed efficiently. This step is called "instrumenting a target". 2. Prepare the fuzzing by selecting and optimizing the input corpus for the target. -3. perform the fuzzing of the target by randomly mutating input and assessing +3. Perform the fuzzing of the target by randomly mutating input and assessing if a generated input was processed in a new path in the target binary. ### 1. Instrumenting that target #### a) Selecting the best afl++ compiler for instrumenting the target -afl++ comes with different compilers and instrumentation options. +afl++ comes with a central compiler `afl-cc` that incorporates various different +kinds of compiler targets and and instrumentation options. The following evaluation flow will help you to select the best possible. It is highly recommended to have the newest llvm version possible installed, @@ -231,49 +251,62 @@ anything below 9 is not recommended. ``` +--------------------------------+ -| clang/clang++ 11+ is available | --> use afl-clang-lto and afl-clang-lto++ -+--------------------------------+ see [llvm/README.lto.md](llvm/README.lto.md) +| clang/clang++ 11+ is available | --> use LTO mode (afl-clang-lto/afl-clang-lto++) ++--------------------------------+ see [instrumentation/README.lto.md](instrumentation/README.lto.md) | - | if not, or if the target fails with afl-clang-lto/++ + | if not, or if the target fails with LTO afl-clang-lto/++ | v +---------------------------------+ -| clang/clang++ 3.3+ is available | --> use afl-clang-fast and afl-clang-fast++ -+---------------------------------+ see [llvm/README.md](llvm/README.md) +| clang/clang++ 3.3+ is available | --> use LLVM mode (afl-clang-fast/afl-clang-fast++) ++---------------------------------+ see [instrumentation/README.md](instrumentation/README.md) | - | if not, or if the target fails with afl-clang-fast/++ + | if not, or if the target fails with LLVM afl-clang-fast/++ | v +--------------------------------+ - | if you want to instrument only | -> use afl-gcc-fast and afl-gcc-fast++ - | parts of the target | see [gcc_plugin/README.md](gcc_plugin/README.md) and - +--------------------------------+ [gcc_plugin/README.instrument_list.md](gcc_plugin/README.instrument_list.md) + | if you want to instrument only | -> use GCC_PLUGIN mode (afl-gcc-fast/afl-g++-fast) + | parts of the target | see [instrumentation/README.gcc_plugin.md](instrumentation/README.gcc_plugin.md) and + +--------------------------------+ [instrumentation/README.instrument_list.md](instrumentation/README.instrument_list.md) | | if not, or if you do not have a gcc with plugin support | v - use afl-gcc and afl-g++ (or afl-clang and afl-clang++) + use GCC mode (afl-gcc/afl-g++) (or afl-clang/afl-clang++ for clang) ``` Clickable README links for the chosen compiler: - * [afl-clang-lto](llvm/README.lto.md) - * [afl-clang-fast](llvm/README.md) - * [afl-gcc-fast](gcc_plugin/README.md) - * afl-gcc has no README as it has no features + * [LTO mode - afl-clang-lto](instrumentation/README.lto.md) + * [LLVM mode - afl-clang-fast](instrumentation/README.md) + * [GCC_PLUGIN mode - afl-gcc-fast](instrumentation/README.gcc_plugin.md) + * GCC mode (afl-gcc) has no README as it has no own features + +You can select the mode for the afl-cc compiler by: + 1. passing --afl-MODE command line options to the compiler via CFLAGS/CXXFLAGS/CPPFLAGS + 2. use a symlink to afl-cc: afl-gcc, afl-g++, afl-clang, afl-clang++, + afl-clang-fast, afl-clang-fast++, afl-clang-lto, afl-clang-lto++, + afl-gcc-fast, afl-g++-fast + 3. using the environment variable AFL_CC_COMPILER with MODE + +MODE can be one of: LTO (afl-clang-lto*), LLVM (afl-clang-fast*), GCC_PLUGIN +(afl-g*-fast) or GCC (afl-gcc/afl-g++). + +Because no afl specific command-line options are accepted (beside the +--afl-MODE command), the compile-time tools make fairly broad use of environment +variables, which can be listed with `afl-cc -hh` or by reading [docs/env_variables.md](docs/env_variables.md). #### b) Selecting instrumentation options -The following options are available when you instrument with afl-clang-fast or -afl-clang-lto: +The following options are available when you instrument with LTO mode (afl-clang-fast/afl-clang-lto): * Splitting integer, string, float and switch comparisons so afl++ can easier solve these. This is an important option if you do not have a very good and large input corpus. This technique is called laf-intel or COMPCOV. To use this set the following environment variable before compiling the target: `export AFL_LLVM_LAF_ALL=1` - You can read more about this in [llvm/README.laf-intel.md](llvm/README.laf-intel.md) - * A different technique (and usually a better than laf-intel) is to + You can read more about this in [instrumentation/README.laf-intel.md](instrumentation/README.laf-intel.md) + * A different technique (and usually a better one than laf-intel) is to instrument the target so that any compare values in the target are sent to afl++ which then tries to put these values into the fuzzing data at different locations. This technique is very fast and good - if the target does not @@ -282,12 +315,13 @@ afl-clang-lto: If you want to use this technique, then you have to compile the target twice, once specifically with/for this mode, and pass this binary to afl-fuzz via the `-c` parameter. - Not that you can compile also just a cmplog binary and use that for both - however there will a performance penality. - You can read more about this in [llvm_mode/README.cmplog.md](llvm_mode/README.cmplog.md) + Note that you can compile also just a cmplog binary and use that for both + however there will be a performance penality. + You can read more about this in [instrumentation/README.cmplog.md](instrumentation/README.cmplog.md) -If you use afl-clang-fast, afl-clang-lto or afl-gcc-fast you have the option to -selectively only instrument parts of the target that you are interested in: +If you use LTO, LLVM or GCC_PLUGIN mode (afl-clang-fast/afl-clang-lto/afl-gcc-fast) + you have the option to selectively only instrument parts of the target that you +are interested in: * To instrument only those parts of the target that you are interested in create a file with all the filenames of the source code that should be @@ -299,29 +333,29 @@ selectively only instrument parts of the target that you are interested in: `export AFL_LLVM_DENYLIST=denylist.txt` - depending on if you want per default to instrument unless noted (DENYLIST) or not perform instrumentation unless requested (ALLOWLIST). - **NOTE:** In optimization functions might be inlined and then not match! - see [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md) + **NOTE:** During optimization functions might be inlined and then would not match! + See [instrumentation/README.instrument_list.md](instrumentation/README.instrument_list.md) For afl-clang-fast > 6.0 or if PCGUARD instrumentation is used then use the llvm sancov allow-list feature: [http://clang.llvm.org/docs/SanitizerCoverage.html](http://clang.llvm.org/docs/SanitizerCoverage.html) The llvm sancov format works with the allowlist/denylist feature of afl++ - however afl++ is more flexible in the format. + however afl++'s format is more flexible. There are many more options and modes available however these are most of the time less effective. See: - * [llvm_mode/README.ctx.md](llvm_mode/README.ctx.md) - * [llvm_mode/README.ngram.md](llvm_mode/README.ngram.md) - * [llvm_mode/README.instrim.md](llvm_mode/README.instrim.md) + * [instrumentation/README.ctx.md](instrumentation/README.ctx.md) + * [instrumentation/README.ngram.md](instrumentation/README.ngram.md) + * [instrumentation/README.instrim.md](instrumentation/README.instrim.md) -afl++ employs never zero counting in its bitmap. You can read more about this +afl++ performs "never zero" counting in its bitmap. You can read more about this here: - * [llvm_mode/README.neverzero.md](llvm_mode/README.neverzero.md) + * [instrumentation/README.neverzero.md](instrumentation/README.neverzero.md) #### c) Modify the target If the target has features that make fuzzing more difficult, e.g. checksums, HMAC, etc. then modify the source code so that this is removed. -This can even be done for productional source code be eliminating +This can even be done for operational source code by eliminating these checks within this specific defines: ``` @@ -332,13 +366,15 @@ these checks within this specific defines: #endif ``` +All afl++ compilers will set this preprocessor definition automatically. + #### d) Instrument the target In this step the target source code is compiled so that it can be fuzzed. Basically you have to tell the target build system that the selected afl++ compiler is used. Also - if possible - you should always configure the -build system that the target is compiled statically and not dynamically. +build system such that the target is compiled statically and not dynamically. How to do this is described below. Then build the target. (Usually with `make`) @@ -349,20 +385,22 @@ For `configure` build systems this is usually done by: `CC=afl-clang-fast CXX=afl-clang-fast++ ./configure --disable-shared` Note that if you are using the (better) afl-clang-lto compiler you also have to -set AR to llvm-ar[-VERSION] and RANLIB to llvm-ranlib[-VERSION] - as it is -described in [llvm/README.lto.md](llvm/README.lto.md) +set AR to llvm-ar[-VERSION] and RANLIB to llvm-ranlib[-VERSION] - as is +described in [instrumentation/README.lto.md](instrumentation/README.lto.md). ##### cmake -For `configure` build systems this is usually done by: -`mkdir build; cd build; CC=afl-clang-fast CXX=afl-clang-fast++ cmake ..` - -Some cmake scripts require something like `-DCMAKE_CC=... -DCMAKE_CXX=...` -or `-DCMAKE_C_COMPILER=... DCMAKE_CPP_COMPILER=...` instead. +For `cmake` build systems this is usually done by: +`mkdir build; cmake -DCMAKE_C_COMPILERC=afl-cc -DCMAKE_CXX_COMPILER=afl-c++ ..` Note that if you are using the (better) afl-clang-lto compiler you also have to -set AR to llvm-ar[-VERSION] and RANLIB to llvm-ranlib[-VERSION] - as it is -described in [llvm/README.lto.md](llvm/README.lto.md) +set AR to llvm-ar[-VERSION] and RANLIB to llvm-ranlib[-VERSION] - as is +described in [instrumentation/README.lto.md](instrumentation/README.lto.md). + +##### meson + +For meson you have to set the afl++ compiler with the very first command! +`CC=afl-cc CXX=afl-c++ meson` ##### other build systems or if configure/cmake didn't work @@ -370,7 +408,7 @@ Sometimes cmake and configure do not pick up the afl++ compiler, or the ranlib/ar that is needed - because this was just not foreseen by the developer of the target. Or they have non-standard options. Figure out if there is a non-standard way to set this, otherwise set up the build normally and edit the -generated build environment afterwards manually to point to the right compiler +generated build environment afterwards manually to point it to the right compiler (and/or ranlib and ar). #### d) Better instrumentation @@ -383,12 +421,12 @@ This requires the usage of afl-clang-lto or afl-clang-fast. This is the so-called `persistent mode`, which is much, much faster but requires that you code a source file that is specifically calling the target functions that you want to fuzz, plus a few specific afl++ functions around -it. See [llvm_mode/README.persistent_mode.md](llvm_mode/README.persistent_mode.md) for details. +it. See [instrumentation/README.persistent_mode.md](instrumentation/README.persistent_mode.md) for details. Basically if you do not fuzz a target in persistent mode then you are just doing it for a hobby and not professionally :-) -### 2. Preparing the fuzzing +### 2. Preparing the fuzzing campaign As you fuzz the target with mutated input, having as diverse inputs for the target as possible improves the efficiency a lot. @@ -401,7 +439,7 @@ reported bugs, test suites, random downloads from the internet, unit test case data - from all kind of PNG software. If the input format is not known, you can also modify a target program to write -away normal data it receives and processes to a file and use these. +normal data it receives and processes to a file and use these. #### b) Making the input corpus unique @@ -415,7 +453,7 @@ the run afl-cmin like this: `afl-cmin -i INPUTS -o INPUTS_UNIQUE -- bin/target -d @@` Note that the INPUTFILE argument that the target program would read from has to be set as `@@`. -If the target reads from stdin instead, just omit the `@@` as this is the +If the target reads from stdin instead, just omit the `@@` as this is the default. #### c) Minimizing all corpus files @@ -432,7 +470,7 @@ for i in *; do done ``` -This can also be parallelized, e.g. with `parallel` +This step can also be parallelized, e.g. with `parallel` #### Done! @@ -456,7 +494,7 @@ before the start of afl-fuzz as this improves performance by a x2 speed increase #### a) Running afl-fuzz -Before to do even a test run of afl-fuzz execute `sudo afl-system-config` (on +Before you do even a test run of afl-fuzz execute `sudo afl-system-config` (on the host if you execute afl-fuzz in a docker container). This reconfigures the system for optimal speed - which afl-fuzz checks and bails otherwise. Set `export AFL_SKIP_CPUFREQ=1` for afl-fuzz to skip this check if you cannot @@ -588,7 +626,7 @@ then terminate it. The main node will pick it up and make it available to the other secondary nodes over time. Set `export AFL_NO_AFFINITY=1` if you have no free core. -Note that you in nearly all cases you can never reach full coverage. A lot of +Note that you in nearly all cases can never reach full coverage. A lot of functionality is usually behind options that were not activated or fuzz e.g. if you fuzz a library to convert image formats and your target is the png to tiff API then you will not touch any of the other library APIs and features. @@ -607,7 +645,7 @@ switch or honggfuzz. #### f) Improve the speed! - * Use [persistent mode](llvm_mode/README.persistent_mode.md) (x2-x20 speed increase) + * Use [persistent mode](instrumentation/README.persistent_mode.md) (x2-x20 speed increase) * If you do not use shmem persistent mode, use `AFL_TMPDIR` to point the input file on a tempfs location, see [docs/env_variables.md](docs/env_variables.md) * Linux: Use the [afl++ snapshot module](https://github.com/AFLplusplus/AFL-Snapshot-LKM) (x2 speed increase) * Linux: Improve kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system more insecure) @@ -1035,7 +1073,6 @@ without feedback, bug reports, or patches from: Andrea Biondo Vincent Le Garrec Khaled Yakdan Kuang-che Wu Josephine Calliotte Konrad Welc - Thomas Rooijakkers ``` Thank you! diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 1a05f4f4..4281c554 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -162,8 +162,7 @@ struct queue_entry { u8 *trace_mini; /* Trace bytes, if kept */ u32 tc_ref; /* Trace bytes ref count */ - struct queue_entry *next, /* Next element, if any */ - *next_100; /* 100 elements ahead */ + struct queue_entry *next; /* Next element, if any */ }; @@ -575,8 +574,7 @@ typedef struct afl_state { struct queue_entry *queue, /* Fuzzing queue (linked list) */ *queue_cur, /* Current offset within the queue */ - *queue_top, /* Top of the list */ - *q_prev100; /* Previous 100 marker */ + *queue_top; /* Top of the list */ // growing buf struct queue_entry **queue_buf; diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index d6c368d1..58ce5b6f 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -101,7 +101,8 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, if (rptr < lptr || *rptr != '"') { - FATAL("Malformed name=\"value\" pair in line %u.", cur_line); + WARNF("Malformed name=\"value\" pair in line %u.", cur_line); + continue; } @@ -141,13 +142,19 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, if (*lptr != '"') { - FATAL("Malformed name=\"keyword\" pair in line %u.", cur_line); + WARNF("Malformed name=\"keyword\" pair in line %u.", cur_line); + continue; } ++lptr; - if (!*lptr) { FATAL("Empty keyword in line %u.", cur_line); } + if (!*lptr) { + + WARNF("Empty keyword in line %u.", cur_line); + continue; + + } /* Okay, let's allocate memory and copy data between "...", handling \xNN escaping, \\, and \". */ @@ -169,7 +176,9 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, case 1 ... 31: case 128 ... 255: - FATAL("Non-printable characters in line %u.", cur_line); + WARNF("Non-printable characters in line %u.", cur_line); + continue; + break; case '\\': @@ -185,7 +194,8 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, if (*lptr != 'x' || !isxdigit(lptr[1]) || !isxdigit(lptr[2])) { - FATAL("Invalid escaping (not \\xNN) in line %u.", cur_line); + WARNF("Invalid escaping (not \\xNN) in line %u.", cur_line); + continue; } @@ -209,10 +219,11 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, if (afl->extras[afl->extras_cnt].len > MAX_DICT_FILE) { - FATAL( + WARNF( "Keyword too big in line %u (%s, limit is %s)", cur_line, stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), klen), stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); + continue; } @@ -232,14 +243,19 @@ static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; - if (!afl->extras_cnt) { FATAL("No usable files in '%s'", dir); } + if (!afl->extras_cnt) { + + WARNF("No usable data in '%s'", dir); + return; + + } qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), compare_extras_len); - OKF("Loaded %u extra tokens, size range %s to %s.", afl->extras_cnt, - stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), min_len), - stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), max_len)); + ACTF("Loaded %u extra tokens, size range %s to %s.", afl->extras_cnt, + stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), min_len), + stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), max_len)); if (max_len > 32) { @@ -250,8 +266,8 @@ static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, if (afl->extras_cnt > afl->max_det_extras) { - OKF("More than %d tokens - will use them probabilistically.", - afl->max_det_extras); + WARNF("More than %d tokens - will use them probabilistically.", + afl->max_det_extras); } @@ -320,9 +336,10 @@ void load_extras(afl_state_t *afl, u8 *dir) { if (st.st_size > MAX_DICT_FILE) { WARNF( - "Extra '%s' is very big (%s, limit is %s)", fn, + "Extra '%s' is too big (%s, limit is %s)", fn, stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), st.st_size), stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); + continue; } @@ -370,16 +387,74 @@ static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) { } -/* Adds a new extra / dict entry. Used for LTO autodict. */ +/* Removes duplicates from the loaded extras. This can happen if multiple files + are loaded */ + +void dedup_extras(afl_state_t *afl) { + + if (afl->extras_cnt < 2) return; + + u32 i, j, orig_cnt = afl->extras_cnt; + + for (i = 0; i < afl->extras_cnt - 1; i++) { + + for (j = i + 1; j < afl->extras_cnt; j++) { + + restart_dedup: + + // if the goto was used we could be at the end of the list + if (j >= afl->extras_cnt || afl->extras[i].len != afl->extras[j].len) + break; + + if (memcmp(afl->extras[i].data, afl->extras[j].data, + afl->extras[i].len) == 0) { + + ck_free(afl->extras[j].data); + if (j + 1 < afl->extras_cnt) // not at the end of the list? + memmove((char *)&afl->extras[j], (char *)&afl->extras[j + 1], + (afl->extras_cnt - j - 1) * sizeof(struct extra_data)); + afl->extras_cnt--; + goto restart_dedup; // restart if several duplicates are in a row + + } + + } + + } + + if (afl->extras_cnt != orig_cnt) + afl->extras = afl_realloc((void **)&afl->extras, + afl->extras_cnt * sizeof(struct extra_data)); + +} + +/* Adds a new extra / dict entry. */ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { - u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; + u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; + u32 i, found = 0; + + for (i = 0; i < afl->extras_cnt; i++) { + + if (afl->extras[i].len == len) { + + if (memcmp(afl->extras[i].data, mem, len) == 0) return; + found = 1; + + } else { + + if (found) break; + + } + + } if (len > MAX_DICT_FILE) { - WARNF("Extra '%.*s' is very big (%s, limit is %s)", (int)len, mem, + WARNF("Extra '%.*s' is too big (%s, limit is %s)", (int)len, mem, stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), len), stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); + return; } else if (len > 32) { @@ -405,8 +480,8 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { if (afl->extras_cnt == afl->max_det_extras + 1) { - OKF("More than %d tokens - will use them probabilistically.", - afl->max_det_extras); + WARNF("More than %d tokens - will use them probabilistically.", + afl->max_det_extras); } @@ -609,7 +684,7 @@ void load_auto(afl_state_t *afl) { } else { - OKF("No auto-generated dictionary tokens to reuse."); + ACTF("No auto-generated dictionary tokens to reuse."); } diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 102f04b9..713849a1 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -611,17 +611,17 @@ void read_foreign_testcases(afl_state_t *afl, int first) { /* Read all testcases from the input directory, then queue them for testing. Called at startup. */ -void read_testcases(afl_state_t *afl) { +void read_testcases(afl_state_t *afl, u8 *directory) { struct dirent **nl; - s32 nl_cnt; + s32 nl_cnt, subdirs = 1; u32 i; - u8 * fn1; - + u8 * fn1, *dir = directory; u8 val_buf[2][STRINGIFY_VAL_SIZE_MAX]; /* Auto-detect non-in-place resumption attempts. */ +if (dir == NULL) { fn1 = alloc_printf("%s/queue", afl->in_dir); if (!access(fn1, F_OK)) { @@ -632,16 +632,18 @@ void read_testcases(afl_state_t *afl) { ck_free(fn1); } + dir = afl->in_dir; +} - ACTF("Scanning '%s'...", afl->in_dir); + ACTF("Scanning '%s'...", dir); /* We use scandir() + alphasort() rather than readdir() because otherwise, the ordering of test cases would vary somewhat randomly and would be difficult to control. */ - nl_cnt = scandir(afl->in_dir, &nl, NULL, alphasort); + nl_cnt = scandir(dir, &nl, NULL, alphasort); - if (nl_cnt < 0) { + if (nl_cnt < 0 && directory == NULL) { if (errno == ENOENT || errno == ENOTDIR) { @@ -656,7 +658,7 @@ void read_testcases(afl_state_t *afl) { } - PFATAL("Unable to open '%s'", afl->in_dir); + PFATAL("Unable to open '%s'", dir); } @@ -674,19 +676,29 @@ void read_testcases(afl_state_t *afl) { u8 dfn[PATH_MAX]; snprintf(dfn, PATH_MAX, "%s/.state/deterministic_done/%s", afl->in_dir, nl[i]->d_name); - u8 *fn2 = alloc_printf("%s/%s", afl->in_dir, nl[i]->d_name); + u8 *fn2 = alloc_printf("%s/%s", dir, nl[i]->d_name); u8 passed_det = 0; - free(nl[i]); /* not tracked */ - if (lstat(fn2, &st) || access(fn2, R_OK)) { PFATAL("Unable to access '%s'", fn2); } - /* This also takes care of . and .. */ + /* obviously we want to skip "descending" into . and .. directories, + however it is a good idea to skip also directories that start with + a dot */ + if (subdirs && S_ISDIR(st.st_mode) && nl[i]->d_name[0] != '.') { + + free(nl[i]); /* not tracked */ + read_testcases(afl, fn2); + ck_free(fn2); + continue; + + } + + free(nl[i]); if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn2, "/README.txt")) { @@ -718,7 +730,7 @@ void read_testcases(afl_state_t *afl) { free(nl); /* not tracked */ - if (!afl->queued_paths) { + if (!afl->queued_paths && directory == NULL) { SAYF("\n" cLRD "[-] " cRST "Looks like there are no valid test cases in the input directory! The " @@ -985,6 +997,76 @@ void perform_dry_run(afl_state_t *afl) { } + /* Now we remove all entries from the queue that have a duplicate trace map */ + + q = afl->queue; + struct queue_entry *p, *prev = NULL; + int duplicates = 0; + +restart_outer_cull_loop: + + while (q) { + + if (q->cal_failed || !q->exec_cksum) continue; + + restart_inner_cull_loop: + + p = q->next; + + while (p) { + + if (!p->cal_failed && p->exec_cksum == q->exec_cksum) { + + duplicates = 1; + --afl->pending_not_fuzzed; + + // We do not remove any of the memory allocated because for + // splicing the data might still be interesting. + // We only decouple them from the linked list. + // This will result in some leaks at exit, but who cares. + + // we keep the shorter file + if (p->len >= q->len) { + + q->next = p->next; + goto restart_inner_cull_loop; + + } else { + + if (prev) + prev->next = q = p; + else + afl->queue = q = p; + goto restart_outer_cull_loop; + + } + + } + + p = p->next; + + } + + prev = q; + q = q->next; + + } + + if (duplicates) { + + afl->max_depth = 0; + q = afl->queue; + while (q) { + + if (q->depth > afl->max_depth) afl->max_depth = q->depth; + q = q->next; + + } + + afl->q_prev100 = afl->queue = afl->queue_top = afl->queue; + + } + OKF("All test cases processed."); } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index bf568c38..5737c1f5 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1707,20 +1707,8 @@ custom_mutator_stage: } while (tid == afl->current_entry && afl->queued_paths > 1); - target = afl->queue; - - while (tid >= 100) { - - target = target->next_100; - tid -= 100; - - } - - while (tid--) { - - target = target->next; - - } + afl->splicing_with = tid; + target = afl->queue_buf[tid]; /* Make sure that the target has a reasonable length. */ @@ -4518,20 +4506,7 @@ pacemaker_fuzzing: } while (tid == afl->current_entry); afl->splicing_with = tid; - target = afl->queue; - - while (tid >= 100) { - - target = target->next_100; - tid -= 100; - - } - - while (tid--) { - - target = target->next; - - } + target = afl->queue_buf[tid]; /* Make sure that the target has a reasonable length. */ diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index c6d8225f..db91813b 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -239,13 +239,6 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { afl->cycles_wo_finds = 0; - if (!(afl->queued_paths % 100)) { - - afl->q_prev100->next_100 = q; - afl->q_prev100 = q; - - } - struct queue_entry **queue_buf = afl_realloc( AFL_BUF_PARAM(queue), afl->queued_paths * sizeof(struct queue_entry *)); if (unlikely(!queue_buf)) { PFATAL("alloc"); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index c12d5db5..bfaa22e8 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -119,8 +119,8 @@ static void usage(u8 *argv0, int more_help) { "etc.)\n" " -d - quick & dirty mode (skips deterministic steps)\n" " -n - fuzz without instrumentation (non-instrumented mode)\n" - " -x dict_file - optional fuzzer dictionary (see README.md, its really " - "good!)\n\n" + " -x dict_file - fuzzer dictionary (see README.md, specify up to 4 " + "times)\n\n" "Testing settings:\n" " -s seed - use a fixed seed for the RNG\n" @@ -243,11 +243,11 @@ static int stricmp(char const *a, char const *b) { int main(int argc, char **argv_orig, char **envp) { - s32 opt; + s32 opt, i; u64 prev_queued = 0; u32 sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE; - u8 * extras_dir = 0; - u8 mem_limit_given = 0, exit_1 = 0, debug = 0; + u8 * extras_dir[4]; + u8 mem_limit_given = 0, exit_1 = 0, debug = 0, extras_dir_cnt = 0; char **use_argv; struct timeval tv; @@ -450,8 +450,13 @@ int main(int argc, char **argv_orig, char **envp) { case 'x': /* dictionary */ - if (extras_dir) { FATAL("Multiple -x options not supported"); } - extras_dir = optarg; + if (extras_dir_cnt >= 4) { + + FATAL("More than four -x options are not supported"); + + } + + extras_dir[extras_dir_cnt++] = optarg; break; case 't': { /* timeout */ @@ -828,10 +833,6 @@ int main(int argc, char **argv_orig, char **envp) { "Eißfeldt, Andrea Fioraldi and Dominik Maier"); OKF("afl++ is open source, get it at " "https://github.com/AFLplusplus/AFLplusplus"); - OKF("Power schedules from github.com/mboehme/aflfast"); - OKF("Python Mutator and llvm_mode instrument file list from " - "github.com/choller/afl"); - OKF("MOpt Mutator from github.com/puppet-meteor/MOpt-AFL"); if (afl->sync_id && afl->is_main_node && afl->afl_env.afl_custom_mutator_only) { @@ -1139,7 +1140,15 @@ int main(int argc, char **argv_orig, char **envp) { pivot_inputs(afl); - if (extras_dir) { load_extras(afl, extras_dir); } + if (extras_dir_cnt) { + + for (i = 0; i < extras_dir_cnt; i++) + load_extras(afl, extras_dir[i]); + + dedup_extras(afl); + OKF("Loaded a total of %u extras.", afl->extras_cnt); + + } if (!afl->timeout_given) { find_timeout(afl); } -- cgit 1.4.1 From e30b2c6af6e369844c92c00a20ebdd53473a747c Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 5 Sep 2020 13:18:28 +0200 Subject: final changes for pre-3.0 --- .gitignore | 6 ++ Android.bp | 6 +- GNUmakefile | 3 +- GNUmakefile.gcc_plugin | 1 + GNUmakefile.llvm | 8 ++- README.md | 2 +- docs/Changelog.md | 14 ++++ docs/FAQ.md | 104 +++++++++++++++--------------- docs/INSTALL.md | 19 +++--- docs/env_variables.md | 121 ++++++++++++++++++----------------- docs/ideas.md | 57 ----------------- docs/life_pro_tips.md | 4 +- docs/perf_tips.md | 8 +-- docs/sister_projects.md | 4 +- docs/status_screen.md | 2 +- examples/README.md | 2 +- examples/aflpp_driver/aflpp_driver.c | 2 +- include/afl-fuzz.h | 3 +- include/config.h | 4 +- include/envs.h | 1 + qemu_mode/patches/afl-qemu-cpu-inl.h | 2 +- src/afl-fuzz-init.c | 3 +- src/afl-fuzz-queue.c | 13 ++-- src/afl-fuzz-stats.c | 8 +-- src/afl-fuzz.c | 3 +- test/test-gcc-plugin.sh | 2 +- test/test-unittests.sh | 2 + 27 files changed, 188 insertions(+), 216 deletions(-) (limited to 'src') diff --git a/.gitignore b/.gitignore index 0527a0b2..e3adb6ef 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,12 @@ afl-showmap.8 afl-system-config.8 afl-tmin.8 afl-whatsup.8 +afl-c++ +afl-cc +afl-lto +afl-lto++ +afl-lto++.8 +afl-lto.8 qemu_mode/libcompcov/compcovtest qemu_mode/qemu-* unicorn_mode/samples/*/\.test-* diff --git a/Android.bp b/Android.bp index e59129db..2c2114b2 100644 --- a/Android.bp +++ b/Android.bp @@ -101,7 +101,7 @@ cc_binary_host { ], srcs: [ - "llvm_mode/afl-clang-fast.c", + "src/afl-cc.c", ], } @@ -119,7 +119,7 @@ cc_binary_host { ], srcs: [ - "llvm_mode/afl-clang-fast.c", + "src/afl-cc.c", ], } @@ -136,6 +136,6 @@ cc_library_static { ], srcs: [ - "llvm_mode/afl-llvm-rt.o.c", + "instrumentation/afl-llvm-rt.o.c", ], } diff --git a/GNUmakefile b/GNUmakefile index 0046a481..7455483c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -533,7 +533,7 @@ endif deepclean: clean rm -rf qemu_mode/qemu-3.1.1.tar.xz rm -rf unicorn_mode/unicornafl - git reset --hard >/dev/null 2>&1 || true + # NEVER EVER ACTIVATE THAT!!!!! git reset --hard >/dev/null 2>&1 || true .PHONY: distrib distrib: all @@ -591,6 +591,7 @@ install: all $(MANPAGES) @install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH) @rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh @rm -f $${DESTDIR}$(BIN_PATH)/afl-as + @rm -f $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt.o install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH) @if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi @if [ -f libdislocator.so ]; then set -e; install -m 755 libdislocator.so $${DESTDIR}$(HELPER_PATH); fi diff --git a/GNUmakefile.gcc_plugin b/GNUmakefile.gcc_plugin index aeb1ef16..b73fcfda 100644 --- a/GNUmakefile.gcc_plugin +++ b/GNUmakefile.gcc_plugin @@ -158,6 +158,7 @@ vpath % .. install: all ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-gcc-fast ln -sf afl-c++ $${DESTDIR}$(BIN_PATH)/afl-g++-fast + ln -sf afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt.o install -m 755 ./afl-gcc-pass.so $${DESTDIR}$(HELPER_PATH) install -m 644 -T instrumentation/README.gcc_plugin.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index d76e0b28..1bb3d265 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -423,10 +423,12 @@ document: ./afl-compiler-rt-32.o: instrumentation/afl-compiler-rt.o.c | test_deps @printf "[*] Building 32-bit variant of the runtime (-m32)... " @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + @test -e afl-compiler-rt-32.o && ln -sf afl-compiler-rt-32.o afl-llvm-rt-64.o ./afl-compiler-rt-64.o: instrumentation/afl-compiler-rt.o.c | test_deps @printf "[*] Building 64-bit variant of the runtime (-m64)... " @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + @test -e afl-compiler-rt-64.o && ln -sf afl-compiler-rt-64.o afl-llvm-rt-64.o .PHONY: test_build test_build: $(PROGS) @@ -448,11 +450,11 @@ all_done: test_build install: all @install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH) @if [ -f ./afl-cc ]; then set -e; install -m 755 ./afl-cc $${DESTDIR}$(BIN_PATH); ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-c++; fi - @if [ -f ./afl-compiler-rt.o ]; then set -e; install -m 755 ./afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH); fi + @if [ -f ./afl-compiler-rt.o ]; then set -e; install -m 755 ./afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH)afl-llvm-rt.o ;fi @if [ -f ./afl-lto ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto++; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ./afl-llvm-lto-instrumentation.so ./afl-llvm-rt-lto*.o ./afl-llvm-lto-instrumentlist.so $${DESTDIR}$(HELPER_PATH); fi @if [ -f ./afl-ld-lto ]; then set -e; install -m 755 ./afl-ld-lto $${DESTDIR}$(BIN_PATH); fi - @if [ -f ./afl-compiler-rt-32.o ]; then set -e; install -m 755 ./afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH); fi - @if [ -f ./afl-compiler-rt-64.o ]; then set -e; install -m 755 ./afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH); fi + @if [ -f ./afl-compiler-rt-32.o ]; then set -e; install -m 755 ./afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH)afl-llvm-rt-32.o ;fi + @if [ -f ./afl-compiler-rt-64.o ]; then set -e; install -m 755 ./afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH)afl-llvm-rt-64.o ; fi @if [ -f ./compare-transform-pass.so ]; then set -e; install -m 755 ./*.so $${DESTDIR}$(HELPER_PATH); fi @if [ -f ./compare-transform-pass.so ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-fast ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang-fast++ ; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang++ ; fi @if [ -f ./SanitizerCoverageLTO.so ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang-lto++ ; fi diff --git a/README.md b/README.md index 96b34260..c886489d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Release Version: [2.67c](https://github.com/AFLplusplus/AFLplusplus/releases) - Github Version: 2.67d + Github Version: 3.00a Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) diff --git a/docs/Changelog.md b/docs/Changelog.md index 6321aee4..9de03e78 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,6 +9,20 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . +### Version ++3.00a (develop) + - llvm_mode/ and gcc_plugin/ moved to instrumentation/ + - all compilers combined to afl-cc which emulates the previous ones + - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o + - afl-fuzz + - reading testcases from -i now descends into subdirectories + - allow up to 4 -x command line options + - loaded extras now have a duplicate protection + - instrumentation + - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz + -x dictionary of string comparisons found during compilation + - not overriding -Ox or -fno-unroll-loops anymore + + ### Version ++2.68c (release) - added the GSoC excellent afl++ grammar mutator by Shengtuo to our custom_mutators/ (see custom_mutators/README.md) - or get it here: diff --git a/docs/FAQ.md b/docs/FAQ.md index 064638f4..24942492 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -4,11 +4,11 @@ * [What is the difference between afl and afl++?](#what-is-the-difference-between-afl-and-afl) * [How to improve the fuzzing speed?](#how-to-improve-the-fuzzing-speed) - * [How do I fuzz a network service?](#how-do-i-fuzz-a-network-service) - * [How do I fuzz a GUI program?](#how-do-i-fuzz-a-gui-program) + * [How do I fuzz a network service?](#how-to-fuzz-a-network-service) + * [How do I fuzz a GUI program?](#how-to-fuzz-a-gui-program) * [What is an edge?](#what-is-an-edge) * [Why is my stability below 100%?](#why-is-my-stability-below-100) - * [How can I improve the stability value?](#how-can-i-improve-the-stability-value) + * [How can I improve the stability value](#how-can-i-improve-the-stability-value) If you find an interesting or important question missing, submit it via [https://github.com/AFLplusplus/AFLplusplus/issues](https://github.com/AFLplusplus/AFLplusplus/issues) @@ -18,52 +18,51 @@ If you find an interesting or important question missing, submit it via American Fuzzy Lop (AFL) was developed by Michał "lcamtuf" Zalewski starting in 2013/2014, and when he left Google end of 2017 he stopped developing it. -At the end of 2019 the Google fuzzing team took over maintenance of AFL, however -it is only accepting PRs from the community and is not developing enhancements +At the end of 2019 the Google fuzzing team took over maintance of AFL, however +it is only accepting PR from the community and is not developing enhancements anymore. -In the second quarter of 2019, 1 1/2 year later when no further development of -AFL had happened and it became clear there would none be coming, afl++ -was born, where initially community patches were collected and applied -for bug fixes and enhancements. Then from various AFL spin-offs - mostly academic +In the second quarter of 2019, 1 1/2 years after no further development of +AFL had happened and it became clear there would be none coming, afl++ +was born, where initially first community patches were collected and applied +for bugs and enhancements. Then from various AFL spin-offs - mostly academic research - features were integrated. This already resulted in a much advanced AFL. Until the end of 2019 the afl++ team had grown to four active developers which -then implemented their own research and features, making it now by far the most +then implemented their own research and feature, making it now by far the most flexible and feature rich guided fuzzer available as open source. And in independent fuzzing benchmarks it is one of the best fuzzers available, e.g. [Fuzzbench Report](https://www.fuzzbench.com/reports/2020-08-03/index.html) -## How to improve the fuzzing speed? +## How to improve the fuzzing speed - 1. Use [llvm_mode](docs/llvm_mode/README.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended) - 2. Use [persistent mode](llvm_mode/README.persistent_mode.md) (x2-x20 speed increase) + 1. use [instrumentation](docs/README.llvm.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended) + 2. Use [persistent mode](instrumentation/README.persistent_mode.md) (x2-x20 speed increase) 3. Use the [afl++ snapshot module](https://github.com/AFLplusplus/AFL-Snapshot-LKM) (x2 speed increase) - 4. If you do not use shmem persistent mode, use `AFL_TMPDIR` to put the input file directory on a tempfs location, see [docs/env_variables.md](docs/env_variables.md) - 5. Improve Linux kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system less secure) + 4. If you do not use shmem persistent mode, use `AFL_TMPDIR` to point the input file on a tempfs location, see [docs/env_variables.md](docs/env_variables.md) + 5. Improve kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system more insecure) 6. Running on an `ext2` filesystem with `noatime` mount option will be a bit faster than on any other journaling filesystem 7. Use your cores! [README.md:3.b) Using multiple cores/threads](../README.md#b-using-multiple-coresthreads) ## How do I fuzz a network service? -The short answer is - you cannot, at least not "out of the box". +The short answer is - you cannot, at least "out of the box". -Using a network channel is inadequate for several reasons: -- it has a slow-down of x10-20 on the fuzzing speed -- it does not scale to fuzzing multiple instances easily, -- instead of one initial data packet often a back-and-forth interplay of packets is needed for stateful protocols (which is totally unsupported by most coverage aware fuzzers). +Using network has a slow-down of x10-20 on the fuzzing speed, does not scale, +and finally usually it is more than one initial data packet but a back-and-forth +which is totally unsupported by most coverage aware fuzzers. The established method to fuzz network services is to modify the source code to read from a file or stdin (fd 0) (or even faster via shared memory, combine -this with persistent mode [llvm_mode/README.persistent_mode.md](llvm_mode/README.persistent_mode.md) +this with persistent mode [instrumentation/README.persistent_mode.md](instrumentation/README.persistent_mode.md) and you have a performance gain of x10 instead of a performance loss of over -x10 - that is a x100 difference!). +x10 - that is a x100 difference! If modifying the source is not an option (e.g. because you only have a binary and perform binary fuzzing) you can also use a shared library with AFL_PRELOAD -to emulate the network. This is also much faster than the real network would be. -See [examples/socket_fuzzing/](../examples/socket_fuzzing/). +to emulate the network. This is also much faster than network would be. +See [examples/socket_fuzzing/](../examples/socket_fuzzing/) There is an outdated afl++ branch that implements networking if you are desperate though: [https://github.com/AFLplusplus/AFLplusplus/tree/networking](https://github.com/AFLplusplus/AFLplusplus/tree/networking) - @@ -74,7 +73,7 @@ which allows you to define network state with different type of data packets. If the GUI program can read the fuzz data from a file (via the command line, a fixed location or via an environment variable) without needing any user -interaction then it would be suitable for fuzzing. +interaction then then yes. Otherwise it is not possible without modifying the source code - which is a very good idea anyway as the GUI functionality is a huge CPU/time overhead @@ -83,13 +82,13 @@ for the fuzzing. So create a new `main()` that just reads the test case and calls the functionality for processing the input that the GUI program is using. -## What is an "edge"? +## What is an "edge" A program contains `functions`, `functions` contain the compiled machine code. The compiled machine code in a `function` can be in a single or many `basic blocks`. A `basic block` is the largest possible number of subsequent machine code -instructions that has exactly one entrypoint (which can be be entered by multiple other basic blocks) -and runs linearly without branching or jumping to other addresses (except at the end). +instructions that runs independent, meaning it does not split up to different +locations nor is it jumped into it from a different location: ``` function() { A: @@ -99,7 +98,7 @@ function() { if (x) goto C; else goto D; C: some code - goto E + goto D D: some code goto B @@ -109,7 +108,7 @@ function() { ``` Every code block between two jump locations is a `basic block`. -An `edge` is then the unique relationship between two directly connected `basic blocks` (from the +An `edge` is then the unique relationship between two `basic blocks` (from the code example above): ``` Block A @@ -124,9 +123,8 @@ code example above): Block E ``` Every line between two blocks is an `edge`. -Note that a few basic block loop to itself, this too would be an edge. -## Why is my stability below 100%? +## Why is my stability below 100% Stability is measured by how many percent of the edges in the target are "stable". Sending the same input again and again should take the exact same @@ -134,37 +132,37 @@ path through the target every time. If that is the case, the stability is 100%. If however randomness happens, e.g. a thread reading other external data, reaction to timing, etc. then in some of the re-executions with the same data -the edge coverage result will be different accross runs. +the result in the edge information will be different accross runs. Those edges that change are then flagged "unstable". The more "unstable" edges, the more difficult for afl++ to identify valid new paths. A value above 90% is usually fine and a value above 80% is also still ok, and -even a value above 20% can still result in successful finds of bugs. -However, it is recommended that for values below 90% or 80% you should take -countermeasures to improve stability. +even above 20% can still result in successful finds of bugs. +However, it is recommended that below 90% or 80% you should take measures to +improve the stability. -## How can I improve the stability value? +## How can I improve the stability value -For fuzzing a 100% stable target that covers all edges is the best case. +For fuzzing a 100% stable target that covers all edges is the best. A 90% stable target that covers all edges is however better than a 100% stable target that ignores 10% of the edges. With instability you basically have a partial coverage loss on an edge, with -ignored functions you have a full loss on that edges. +ignore you have a full loss on that edge. There are functions that are unstable, but also provide value to coverage, eg init functions that use fuzz data as input for example. -If however a function that has nothing to do with the input data is the -source of instability, e.g. checking jitter, or is a hash map function etc. -then it should not be instrumented. +If however it is a function that has nothing to do with the input data is the +source, e.g. checking jitter, or is a hash map function etc. then it should +not be instrumented. -To be able to exclude these functions (based on AFL++'s measured stability) -the following process will allow to identify functions with variable edges. +To be able to make this decision the following process will allow you to +identify the functions with variable edges so you can make this decision. -Four steps are required to do this and it also requires quite some knowledge -of coding and/or disassembly and is effectively possible only with +Four steps are required to do this and requires quite some knowledge of +coding and/or disassembly and it is only effectively possible with afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. 1. First step: Identify which edge ID numbers are unstable @@ -173,7 +171,7 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. The out/fuzzer_stats file will then show the edge IDs that were identified as unstable. - 2. Second step: Find the responsible function(s). + 2. Second step: Find the responsible function. a) For LTO instrumented binaries this can be documented during compile time, just set `export AFL_LLVM_DOCUMENT_IDS=/path/to/a/file`. @@ -182,10 +180,10 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. b) For PCGUARD instrumented binaries it is much more difficult. Here you can either modify the __sanitizer_cov_trace_pc_guard function in - llvm_mode/afl-llvm-rt.o.c to write a backtrace to a file if the ID in + instrumentation/afl-llvm-rt.o.c to write a backtrace to a file if the ID in __afl_area_ptr[*guard] is one of the unstable edge IDs. (Example code is already there). - Then recompile and reinstall llvm_mode and rebuild your target. + Then recompile and reinstall instrumentation and rebuild your target. Run the recompiled target with afl-fuzz for a while and then check the file that you wrote with the backtrace information. Alternatively you can use `gdb` to hook __sanitizer_cov_trace_pc_guard_init @@ -193,20 +191,20 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. and set a write breakpoint to that address (`watch 0x.....`). c) in all other instrumentation types this is not possible. So just - recompile with the two mentioned above. This is just for + recompile with the the two mentioned above. This is just for identifying the functions that have unstable edges. 3. Third step: create a text file with the filenames/functions Identify which source code files contain the functions that you need to remove from instrumentation, or just specify the functions you want to - skip for instrumentation. Note that optimization might inline functions! + skip instrumenting. Note that optimization might inline functions! - Simply follow this document on how to do this: [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md) + Simply follow this document on how to do this: [instrumentation/README.instrument_list.md](instrumentation/README.instrument_list.md) If PCGUARD is used, then you need to follow this guide (needs llvm 12+!): [http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation](http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation) - Only exclude those functions from instrumentation that provide no value + Only deny those functions from instrumentation that provide no value for coverage - that is if it does not process any fuzz data directly or indirectly (e.g. hash maps, thread management etc.). If however a function directly or indirectly handles fuzz data then you diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 766f24d7..fb7b5642 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -24,7 +24,7 @@ There are no special dependencies to speak of; you will need GNU make and a working compiler (gcc or clang). Some of the optional scripts bundled with the program may depend on bash, gdb, and similar basic tools. -If you are using clang, please review llvm_mode/README.md; the LLVM +If you are using clang, please review README.llvm.md; the LLVM integration mode can offer substantial performance gains compared to the traditional approach. @@ -52,10 +52,10 @@ sudo gmake install Keep in mind that if you are using csh as your shell, the syntax of some of the shell commands given in the README.md and other docs will be different. -The `llvm_mode` requires a dynamically linked, fully-operational installation of +The `llvm` requires a dynamically linked, fully-operational installation of clang. At least on FreeBSD, the clang binaries are static and do not include some of the essential tools, so if you want to make it work, you may need to -follow the instructions in llvm_mode/README.md. +follow the instructions in README.llvm.md. Beyond that, everything should work as advertised. @@ -97,27 +97,24 @@ and definitely don't look POSIX-compliant. This means two things: User emulation mode of QEMU does not appear to be supported on MacOS X, so black-box instrumentation mode (`-Q`) will not work. -The llvm_mode requires a fully-operational installation of clang. The one that +The llvm instrumentation requires a fully-operational installation of clang. The one that comes with Xcode is missing some of the essential headers and helper tools. -See llvm_mode/README.md for advice on how to build the compiler from scratch. +See README.llvm.md for advice on how to build the compiler from scratch. ## 4. Linux or *BSD on non-x86 systems Standard build will fail on non-x86 systems, but you should be able to leverage two other options: - - The LLVM mode (see llvm_mode/README.md), which does not rely on + - The LLVM mode (see README.llvm.md), which does not rely on x86-specific assembly shims. It's fast and robust, but requires a complete installation of clang. - The QEMU mode (see qemu_mode/README.md), which can be also used for fuzzing cross-platform binaries. It's slower and more fragile, but can be used even when you don't have the source for the tested app. -If you're not sure what you need, you need the LLVM mode. To get it, try: - -```bash -AFL_NO_X86=1 gmake && gmake -C llvm_mode -``` +If you're not sure what you need, you need the LLVM mode, which is built by +default. ...and compile your target program with afl-clang-fast or afl-clang-fast++ instead of the traditional afl-gcc or afl-clang wrappers. diff --git a/docs/env_variables.md b/docs/env_variables.md index c47d10e8..9d289f6d 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -5,13 +5,25 @@ users or for some types of custom fuzzing setups. See README.md for the general instruction manual. -## 1) Settings for afl-gcc, afl-clang, and afl-as - and gcc_plugin afl-gcc-fast +## 1) Settings for all compilers -Because they can't directly accept command-line options, the compile-time -tools make fairly broad use of environmental variables: +Starting with afl++ 3.0 there is only one compiler: afl-cc +To select the different instrumentation modes this can be done by + 1. passing --afl-MODE command line options to the compiler + 2. use a symlink to afl-cc: afl-gcc, afl-g++, afl-clang, afl-clang++, + afl-clang-fast, afl-clang-fast++, afl-clang-lto, afl-clang-lto++, + afl-gcc-fast, afl-g++-fast + 3. using the environment variable AFL_CC_COMPILER with MODE - - Most afl tools do not print any output if stdout/stderr are redirected. - If you want to save the output in a file then set the AFL_DEBUG +MODE can one of LTO (afl-clang-lto*), LLVM (afl-clang-fast*), GCC_PLUGIN +(afl-g*-fast) or GCC (afl-gcc/afl-g++). + +Because beside the --afl-MODE command no afl specific command-line options +are accepted, the compile-time tools make fairly broad use of environmental +variables: + + - Most afl tools do not print any ouput if stout/stderr are redirected. + If you want to have the output into a file then set the AFL_DEBUG environment variable. This is sadly necessary for various build processes which fail otherwise. @@ -24,6 +36,8 @@ tools make fairly broad use of environmental variables: will cause problems in programs built with -Werror, simply because -O3 enables more thorough code analysis and can spew out additional warnings. To disable optimizations, set AFL_DONT_OPTIMIZE. + However if -O... and/or -fno-unroll-loops are set, these are not + overriden. - Setting AFL_USE_ASAN automatically enables ASAN, provided that your compiler supports that. Note that fuzzing with ASAN is mildly challenging @@ -44,7 +58,7 @@ tools make fairly broad use of environmental variables: you instrument hand-written assembly when compiling clang code by plugging a normalizer into the chain. (There is no equivalent feature for GCC.) - - Setting AFL_INST_RATIO to a percentage between 0% and 100% controls the + - Setting AFL_INST_RATIO to a percentage between 0 and 100% controls the probability of instrumenting every branch. This is (very rarely) useful when dealing with exceptionally complex programs that saturate the output bitmap. Examples include v8, ffmpeg, and perl. @@ -55,19 +69,16 @@ tools make fairly broad use of environmental variables: Setting AFL_INST_RATIO to 0 is a valid choice. This will instrument only the transitions between function entry points, but not individual branches. + Note that this is an outdated variable. A few instances (e.g. afl-gcc) + still support these, but state-of-the-art (e.g. LLVM LTO and LLVM PCGUARD) + do not need this. + - AFL_NO_BUILTIN causes the compiler to generate code suitable for use with libtokencap.so (but perhaps running a bit slower than without the flag). - TMPDIR is used by afl-as for temporary files; if this variable is not set, the tool defaults to /tmp. - - Setting AFL_KEEP_ASSEMBLY prevents afl-as from deleting instrumented - assembly files. Useful for troubleshooting problems or understanding how - the tool works. To get them in a predictable place, try something like: - - mkdir assembly_here - TMPDIR=$PWD/assembly_here AFL_KEEP_ASSEMBLY=1 make clean all - - If you are a weird person that wants to compile and instrument asm text files then use the AFL_AS_FORCE_INSTRUMENT variable: AFL_AS_FORCE_INSTRUMENT=1 afl-gcc foo.s -o foo @@ -78,19 +89,24 @@ tools make fairly broad use of environmental variables: - Setting AFL_CAL_FAST will speed up the initial calibration, if the application is very slow -## 2) Settings for afl-clang-fast / afl-clang-fast++ / afl-gcc-fast / afl-g++-fast +## 2) Settings for LLVM and LTO: afl-clang-fast / afl-clang-fast++ / afl-clang-lto / afl-clang-lto++ -The native instrumentation helpers (llvm_mode and gcc_plugin) accept a subset +The native instrumentation helpers (instrumentation and gcc_plugin) accept a subset of the settings discussed in section #1, with the exception of: + - LLVM modes support `AFL_LLVM_DICT2FILE=/absolute/path/file.txt` which will + write all constant string comparisons to this file to be used with + afl-fuzz' `-x` option. + - AFL_AS, since this toolchain does not directly invoke GNU as. - TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are created. - - AFL_INST_RATIO, as we by default use collision free instrumentation. + - AFL_INST_RATIO, as we by default collision free instrumentation is used. + Not all passes support this option though as it is an outdated feature. -Then there are a few specific features that are only available in llvm_mode: +Then there are a few specific features that are only available in instrumentation: ### Select the instrumentation mode @@ -121,7 +137,7 @@ Then there are a few specific features that are only available in llvm_mode: None of the following options are necessary to be used and are rather for manual use (which only ever the author of this LTO implementation will use). - These are used if several seperated instrumentations are performed which + These are used if several seperated instrumentation are performed which are then later combined. - AFL_LLVM_DOCUMENT_IDS=file will document to a file which edge ID was given @@ -136,7 +152,7 @@ Then there are a few specific features that are only available in llvm_mode: - AFL_LLVM_LTO_DONTWRITEID prevents that the highest location ID written into the instrumentation is set in a global variable - See llvm_mode/README.LTO.md for more information. + See instrumentation/README.LTO.md for more information. ### INSTRIM @@ -154,7 +170,7 @@ Then there are a few specific features that are only available in llvm_mode: afl-fuzz will only be able to see the path the loop took, but not how many times it was called (unless it is a complex loop). - See llvm_mode/README.instrim.md + See instrumentation/README.instrim.md ### NGRAM @@ -165,7 +181,7 @@ Then there are a few specific features that are only available in llvm_mode: config.h to at least 18 and maybe up to 20 for this as otherwise too many map collisions occur. - See llvm_mode/README.ctx.md + See instrumentation/README.ctx.md ### CTX @@ -176,7 +192,7 @@ Then there are a few specific features that are only available in llvm_mode: config.h to at least 18 and maybe up to 20 for this as otherwise too many map collisions occur. - See llvm_mode/README.ngram.md + See instrumentation/README.ngram.md ### LAF-INTEL @@ -196,17 +212,17 @@ Then there are a few specific features that are only available in llvm_mode: - Setting AFL_LLVM_LAF_ALL sets all of the above - See llvm_mode/README.laf-intel.md for more information. + See instrumentation/README.laf-intel.md for more information. ### INSTRUMENT LIST (selectively instrument files and functions) - This feature allows selective instrumentation of the source + This feature allows selectively instrumentation of the source - Setting AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST with a filenames and/or function will only instrument (or skip) those files that match the names listed in the specified file. - See llvm_mode/README.instrument_list.md for more information. + See instrumentation/README.instrument_list.md for more information. ### NOT_ZERO @@ -220,27 +236,34 @@ Then there are a few specific features that are only available in llvm_mode: test. If the target performs only few loops then this will give a small performance boost. - See llvm_mode/README.neverzero.md + See instrumentation/README.neverzero.md ### CMPLOG - Setting AFL_LLVM_CMPLOG=1 during compilation will tell afl-clang-fast to - produce a CmpLog binary. See llvm_mode/README.cmplog.md + produce a CmpLog binary. See instrumentation/README.cmplog.md - See llvm_mode/README.neverzero.md + See instrumentation/README.neverzero.md -Then there are a few specific features that are only available in the gcc_plugin: +## 3) Settings for GCC / GCC_PLUGIN modes -### INSTRUMENT_FILE +Then there are a few specific features that are only available in GCC and +GCC_PLUGIN mode. - This feature allows selective instrumentation of the source + - Setting AFL_KEEP_ASSEMBLY prevents afl-as from deleting instrumented + assembly files. Useful for troubleshooting problems or understanding how + the tool works. (GCC mode only) + To get them in a predictable place, try something like: - - Setting AFL_GCC_INSTRUMENT_FILE with a filename will only instrument those - files that match the names listed in this file (one filename per line). + mkdir assembly_here + TMPDIR=$PWD/assembly_here AFL_KEEP_ASSEMBLY=1 make clean all + - Setting AFL_GCC_INSTRUMENT_FILE with a filename will only instrument those + files that match the names listed in this file (one filename per line). See gcc_plugin/README.instrument_list.md for more information. + (GCC_PLUGIN mode only) -## 3) Settings for afl-fuzz +## 4) Settings for afl-fuzz The main fuzzer binary accepts several options that disable a couple of sanity checks or alter some of the more exotic semantics of the tool: @@ -278,14 +301,6 @@ checks or alter some of the more exotic semantics of the tool: don't want AFL to spend too much time classifying that stuff and just rapidly put all timeouts in that bin. - - Setting AFL_FORKSRV_INIT_TMOUT allows yout to specify a different timeout - to wait for the forkserver to spin up. The default is the `-t` value times - `FORK_WAIT_MULT` from `config.h` (usually 10), so for a `-t 100`, the - default would wait `1000` milis. Setting a different time here is useful - if the target has a very slow startup time, for example when doing - full-system fuzzing or emulation, but you don't want the actual runs - to wait too long for timeouts. - - AFL_NO_ARITH causes AFL to skip most of the deterministic arithmetics. This can be useful to speed up the fuzzing of text-based file formats. @@ -377,22 +392,12 @@ checks or alter some of the more exotic semantics of the tool: Note that this setting inhibits some of the user-friendly diagnostics normally done when starting up the forkserver and causes a pretty significant performance drop. - - - Setting AFL_MAX_DET_EXTRAS changes the count of dictionary entries/extras - (default 200), after which the entries will be used probabilistically. - So, if the dict/extras file (`-x`) contains more tokens than this threshold, - not all of the tokens will be used in each fuzzing step, every time. - Instead, there is a chance that the entry will be skipped during fuzzing. - This makes sure that the fuzzer doesn't spend all its time only inserting - the extras, but will still do other mutations. However, it decreases the - likelihood for each token to be inserted, before the next queue entry is fuzzed. - Either way, all tokens will be used eventually, in a longer fuzzing campaign. - Outdated environment variables that are that not supported anymore: AFL_DEFER_FORKSRV AFL_PERSISTENT -## 4) Settings for afl-qemu-trace +## 5) Settings for afl-qemu-trace The QEMU wrapper used to instrument binary-only code supports several settings: @@ -446,7 +451,7 @@ The QEMU wrapper used to instrument binary-only code supports several settings: stack pointer in which QEMU can find the return address when `start addr` is hitted. -## 5) Settings for afl-cmin +## 6) Settings for afl-cmin The corpus minimization script offers very little customization: @@ -472,12 +477,12 @@ to match when minimizing crashes. This will make minimization less useful, but may prevent the tool from "jumping" from one crashing condition to another in very buggy software. You probably want to combine it with the -e flag. -## 7) Settings for afl-analyze +## 8) Settings for afl-analyze You can set AFL_ANALYZE_HEX to get file offsets printed as hexadecimal instead of decimal. -## 8) Settings for libdislocator +## 9) Settings for libdislocator The library honors these environmental variables: @@ -499,12 +504,12 @@ The library honors these environmental variables: - AFL_ALIGNED_ALLOC=1 will force the alignment of the allocation size to max_align_t to be compliant with the C standard. -## 9) Settings for libtokencap +## 10) Settings for libtokencap This library accepts AFL_TOKEN_FILE to indicate the location to which the discovered tokens should be written. -## 10) Third-party variables set by afl-fuzz & other tools +## 11) Third-party variables set by afl-fuzz & other tools Several variables are not directly interpreted by afl-fuzz, but are set to optimal values if not already present in the environment: diff --git a/docs/ideas.md b/docs/ideas.md index 65e2e8e6..a5d40963 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -3,49 +3,6 @@ In the following, we describe a variety of ideas that could be implemented for future AFL++ versions. -For GSOC2020 interested students please see -[https://github.com/AFLplusplus/AFLplusplus/issues/208](https://github.com/AFLplusplus/AFLplusplus/issues/208) - -## Flexible Grammar Mutator (currently in development) - -Currently, AFL++'s mutation does not have deeper knowledge about the fuzzed -binary, apart from feedback, even though the developer may have insights -about the target. - -A developer may choose to provide dictionaries and implement own mutations -in python or C, but an easy mutator that behaves according to a given grammar, -does not exist. - -State-of-the-art research on grammar fuzzing has some problems in their -implementations like code quality, scalability, or ease of use and other -common issues of the academic code. - -We aim to develop a pluggable grammar mutator for afl++ that combines -various results. - -Mentor: andreafioraldi - -## perf-fuzz Linux Kernel Module - -Expand on [snapshot LKM](https://github.com/AFLplusplus/AFL-Snapshot-LKM) -To make it thread safe, can snapshot several processes at once and increase -overall performance. - -Mentor: any - -## QEMU 5-based Instrumentation - -First tests to use QEMU 4 for binary-only AFL++ showed that caching behavior -changed, which vastly decreases fuzzing speeds. - -In this task test if QEMU 5 performs better and port the afl++ QEMU 3.1 -patches to QEMU 5. - -Understanding the current instrumentation and fixing the current caching -issues will be needed. - -Mentor: andreafioraldi - ## WASM Instrumentation Currently, AFL++ can be used for source code fuzzing and traditional binaries. @@ -66,20 +23,6 @@ Either improve a single mutator thorugh learning of many different bugs Mentor: domenukk -## Reengineer `afl-fuzz` as Thread Safe, Embeddable Library (currently in development) - -Right now, afl-fuzz is single threaded, cannot safely be embedded in tools, -and not multi-threaded. It makes use of a large number of globals, must always -be the parent process and exec child processes. -Instead, afl-fuzz could be refactored to contain no global state and globals. -This allows for different use cases that could be implemented during this -project. -Note that in the mean time a lot has happened here already, but e.g. making -it all work and implement multithreading in afl-fuzz ... there is still quite -some work to do. - -Mentor: hexcoder- or vanhauser-thc - ## Collision-free Binary-Only Maps AFL++ supports collison-free maps using an LTO (link-time-optimization) pass. diff --git a/docs/life_pro_tips.md b/docs/life_pro_tips.md index a5bd7286..0004c297 100644 --- a/docs/life_pro_tips.md +++ b/docs/life_pro_tips.md @@ -30,10 +30,10 @@ Check out the `fuzzer_stats` file in the AFL output dir or try `afl-whatsup`. It could be important - consult docs/status_screen.md right away! ## Know your target? Convert it to persistent mode for a huge performance gain! -Consult section #5 in llvm_mode/README.md for tips. +Consult section #5 in README.llvm.md for tips. ## Using clang? -Check out llvm_mode/ for a faster alternative to afl-gcc! +Check out instrumentation/ for a faster alternative to afl-gcc! ## Did you know that AFL can fuzz closed-source or cross-platform binaries? Check out qemu_mode/README.md and unicorn_mode/README.md for more. diff --git a/docs/perf_tips.md b/docs/perf_tips.md index 731dc238..fbcb4d8d 100644 --- a/docs/perf_tips.md +++ b/docs/perf_tips.md @@ -51,7 +51,7 @@ a file. ## 3. Use LLVM instrumentation When fuzzing slow targets, you can gain 20-100% performance improvement by -using the LLVM-based instrumentation mode described in [the llvm_mode README](../llvm_mode/README.md). +using the LLVM-based instrumentation mode described in [the instrumentation README](../instrumentation/README.llvm.md). Note that this mode requires the use of clang and will not work with GCC. The LLVM mode also offers a "persistent", in-process fuzzing mode that can @@ -62,12 +62,12 @@ modes require you to edit the source code of the fuzzed program, but the changes often amount to just strategically placing a single line or two. If there are important data comparisons performed (e.g. `strcmp(ptr, MAGIC_HDR)`) -then using laf-intel (see llvm_mode/README.laf-intel.md) will help `afl-fuzz` a lot +then using laf-intel (see instrumentation/README.laf-intel.md) will help `afl-fuzz` a lot to get to the important parts in the code. If you are only interested in specific parts of the code being fuzzed, you can instrument_files the files that are actually relevant. This improves the speed and -accuracy of afl. See llvm_mode/README.instrument_list.md +accuracy of afl. See instrumentation/README.instrument_list.md Also use the InsTrim mode on larger binaries, this improves performance and coverage a lot. @@ -110,7 +110,7 @@ e.g.: https://launchpad.net/libeatmydata In programs that are slow due to unavoidable initialization overhead, you may -want to try the LLVM deferred forkserver mode (see llvm_mode/README.md), +want to try the LLVM deferred forkserver mode (see README.llvm.md), which can give you speed gains up to 10x, as mentioned above. Last but not least, if you are using ASAN and the performance is unacceptable, diff --git a/docs/sister_projects.md b/docs/sister_projects.md index a501ecbd..640e59f7 100644 --- a/docs/sister_projects.md +++ b/docs/sister_projects.md @@ -52,7 +52,7 @@ options. Provides an evolutionary instrumentation-guided fuzzing harness that allows some programs to be fuzzed without the fork / execve overhead. (Similar functionality is now available as the "persistent" feature described in -[the llvm_mode readme](../llvm_mode/README.md)) +[the llvm_mode readme](../instrumentation/README.llvm.md)) http://llvm.org/docs/LibFuzzer.html @@ -245,7 +245,7 @@ https://code.google.com/p/address-sanitizer/wiki/AsanCoverage#Coverage_counters ### AFL JS (Han Choongwoo) One-off optimizations to speed up the fuzzing of JavaScriptCore (now likely -superseded by LLVM deferred forkserver init - see llvm_mode/README.md). +superseded by LLVM deferred forkserver init - see README.llvm.md). https://github.com/tunz/afl-fuzz-js diff --git a/docs/status_screen.md b/docs/status_screen.md index b89468ce..2eeb8f3f 100644 --- a/docs/status_screen.md +++ b/docs/status_screen.md @@ -324,7 +324,7 @@ there are several things to look at: - Multiple threads executing at once in semi-random order. This is harmless when the 'stability' metric stays over 90% or so, but can become an issue if not. Here's what to try: - * Use afl-clang-fast from [llvm_mode](../llvm_mode/) - it uses a thread-local tracking + * Use afl-clang-fast from [instrumentation](../instrumentation/) - it uses a thread-local tracking model that is less prone to concurrency issues, * See if the target can be compiled or run without threads. Common `./configure` options include `--without-threads`, `--disable-pthreads`, or diff --git a/examples/README.md b/examples/README.md index d28aadbe..46a92c6e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -47,7 +47,7 @@ Here's a quick overview of the stuff you can find in this directory: Note that the minimize_corpus.sh tool has graduated from the examples/ directory and is now available as ../afl-cmin. The LLVM mode has likewise -graduated to ../llvm_mode/*. +graduated to ../instrumentation/*. Most of the tools in this directory are meant chiefly as examples that need to be tweaked for your specific needs. They come with some basic documentation, diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c index ff5446e9..82e55fc4 100644 --- a/examples/aflpp_driver/aflpp_driver.c +++ b/examples/aflpp_driver/aflpp_driver.c @@ -27,7 +27,7 @@ EOF # Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang. clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c # Build afl-llvm-rt.o.c from the AFL distribution. -clang -c -w $AFL_HOME/llvm_mode/afl-llvm-rt.o.c +clang -c -w $AFL_HOME/instrumentation/afl-llvm-rt.o.c # Build this file, link it with afl-llvm-rt.o.o and the target code. clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o # Run AFL: diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 4281c554..9e469864 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -935,6 +935,7 @@ u8 has_new_bits(afl_state_t *, u8 *); void load_extras_file(afl_state_t *, u8 *, u32 *, u32 *, u32); void load_extras(afl_state_t *, u8 *); +void dedup_extras(afl_state_t *); void add_extra(afl_state_t *afl, u8 *mem, u32 len); void maybe_add_auto(afl_state_t *, u8 *, u32); void save_auto(afl_state_t *); @@ -972,7 +973,7 @@ u8 fuzz_one(afl_state_t *); void bind_to_free_cpu(afl_state_t *); #endif void setup_post(afl_state_t *); -void read_testcases(afl_state_t *); +void read_testcases(afl_state_t *, u8 *); void perform_dry_run(afl_state_t *); void pivot_inputs(afl_state_t *); u32 find_start_position(afl_state_t *); diff --git a/include/config.h b/include/config.h index 77407d50..8cc70075 100644 --- a/include/config.h +++ b/include/config.h @@ -28,7 +28,7 @@ /* Version string: */ // c = release, d = volatile github dev, e = experimental branch -#define VERSION "++2.68c" +#define VERSION "++3.00a" /****************************************************** * * @@ -195,7 +195,7 @@ steps; past this point, the "extras/user" step will be still carried out, but with proportionally lower odds: */ -#define MAX_DET_EXTRAS 200 +#define MAX_DET_EXTRAS 256 /* Maximum number of auto-extracted dictionary tokens to actually use in fuzzing (first value), and to keep in memory as candidates. The latter should be much diff --git a/include/envs.h b/include/envs.h index 2dc1dbbf..d9968fcd 100644 --- a/include/envs.h +++ b/include/envs.h @@ -69,6 +69,7 @@ static char *afl_environment_variables[] = { "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM", "AFL_LLVM_CTX", + "AFL_LLVM_DICT2FILE", "AFL_LLVM_DOCUMENT_IDS", "AFL_LLVM_INSTRUMENT", "AFL_LLVM_INSTRIM_LOOPHEAD", diff --git a/qemu_mode/patches/afl-qemu-cpu-inl.h b/qemu_mode/patches/afl-qemu-cpu-inl.h index 63b7581d..0e38f38b 100644 --- a/qemu_mode/patches/afl-qemu-cpu-inl.h +++ b/qemu_mode/patches/afl-qemu-cpu-inl.h @@ -466,7 +466,7 @@ void afl_forkserver(CPUState *cpu) { } /* A simplified persistent mode handler, used as explained in - * llvm_mode/README.md. */ + * instrumentation/README.llvm.md */ void afl_persistent_loop(void) { diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 713849a1..1351d274 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -626,6 +626,7 @@ if (dir == NULL) { if (!access(fn1, F_OK)) { afl->in_dir = fn1; + subdirs = 0; } else { @@ -1063,7 +1064,7 @@ restart_outer_cull_loop: } - afl->q_prev100 = afl->queue = afl->queue_top = afl->queue; + afl->queue = afl->queue_top = afl->queue; } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index db91813b..af52aa45 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -138,8 +138,7 @@ static u8 check_if_text(struct queue_entry *q) { } // non-overlong 2-byte - if (len - offset > 1 && - ((0xC2 <= buf[offset + 0] && buf[offset + 0] <= 0xDF) && + if (len - offset > 1 && ((0xC2 <= buf[offset + 0] && buf[offset + 0] <= 0xDF) && (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF))) { offset += 2; @@ -230,7 +229,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { } else { - afl->q_prev100 = afl->queue = afl->queue_top = q; + afl->queue = afl->queue_top = q; } @@ -274,15 +273,15 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { void destroy_queue(afl_state_t *afl) { - struct queue_entry *q = afl->queue, *n; + struct queue_entry *q; + u32 i; - while (q) { + for (i = 0; i < afl->queued_paths; i++) { - n = q->next; + q = afl->queue_buf[i]; ck_free(q->fname); ck_free(q->trace_mini); ck_free(q); - q = n; } diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 51eed14b..c60c65aa 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -982,10 +982,9 @@ void show_stats(afl_state_t *afl) { void show_init_stats(afl_state_t *afl) { struct queue_entry *q = afl->queue; - u32 min_bits = 0, max_bits = 0; + u32 min_bits = 0, max_bits = 0, max_len = 0, count = 0; u64 min_us = 0, max_us = 0; u64 avg_us = 0; - u32 max_len = 0; u8 val_bufs[4][STRINGIFY_VAL_SIZE_MAX]; #define IB(i) val_bufs[(i)], sizeof(val_bufs[(i)]) @@ -1006,6 +1005,7 @@ void show_init_stats(afl_state_t *afl) { if (q->len > max_len) { max_len = q->len; } + ++count; q = q->next; } @@ -1072,10 +1072,10 @@ void show_init_stats(afl_state_t *afl) { OKF("Here are some useful stats:\n\n" cGRA " Test case count : " cRST - "%u favored, %u variable, %u total\n" cGRA " Bitmap range : " cRST + "%u favored, %u variable, %u ignored, %u total\n" cGRA " Bitmap range : " cRST "%u to %u bits (average: %0.02f bits)\n" cGRA " Exec timing : " cRST "%s to %s us (average: %s us)\n", - afl->queued_favored, afl->queued_variable, afl->queued_paths, min_bits, + afl->queued_favored, afl->queued_variable, afl->queued_paths - count, afl->queued_paths, min_bits, max_bits, ((double)afl->total_bitmap_size) / (afl->total_bitmap_entries ? afl->total_bitmap_entries : 1), diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index bfaa22e8..73ca6aaa 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1133,8 +1133,9 @@ int main(int argc, char **argv_orig, char **envp) { setup_cmdline_file(afl, argv + optind); - read_testcases(afl); + read_testcases(afl, NULL); // read_foreign_testcases(afl, 1); for the moment dont do this + OKF("Loaded a total of %u seeds.", afl->queued_paths); load_auto(afl); diff --git a/test/test-gcc-plugin.sh b/test/test-gcc-plugin.sh index 2ed10a72..8b8cbd8e 100755 --- a/test/test-gcc-plugin.sh +++ b/test/test-gcc-plugin.sh @@ -3,7 +3,7 @@ . ./test-pre.sh $ECHO "$BLUE[*] Testing: gcc_plugin" -test -e ../afl-gcc-fast -a -e ../afl-gcc-rt.o && { +test -e ../afl-gcc-fast -a -e ../afl-compiler-rt.o && { SAVE_AFL_CC=${AFL_CC} export AFL_CC=`command -v gcc` ../afl-gcc-fast -o test-instr.plain.gccpi ../test-instr.c > /dev/null 2>&1 diff --git a/test/test-unittests.sh b/test/test-unittests.sh index f540b5f8..58c2eea9 100755 --- a/test/test-unittests.sh +++ b/test/test-unittests.sh @@ -7,3 +7,5 @@ unset AFL_CC make -C .. unit || CODE=1 INCOMPLETE=1 : . ./test-post.sh + +rm -rf unittests/unit_hash unittests/unit_rand -- cgit 1.4.1 From 2f90f2faba92c0ef5e081ff74b54fb07eb1faaa9 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 5 Sep 2020 13:19:19 +0200 Subject: code-format --- src/afl-fuzz-init.c | 23 +++++++++++++---------- src/afl-fuzz-queue.c | 3 ++- src/afl-fuzz-stats.c | 16 +++++++++------- 3 files changed, 24 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 1351d274..c834e5db 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -617,24 +617,27 @@ void read_testcases(afl_state_t *afl, u8 *directory) { s32 nl_cnt, subdirs = 1; u32 i; u8 * fn1, *dir = directory; - u8 val_buf[2][STRINGIFY_VAL_SIZE_MAX]; + u8 val_buf[2][STRINGIFY_VAL_SIZE_MAX]; /* Auto-detect non-in-place resumption attempts. */ -if (dir == NULL) { - fn1 = alloc_printf("%s/queue", afl->in_dir); - if (!access(fn1, F_OK)) { + if (dir == NULL) { - afl->in_dir = fn1; - subdirs = 0; + fn1 = alloc_printf("%s/queue", afl->in_dir); + if (!access(fn1, F_OK)) { - } else { + afl->in_dir = fn1; + subdirs = 0; + + } else { - ck_free(fn1); + ck_free(fn1); + + } + + dir = afl->in_dir; } - dir = afl->in_dir; -} ACTF("Scanning '%s'...", dir); diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index af52aa45..8c7bfc55 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -138,7 +138,8 @@ static u8 check_if_text(struct queue_entry *q) { } // non-overlong 2-byte - if (len - offset > 1 && ((0xC2 <= buf[offset + 0] && buf[offset + 0] <= 0xDF) && + if (len - offset > 1 && + ((0xC2 <= buf[offset + 0] && buf[offset + 0] <= 0xDF) && (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF))) { offset += 2; diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index c60c65aa..dfc0cd97 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -35,12 +35,12 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { u8 fn[PATH_MAX]; snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); FILE *f = create_ffile(fn); - u32 i; + u32 i; fprintf(f, "# environment variables:\n"); - u32 s_afl_env = (u32) - sizeof(afl_environment_variables) / sizeof(afl_environment_variables[0]) - - 1U; + u32 s_afl_env = (u32)sizeof(afl_environment_variables) / + sizeof(afl_environment_variables[0]) - + 1U; for (i = 0; i < s_afl_env; ++i) { @@ -75,6 +75,7 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { } } + fprintf(f, "\n"); fclose(f); @@ -1072,11 +1073,12 @@ void show_init_stats(afl_state_t *afl) { OKF("Here are some useful stats:\n\n" cGRA " Test case count : " cRST - "%u favored, %u variable, %u ignored, %u total\n" cGRA " Bitmap range : " cRST + "%u favored, %u variable, %u ignored, %u total\n" cGRA + " Bitmap range : " cRST "%u to %u bits (average: %0.02f bits)\n" cGRA " Exec timing : " cRST "%s to %s us (average: %s us)\n", - afl->queued_favored, afl->queued_variable, afl->queued_paths - count, afl->queued_paths, min_bits, - max_bits, + afl->queued_favored, afl->queued_variable, afl->queued_paths - count, + afl->queued_paths, min_bits, max_bits, ((double)afl->total_bitmap_size) / (afl->total_bitmap_entries ? afl->total_bitmap_entries : 1), stringify_int(IB(0), min_us), stringify_int(IB(1), max_us), -- cgit 1.4.1 From ded4d093ff59b4459b04aaae9b3b7bbcdaadcdef Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 5 Sep 2020 16:16:56 +0200 Subject: skip crashes but keep for splices --- docs/Changelog.md | 4 ++++ src/afl-fuzz-init.c | 35 +++++++++++++++++++++++++++++------ src/afl-fuzz.c | 3 +++ 3 files changed, 36 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index f86c0b61..a3c05ed3 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -17,6 +17,10 @@ sending a mail to . - reading testcases from -i now descends into subdirectories - allow up to 4 -x command line options - loaded extras now have a duplicate protection + - If test cases are too large we do a partial read on the maximum + supported size + - longer seeds with the same trace information will now be ignored + for fuzzing but still be used for splicing - instrumentation - not overriding -Ox or -fno-unroll-loops anymore - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index c834e5db..a5ebbcd8 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -713,11 +713,9 @@ void read_testcases(afl_state_t *afl, u8 *directory) { if (st.st_size > MAX_FILE) { - WARNF("Test case '%s' is too big (%s, limit is %s), skipping", fn2, + WARNF("Test case '%s' is too big (%s, limit is %s), partial reading", fn2, stringify_mem_size(val_buf[0], sizeof(val_buf[0]), st.st_size), stringify_mem_size(val_buf[1], sizeof(val_buf[1]), MAX_FILE)); - ck_free(fn2); - continue; } @@ -728,7 +726,8 @@ void read_testcases(afl_state_t *afl, u8 *directory) { if (!access(dfn, F_OK)) { passed_det = 1; } - add_to_queue(afl, fn2, st.st_size, passed_det); + add_to_queue(afl, fn2, st.st_size >= MAX_FILE ? MAX_FILE : st.st_size, + passed_det); } @@ -947,7 +946,31 @@ void perform_dry_run(afl_state_t *afl) { #undef MSG_ULIMIT_USAGE #undef MSG_FORK_ON_APPLE - FATAL("Test case '%s' results in a crash", fn); + WARNF("Test case '%s' results in a crash, skipping", fn); + + /* Remove from fuzzing queue but keep for splicing */ + + struct queue_entry *p = afl->queue; + while (p && p->next != q) + p = p->next; + + if (p) + p->next = q->next; + else + afl->queue = q->next; + + --afl->pending_not_fuzzed; + + afl->max_depth = 0; + p = afl->queue; + while (p) { + + if (p->depth > afl->max_depth) afl->max_depth = p->depth; + p = p->next; + + } + + break; case FSRV_RUN_ERROR: @@ -1067,7 +1090,7 @@ restart_outer_cull_loop: } - afl->queue = afl->queue_top = afl->queue; + afl->queue_top = afl->queue; } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 73ca6aaa..a8816cb3 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1282,6 +1282,9 @@ int main(int argc, char **argv_orig, char **envp) { cull_queue(afl); + if (!afl->pending_not_fuzzed) + FATAL("We need at least on valid input seed that does not crash!"); + show_init_stats(afl); seek_to = find_start_position(afl); -- cgit 1.4.1 From 163e5ffd10936e6e119f643495129ab05fa3e5ec Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 5 Sep 2020 17:40:39 +0200 Subject: -p seek is now the default --- docs/Changelog.md | 3 +++ src/afl-fuzz-state.c | 2 +- src/afl-fuzz.c | 19 +++++++++---------- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index a3c05ed3..b4c575a6 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -21,6 +21,9 @@ sending a mail to . supported size - longer seeds with the same trace information will now be ignored for fuzzing but still be used for splicing + - crashing seeds are now not prohibiting a run anymore but are + skipped. They are used for splicing though. + - set the default power schedule to the superiour "seek" schedule - instrumentation - not overriding -Ox or -fno-unroll-loops anymore - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 577fc34f..4e817843 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -87,7 +87,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->w_end = 0.3; afl->g_max = 5000; afl->period_pilot_tmp = 5000.0; - afl->schedule = EXPLORE; /* Power schedule (default: EXPLORE)*/ + afl->schedule = SEEK; /* Power schedule (default: SEEK) */ afl->havoc_max_mult = HAVOC_MAX_MULT; afl->clear_screen = 1; /* Window resized? */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index a8816cb3..5b96ef45 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -89,11 +89,10 @@ static void usage(u8 *argv0, int more_help) { " -o dir - output directory for fuzzer findings\n\n" "Execution control settings:\n" - " -p schedule - power schedules compute a seed's performance score. " - "\n" - " see docs/power_schedules.md\n" + " -p schedule - power schedules compute a seed's performance score:\n" + " -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " "or @@)\n" " -t msec - timeout for each run (auto-scaled, 50-%d ms)\n" @@ -349,15 +348,15 @@ int main(int argc, char **argv_orig, char **envp) { afl->schedule = RARE; - } else if (!stricmp(optarg, "seek")) { + } else if (!stricmp(optarg, "explore") || !stricmp(optarg, "afl")) { - afl->schedule = SEEK; + afl->schedule = EXPLORE; - } else if (!stricmp(optarg, "explore") || !stricmp(optarg, "default") || + } else if (!stricmp(optarg, "seek") || !stricmp(optarg, "default") || - !stricmp(optarg, "normal") || !stricmp(optarg, "afl")) { + !stricmp(optarg, "normal")) { - afl->schedule = EXPLORE; + afl->schedule = SEEK; } else { -- cgit 1.4.1 From 8ec41b2585390c54256dd823e1beafd6246d9976 Mon Sep 17 00:00:00 2001 From: aflpp Date: Sun, 6 Sep 2020 11:40:41 +0200 Subject: fix various warnings --- GNUmakefile.llvm | 6 ++---- instrumentation/SanitizerCoverageLTO.so.cc | 12 ++++++++---- instrumentation/afl-llvm-dict2file.so.cc | 2 +- src/afl-cc.c | 6 +----- 4 files changed, 12 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 1bb3d265..d432021b 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -422,13 +422,11 @@ document: ./afl-compiler-rt-32.o: instrumentation/afl-compiler-rt.o.c | test_deps @printf "[*] Building 32-bit variant of the runtime (-m32)... " - @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi - @test -e afl-compiler-rt-32.o && ln -sf afl-compiler-rt-32.o afl-llvm-rt-64.o + @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; ln -sf afl-compiler-rt-32.o afl-llvm-rt-32.o; else echo "failed (that's fine)"; fi ./afl-compiler-rt-64.o: instrumentation/afl-compiler-rt.o.c | test_deps @printf "[*] Building 64-bit variant of the runtime (-m64)... " - @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi - @test -e afl-compiler-rt-64.o && ln -sf afl-compiler-rt-64.o afl-llvm-rt-64.o + @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; ln -sf afl-compiler-rt-64.o afl-llvm-rt-64.o; else echo "failed (that's fine)"; fi .PHONY: test_build test_build: $(PROGS) diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index f4958d80..0a136d6f 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -197,8 +197,8 @@ class ModuleSanitizerCoverage { void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks); void InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx, bool IsLeafFunc = true); - std::pair CreateSecStartEnd(Module &M, const char *Section, - Type *Ty); +// std::pair CreateSecStartEnd(Module &M, const char *Section, +// Type *Ty); void SetNoSanitizeMetadata(Instruction *I) { @@ -208,8 +208,8 @@ class ModuleSanitizerCoverage { } std::string getSectionName(const std::string &Section) const; - std::string getSectionStart(const std::string &Section) const; - std::string getSectionEnd(const std::string &Section) const; +// std::string getSectionStart(const std::string &Section) const; +// std::string getSectionEnd(const std::string &Section) const; FunctionCallee SanCovTracePCIndir; FunctionCallee SanCovTracePC /*, SanCovTracePCGuard*/; Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy, @@ -349,6 +349,7 @@ PreservedAnalyses ModuleSanitizerCoveragePass::run(Module & M, } +/* std::pair ModuleSanitizerCoverage::CreateSecStartEnd( Module &M, const char *Section, Type *Ty) { @@ -373,6 +374,7 @@ std::pair ModuleSanitizerCoverage::CreateSecStartEnd( return std::make_pair(IRB.CreatePointerCast(GEP, Ty), SecEndPtr); } +*/ bool ModuleSanitizerCoverage::instrumentModule( Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { @@ -1536,6 +1538,7 @@ std::string ModuleSanitizerCoverage::getSectionName( } +/* std::string ModuleSanitizerCoverage::getSectionStart( const std::string &Section) const { @@ -1553,6 +1556,7 @@ std::string ModuleSanitizerCoverage::getSectionEnd( return "__stop___" + Section; } +*/ char ModuleSanitizerCoverageLegacyPass::ID = 0; diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc index e87ecce8..ef42756e 100644 --- a/instrumentation/afl-llvm-dict2file.so.cc +++ b/instrumentation/afl-llvm-dict2file.so.cc @@ -84,7 +84,7 @@ class AFLdict2filePass : public ModulePass { void dict2file(int fd, u8 *mem, u32 len) { - int i, j, binary = 0; + u32 i, j, binary = 0; char line[MAX_AUTO_EXTRA * 8], tmp[8]; strcpy(line, "\""); diff --git a/src/afl-cc.c b/src/afl-cc.c index e11ce40a..ddda3845 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1153,9 +1153,6 @@ int main(int argc, char **argv, char **envp) { if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) { - char *fp; - fp = realpath(argv[0], NULL); - printf("afl-cc" VERSION " by Michal Zalewski, Laszlo Szekeres, Marc Heuse\n"); @@ -1302,8 +1299,7 @@ int main(int argc, char **argv, char **envp) { " AFL_USE_ASAN: activate address sanitizer\n" " AFL_USE_CFISAN: activate control flow sanitizer\n" " AFL_USE_MSAN: activate memory sanitizer\n" - " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n", - BIN_PATH, BIN_PATH); + " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n"); SAYF( "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " -- cgit 1.4.1 From 7bcbfd48e54eba5a99d05b04f4f3d6bea29cde80 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 6 Sep 2020 15:12:23 +0200 Subject: update ideas --- docs/ideas.md | 13 +++++++++++++ src/README.md | 33 +++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/docs/ideas.md b/docs/ideas.md index a5d40963..aaa3eed1 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -3,6 +3,19 @@ In the following, we describe a variety of ideas that could be implemented for future AFL++ versions. +## Analysis software + +Currently analysis is done by using afl-plot, which is rather outdated. +A GTK or browser tool to create run-time analysis based on fuzzer_stats, +queue/id* information and plot_data that allows for zooming in and out, +changing min/max display values etc. and doing that for a single run, +different runs and campaigns vs campaigns. +Interesting values are execs, and execs/s, edges discovered (total, when +each edge was discovered and which other fuzzer share finding that edge), +test cases executed. +It should be clickable which value is X and Y axis, zoom factor, log scaling +on-off, etc. + ## WASM Instrumentation Currently, AFL++ can be used for source code fuzzing and traditional binaries. diff --git a/src/README.md b/src/README.md index 6da534c3..35af6ab9 100644 --- a/src/README.md +++ b/src/README.md @@ -2,23 +2,28 @@ Quick explanation about the files here: -- `afl-analyze.c` - afl-analyze binary tool +- `afl-analyze.c` - afl-analyze binary tool - `afl-as.c` - afl-as binary tool -- `afl-gotcpu.c` - afl-gotcpu binary tool -- `afl-showmap.c` - afl-showmap binary tool -- `afl-tmin.c` - afl-tmin binary tool -- `afl-fuzz.c` - afl-fuzz binary tool (just main() and usage()) +- `afl-cc.c` - afl-cc binary tool +- `afl-common.c` - common functions, used by afl-analyze, afl-fuzz, afl-showmap and afl-tmin +- `afl-forkserver.c` - forkserver implementation, used by afl-fuzz afl-showmap, afl-tmin - `afl-fuzz-bitmap.c` - afl-fuzz bitmap handling +- `afl-fuzz.c` - afl-fuzz binary tool (just main() and usage()) +- `afl-fuzz-cmplog.c` - afl-fuzz cmplog functions - `afl-fuzz-extras.c` - afl-fuzz the *extra* function calls -- `afl-fuzz-state.c` - afl-fuzz state and globals -- `afl-fuzz-init.c` - afl-fuzz initialization -- `afl-fuzz-misc.c` - afl-fuzz misc functions -- `afl-fuzz-one.c` - afl-fuzz fuzzer_one big loop, this is where the mutation is happening +- `afl-fuzz-init.c` - afl-fuzz initialization +- `afl-fuzz-misc.c` - afl-fuzz misc functions +- `afl-fuzz-mutators.c` - afl-fuzz custom mutator and python support +- `afl-fuzz-one.c` - afl-fuzz fuzzer_one big loop, this is where the mutation is happening +- `afl-fuzz-performance.c` - hash64 and rand functions - `afl-fuzz-python.c` - afl-fuzz the python mutator extension - `afl-fuzz-queue.c` - afl-fuzz handling the queue -- `afl-fuzz-run.c` - afl-fuzz running the target +- `afl-fuzz-redqueen.c` - afl-fuzz redqueen implemention +- `afl-fuzz-run.c` - afl-fuzz running the target +- `afl-fuzz-state.c` - afl-fuzz state and globals - `afl-fuzz-stats.c` - afl-fuzz writing the statistics file -- `afl-gcc.c` - afl-gcc binary tool (deprecated) -- `afl-common.c` - common functions, used by afl-analyze, afl-fuzz, afl-showmap and afl-tmin -- `afl-forkserver.c` - forkserver implementation, used by afl-fuzz and afl-tmin -afl-sharedmem.c - sharedmem implementation, used by afl-fuzz and afl-tmin +- `afl-gotcpu.c` - afl-gotcpu binary tool +- `afl-ld-lto.c` - LTO linker helper +- `afl-sharedmem.c` - sharedmem implementation, used by afl-fuzz, afl-showmap, afl-tmin +- `afl-showmap.c` - afl-showmap binary tool +- `afl-tmin.c` - afl-tmin binary tool -- cgit 1.4.1 From 6404abd7d609350ffd6c6f221cbf56e60b2ef030 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 7 Sep 2020 17:30:28 +0200 Subject: bugfix for fixed seeds --- src/afl-fuzz-queue.c | 2 +- src/afl-fuzz-stats.c | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 8c7bfc55..336b7f4f 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -502,7 +502,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { // Longer execution time means longer work on the input, the deeper in // coverage, the better the fuzzing, right? -mh - if (afl->schedule >= RARE && likely(!afl->fixed_seed)) { + if (likely(afl->schedule < RARE) && likely(!afl->fixed_seed)) { if (q->exec_us * 0.1 > avg_exec_us) { diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index dfc0cd97..1d5b169d 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -1022,7 +1022,11 @@ void show_init_stats(afl_state_t *afl) { /* Let's keep things moving with slow binaries. */ - if (avg_us > 50000) { + if (unlikely(afl->fixed_seed)) { + + afl->havoc_div = 1; + + } else if (avg_us > 50000) { afl->havoc_div = 10; /* 0-19 execs/sec */ @@ -1093,7 +1097,11 @@ void show_init_stats(afl_state_t *afl) { random scheduler jitter is less likely to have any impact, and because our patience is wearing thin =) */ - if (avg_us > 50000) { + if (unlikely(afl->fixed_seed)) { + + afl->fsrv.exec_tmout = avg_us * 5 / 1000; + + } else if (avg_us > 50000) { afl->fsrv.exec_tmout = avg_us * 2 / 1000; -- cgit 1.4.1 From 374e068f97e07831f30b6253157560565240fd52 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 8 Sep 2020 11:37:09 +0200 Subject: set correct error code when -V --- src/afl-forkserver.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 58932bc4..c8056b9e 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -1043,7 +1043,12 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, } - if (fsrv->child_pid <= 0) { FATAL("Fork server is misbehaving (OOM?)"); } + if (fsrv->child_pid <= 0) { + + if (*stop_soon_p) { return 0; } + FATAL("Fork server is misbehaving (OOM?)"); + + } exec_ms = read_s32_timed(fsrv->fsrv_st_fd, &fsrv->child_status, timeout, stop_soon_p); -- cgit 1.4.1 From 9544b3dbf22f1007f7d3f77593ec746a0345a587 Mon Sep 17 00:00:00 2001 From: Alexandre Oliva Date: Mon, 7 Sep 2020 12:35:31 -0300 Subject: rewrite gcc plugin When we started using AFL, it did not have an integrated GCC plugin. There was one proposed by Austin Seipp, but for various reasons we ended up using some of its infrastructure (runtime and wrapper), but writing the GCC plugin proper from scratch. With AFL++'s renewed interest in a GCC plugin, we rebased ours, with some features that are or were missing in the one that was integrated: * efficient, fully-functional inline and out-of-line instrumentation Inline instrumentation was work in progress in the original plugin. Controlled by AFL_GCC_OUT_OF_LINE. * reproducible instrumentation Obey -frandom-seed for pseudorandom number generation. * licensing clarity and strict compliance GPLv3+ for the plugin, that uses GCC internals; add a copy of the license, as required. * allow/deny list support Copied and adjusted from the LLVM plugin implementation. * neverZero support Not as compact as the asm-wrapper version, but likely more efficient. Both are quite thread-unsafe, with different caveats. Controlled with AFL_GCC_SKIP_NEVERZERO. --- GNUmakefile.llvm | 2 +- docs/INSTALL.md | 5 +- include/envs.h | 5 + instrumentation/COPYING3 | 674 ++++++++++++++++++ instrumentation/README.out_of_line.md | 21 + instrumentation/afl-compiler-rt.o.c | 18 +- instrumentation/afl-gcc-pass.so.cc | 1218 +++++++++++++++++++++------------ src/afl-cc.c | 6 + test/test-performance.sh | 43 +- 9 files changed, 1545 insertions(+), 447 deletions(-) create mode 100644 instrumentation/COPYING3 create mode 100644 instrumentation/README.out_of_line.md (limited to 'src') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index d432021b..3eefdf90 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -221,7 +221,7 @@ endif ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fdebug-prefix-map=$(CURDIR)=llvm_mode -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" AFL_CLANG_DEBUG_PREFIX = -fdebug-prefix-map="$(CURDIR)=llvm_mode" else - AFL_CLANG_DEBUG_PREFIX = "" + AFL_CLANG_DEBUG_PREFIX = endif CFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2 diff --git a/docs/INSTALL.md b/docs/INSTALL.md index fb7b5642..93a46caf 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -28,6 +28,8 @@ If you are using clang, please review README.llvm.md; the LLVM integration mode can offer substantial performance gains compared to the traditional approach. +Likewise, if you are using GCC, please review gcc_plugin/README.md. + You may have to change several settings to get optimal results (most notably, disable crash reporting utilities and switch to a different CPU governor), but afl-fuzz will guide you through that if necessary. @@ -157,7 +159,8 @@ instrumentation mode (`-Q`) will not work. ## 6. Everything else You're on your own. On POSIX-compliant systems, you may be able to compile and -run the fuzzer; and the LLVM mode may offer a way to instrument non-x86 code. +run the fuzzer; and the LLVM and GCC plugin modes may offer a way to instrument +non-x86 code. The fuzzer will run on Windows in WSL only. It will not work under Cygwin on in the normal Windows world. It could be ported to the latter platform fairly easily, but it's a pretty bad diff --git a/include/envs.h b/include/envs.h index d9968fcd..3a06aa2a 100644 --- a/include/envs.h +++ b/include/envs.h @@ -45,7 +45,12 @@ static char *afl_environment_variables[] = { "AFL_EXIT_WHEN_DONE", "AFL_FAST_CAL", "AFL_FORCE_UI", + "AFL_GCC_ALLOWLIST", + "AFL_GCC_DENYLIST", + "AFL_GCC_BLOCKLIST", "AFL_GCC_INSTRUMENT_FILE", + "AFL_GCC_OUT_OF_LINE", + "AFL_GCC_SKIP_NEVERZERO", "AFL_GCJ", "AFL_HANG_TMOUT", "AFL_FORKSRV_INIT_TMOUT", diff --git a/instrumentation/COPYING3 b/instrumentation/COPYING3 new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/instrumentation/COPYING3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/instrumentation/README.out_of_line.md b/instrumentation/README.out_of_line.md new file mode 100644 index 00000000..aad215b6 --- /dev/null +++ b/instrumentation/README.out_of_line.md @@ -0,0 +1,21 @@ +=========================================== +Using afl++ without inlined instrumentation +=========================================== + + This file describes how you can disable inlining of instrumentation. + + +By default, the GCC plugin will duplicate the effects of calling +__afl_trace (see afl-gcc-rt.o.c) in instrumented code, instead of +issuing function calls. + +The calls are presumed to be slower, more so because the rt file +itself is not optimized by the compiler. + +Setting AFL_GCC_OUT_OF_LINE=1 in the environment while compiling code +with the plugin will disable this inlining, issuing calls to the +unoptimized runtime instead. + +You probably don't want to do this, but it might be useful in certain +AFL debugging scenarios, and it might work as a fallback in case +something goes wrong with the inlined instrumentation. diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index a3d75b15..05e2d50d 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -38,7 +38,9 @@ #include #include +#if ! __GNUC__ #include "llvm/Config/llvm-config.h" +#endif #ifdef __linux__ #include "snapshot-inl.h" @@ -109,14 +111,22 @@ static u8 _is_sancov; void __afl_trace(const u32 x) { + PREV_LOC_T prev = __afl_prev_loc[0]; + __afl_prev_loc[0] = (x >> 1); + + u8 *p = &__afl_area_ptr[prev ^ x]; + #if 1 /* enable for neverZero feature. */ - __afl_area_ptr[__afl_prev_loc[0] ^ x] += - 1 + ((u8)(1 + __afl_area_ptr[__afl_prev_loc[0] ^ x]) == 0); +# if __GNUC__ + u8 c = __builtin_add_overflow (*p, 1, p); + *p += c; +# else + *p += 1 + ((u8)(1 + *p == 0); +# endif #else - ++__afl_area_ptr[__afl_prev_loc[0] ^ x]; + ++*p; #endif - __afl_prev_loc[0] = (x >> 1); return; } diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index c5614aca..6d6f4636 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -1,36 +1,23 @@ -// -// There are some TODOs in this file: -// - fix instrumentation via external call -// - fix inline instrumentation -// - implement instrument list feature -// - dont instrument blocks that are uninteresting -// - implement neverZero -// - -/* - american fuzzy lop++ - GCC instrumentation pass - --------------------------------------------- - - Written by Austin Seipp with bits from - Emese Revfy - - Fixed by Heiko Eißfeldt 2019-2020 for AFL++ - - GCC integration design is based on the LLVM design, which comes - from Laszlo Szekeres. Some of the boilerplate code below for - afl_pass to adapt to different GCC versions was taken from Emese - Revfy's Size Overflow plugin for GCC, licensed under the GPLv2/v3. - - (NOTE: this plugin code is under GPLv3, in order to comply with the - GCC runtime library exception, which states that you may distribute - "Target Code" from the compiler under a license of your choice, as - long as the "Compilation Process" is "Eligible", and contains no - GPL-incompatible software in GCC "during the process of - transforming high level code to target code". In this case, the - plugin will be used to generate "Target Code" during the - "Compilation Process", and thus it must be GPLv3 to be "eligible".) - - Copyright (C) 2015 Austin Seipp +/* GCC plugin for instrumentation of code for american fuzzy lop. + + Copyright 2014-2019 Free Software Foundation, Inc + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AdaCore + + Written by Alexandre Oliva , based on the AFL + LLVM pass by Laszlo Szekeres and Michal + Zalewski , and copying a little boilerplate + from GCC's libcc1 plugin and GCC proper. Aside from the + boilerplate, namely includes and the pass data structure, and pass + initialization code and output messages borrowed and adapted from + the LLVM pass into plugin_init and plugin_finalize, the + implementation of the GCC pass proper is written from scratch, + aiming at similar behavior and performance to that of the LLVM + pass, and also at compatibility with the out-of-line + instrumentation and run times of AFL++, as well as of an earlier + GCC plugin implementation by Austin Seipp . The + implementation of Allow/Deny Lists is adapted from that in the LLVM + plugin. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -47,555 +34,910 @@ */ -#define BUILD_INLINE_INST +/* This file implements a GCC plugin that introduces an + instrumentation pass for AFL. What follows is the specification + used to rewrite it, extracted from the functional llvm_mode pass + and from an implementation of the gcc_plugin started by Austin + Seipp . + + Declare itself as GPL-compatible. + + Define a 'plugin_init' function. + + Check version against the global gcc_version. + + Register a PLUGIN_INFO object with .version and .help. + + Initialize the random number generator seed with GCC's + random seed. + + Set quiet mode depending on whether stderr is a terminal and + AFL_QUIET is set. + + Output some identification message if not in quiet mode. + + Parse AFL_INST_RATIO, if set, as a number between 0 and 100. Error + out if it's not in range; set up an instrumentation ratio global + otherwise. + + Introduce a single instrumentation pass after SSA. + + The new pass is to be a GIMPLE_PASS. Given the sort of + instrumentation it's supposed to do, its todo_flags_finish will + certainly need TODO_update_ssa, and TODO_cleanup_cfg. + TODO_verify_il is probably desirable, at least during debugging. + TODO_rebuild_cgraph_edges is required only in the out-of-line + instrumentation mode. + + The instrumentation pass amounts to iterating over all basic blocks + and optionally inserting one of the instrumentation sequences below + after its labels, to indicate execution entered the block. + + A block should be skipped if R(100) (from ../types.h) is >= the + global instrumentation ratio. + + A block may be skipped for other reasons, such as if all of its + predecessors have a single successor. + + For an instrumented block, a R(MAP_SIZE) say should be + generated to be used as its location number. Let be a compiler + constant built out of it. + + Count instrumented blocks and print a message at the end of the + compilation, if not in quiet mode. + + Instrumentation in "dumb" or "out-of-line" mode requires calling a + function, passing it the location number. The function to be + called is __afl_trace, implemented in afl-gcc-rt.o.c. Its + declaration needs only be created once. + + Build the call statement (), then add it to the seq to be + inserted. + + Instrumentation in "fast" or "inline" mode performs the computation + of __afl_trace as part of the function. + + It needs to read and write __afl_prev_loc, a TLS u32 variable. Its + declaration

needs only be created once. + + It needs to read and dereference __afl_area_ptr, a pointer to (an + array of) char. Its declaration needs only be created once. + + The instrumentation sequence should then be filled with the + following statements: + + Load from

to a temporary () of the same type. + + Compute ^ in sizetype, converting types as needed. + + Pointer-add (to be introduced at a later point) and into + another temporary . + + Increment the <*A> MEM_REF. + + Store >> 1 in

. + + Temporaries used above need only be created once per function. + + If any block was instrumented in a function, an initializer for + needs to be introduced, loading it from and inserting it in the + entry edge for the entry block. +*/ #include "../include/config.h" #include "../include/debug.h" -/* clear helper macros AFL types pull in, which intervene with gcc-plugin - * headers from GCC-8 */ +#include +#include +#include + #ifdef likely - #undef likely +# undef likely #endif #ifdef unlikely - #undef unlikely +# undef unlikely #endif -#include -#include -#include - #include #include #include +#include +#include + #include #include -#include -#include -#include +#include #include -#include -#include -#include +#include +#include +#include #include #include -#include -#include -#include +#include + #include -#include -#include -#include -#include -/* -------------------------------------------------------------------------- */ -/* -- AFL instrumentation pass ---------------------------------------------- */ +/* This plugin, being under the same license as GCC, satisfies the + "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY + EXCEPTION, so it can be part of an "Eligible" "Compilation + Process". */ +int plugin_is_GPL_compatible = 1; -static int be_quiet = 0; -static unsigned int inst_ratio = 100; -static bool inst_ext = true; -static std::list myInstrumentList; +namespace { -static unsigned int ext_call_instrument(function *fun) { +static const struct pass_data afl_pass_data = + { + .type = GIMPLE_PASS, + .name = "afl", + .optinfo_flags = OPTGROUP_NONE, + .tv_id = TV_NONE, + .properties_required = 0, + .properties_provided = 0, + .properties_destroyed = 0, + .todo_flags_start = 0, + .todo_flags_finish = (TODO_update_ssa + | TODO_cleanup_cfg + | TODO_verify_il), + }; + +struct afl_pass : gimple_opt_pass { + afl_pass (bool quiet, unsigned int ratio) + : gimple_opt_pass (afl_pass_data, g), + be_quiet (quiet), + debug (!!getenv ("AFL_DEBUG")), + inst_ratio (ratio), +#ifdef AFL_GCC_OUT_OF_LINE + out_of_line (!!(AFL_GCC_OUT_OF_LINE)), +#else + out_of_line (getenv ("AFL_GCC_OUT_OF_LINE")), +#endif + neverZero (!getenv ("AFL_GCC_SKIP_NEVERZERO")), + inst_blocks (0) + { + initInstrumentList (); + } - /* Instrument all the things! */ - basic_block bb; - unsigned finst_blocks = 0; - unsigned fcnt_blocks = 0; + /* Are we outputting to a non-terminal, or running with AFL_QUIET + set? */ + const bool be_quiet; + + /* Are we running with AFL_DEBUG set? */ + const bool debug; + + /* How likely (%) is a block to be instrumented? */ + const unsigned int inst_ratio; + + /* Should we use slow, out-of-line call-based instrumentation? */ + const bool out_of_line; + + /* Should we make sure the map edge-crossing counters never wrap + around to zero? */ + const bool neverZero; + + /* Count instrumented blocks. */ + int inst_blocks; + + virtual unsigned int + execute (function *fn) + { + if (!isInInstrumentList(fn)) + return 0; + + int blocks = 0; + + /* These are temporaries used by inline instrumentation only, that + are live throughout the function. */ + tree ploc = NULL, indx = NULL, map = NULL, map_ptr = NULL, + ntry = NULL, cntr = NULL, xaddc = NULL, xincr = NULL; + + basic_block bb; + FOR_EACH_BB_FN (bb, fn) + { + if (!instrument_block_p (bb)) + continue; + + /* Generate the block identifier. */ + unsigned bid = R (MAP_SIZE); + tree bidt = build_int_cst (sizetype, bid); + + gimple_seq seq = NULL; + + if (out_of_line) + { + static tree afl_trace = get_afl_trace_decl (); + + /* Call __afl_trace with bid, the new location; */ + gcall *call = gimple_build_call (afl_trace, 1, bidt); + gimple_seq_add_stmt (&seq, call); + } + else + { + static tree afl_prev_loc = get_afl_prev_loc_decl (); + static tree afl_area_ptr = get_afl_area_ptr_decl (); + + /* Load __afl_prev_loc to a temporary ploc. */ + if (blocks == 0) + ploc = create_tmp_var (TREE_TYPE (afl_prev_loc), ".afl_prev_loc"); + gimple *load_loc = gimple_build_assign (ploc, afl_prev_loc); + gimple_seq_add_stmt (&seq, load_loc); + + /* Compute the index into the map referenced by area_ptr + that we're to update: indx = (sizetype) ploc ^ bid. */ + if (blocks == 0) + indx = create_tmp_var (TREE_TYPE (bidt), ".afl_index"); + gimple *conv_ploc + = gimple_build_assign (indx, + fold_convert (TREE_TYPE (indx), + ploc)); + gimple_seq_add_stmt (&seq, conv_ploc); + gimple *xor_loc = gimple_build_assign (indx, BIT_XOR_EXPR, + indx, bidt); + gimple_seq_add_stmt (&seq, xor_loc); + + /* Compute the address of that map element. */ + if (blocks == 0) + { + map = afl_area_ptr; + map_ptr = create_tmp_var (TREE_TYPE (afl_area_ptr), + ".afl_map_ptr"); + ntry = create_tmp_var (TREE_TYPE (afl_area_ptr), + ".afl_map_entry"); + } + gimple *idx_map = gimple_build_assign (ntry, POINTER_PLUS_EXPR, + map_ptr, indx); + gimple_seq_add_stmt (&seq, idx_map); + + /* Increment the counter in idx_map. */ + tree memref = build2 (MEM_REF, TREE_TYPE (TREE_TYPE (ntry)), + ntry, build_zero_cst (TREE_TYPE (ntry))); + if (blocks == 0) + cntr = create_tmp_var (TREE_TYPE (memref), ".afl_edge_count"); + + gimple *load_cntr = gimple_build_assign (cntr, memref); + gimple_seq_add_stmt (&seq, load_cntr); + + tree cntrb = cntr; + tree incrv = build_one_cst (TREE_TYPE (cntr)); + + if (neverZero) + { + /* NeverZero: if count wrapped around to zero, advance to + one. */ + if (blocks == 0) + { + xaddc = create_tmp_var (build_complex_type + (TREE_TYPE (memref)), + ".afl_edge_xaddc"); + xincr = create_tmp_var (TREE_TYPE (memref), + ".afl_edge_xincr"); + } + + auto_vec vargs (2); + vargs.quick_push (cntr); + vargs.quick_push (incrv); + gcall *add1_cntr + = gimple_build_call_internal_vec (IFN_ADD_OVERFLOW, vargs); + gimple_call_set_lhs (add1_cntr, xaddc); + gimple_seq_add_stmt (&seq, add1_cntr); + + cntrb = build1 (REALPART_EXPR, TREE_TYPE (cntr), xaddc); + incrv = build1 (IMAGPART_EXPR, TREE_TYPE (xincr), xaddc); + } + + gimple *incr_cntr = gimple_build_assign (cntr, PLUS_EXPR, + cntrb, incrv); + gimple_seq_add_stmt (&seq, incr_cntr); + + gimple *store_cntr = gimple_build_assign (unshare_expr (memref), + cntr); + gimple_seq_add_stmt (&seq, store_cntr); + + /* Store bid >> 1 in __afl_prev_loc. */ + gimple *shift_loc = gimple_build_assign (ploc, + build_int_cst + (TREE_TYPE (ploc), + bid >> 1)); + gimple_seq_add_stmt (&seq, shift_loc); + gimple *store_loc = gimple_build_assign (afl_prev_loc, ploc); + gimple_seq_add_stmt (&seq, store_loc); + } + + /* Insert the generated sequence. */ + gimple_stmt_iterator insp = gsi_after_labels (bb); + gsi_insert_seq_before (&insp, seq, GSI_SAME_STMT); + + /* Bump this function's instrumented block counter. */ + blocks++; + } - tree fntype = build_function_type_list(void_type_node, /* return */ - uint32_type_node, /* args */ - NULL_TREE); /* done */ - tree fndecl = build_fn_decl("__afl_trace", fntype); - TREE_STATIC(fndecl) = 1; /* Defined elsewhere */ - TREE_PUBLIC(fndecl) = 1; /* Public */ - DECL_EXTERNAL(fndecl) = 1; /* External linkage */ - DECL_ARTIFICIAL(fndecl) = 1; /* Injected by compiler */ + /* Aggregate the instrumented block count. */ + inst_blocks += blocks; - FOR_EACH_BB_FN(bb, fun) { + if (blocks) + { + if (out_of_line) + return TODO_rebuild_cgraph_edges; - gimple_seq fcall; - gimple_seq seq = NULL; - gimple_stmt_iterator bentry; - ++fcnt_blocks; + gimple_seq seq = NULL; - // only instrument if this basic block is the destination of a previous - // basic block that has multiple successors - // this gets rid of ~5-10% of instrumentations that are unnecessary - // result: a little more speed and less map pollution + /* Load afl_area_ptr into map_ptr. We want to do this only + once per function. */ + gimple *load_ptr = gimple_build_assign (map_ptr, map); + gimple_seq_add_stmt (&seq, load_ptr); - int more_than_one = -1; - edge ep; - edge_iterator eip; + /* Insert it in the edge to the entry block. We don't want to + insert it in the first block, since there might be a loop + or a goto back to it. Insert in the edge, which may create + another block. */ + edge e = single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (fn)); + gsi_insert_seq_on_edge_immediate (e, seq); + } - FOR_EACH_EDGE(ep, eip, bb->preds) { + return 0; + } - int count = 0; - if (more_than_one == -1) more_than_one = 0; + /* Decide whether to instrument block BB. Skip it due to the random + distribution, or if it's the single successor of all its + predecessors. */ + inline bool + instrument_block_p (basic_block bb) + { + if (R (100) >= inst_ratio) + return false; + + edge e; edge_iterator ei; + FOR_EACH_EDGE (e, ei, bb->preds) + if (!single_succ_p (e->src)) + return true; + + return false; + } - basic_block Pred = ep->src; - edge es; - edge_iterator eis; - FOR_EACH_EDGE(es, eis, Pred->succs) { + /* Create and return a declaration for the __afl_trace rt function. */ + static inline tree + get_afl_trace_decl () + { + tree type = build_function_type_list (void_type_node, + uint16_type_node, + NULL_TREE); + tree decl = build_fn_decl ("__afl_trace", type); - basic_block Succ = es->dest; - if (Succ != NULL) count++; + TREE_PUBLIC (decl) = 1; + DECL_EXTERNAL (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; - } + return decl; + } - if (count > 1) more_than_one = 1; + /* Create and return a declaration for the __afl_prev_loc + thread-local variable. */ + static inline tree + get_afl_prev_loc_decl () + { + tree decl = build_decl (BUILTINS_LOCATION, VAR_DECL, + get_identifier ("__afl_prev_loc"), + uint32_type_node); + TREE_PUBLIC (decl) = 1; + DECL_EXTERNAL (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + TREE_STATIC (decl) = 1; + set_decl_tls_model (decl, + (flag_pic + ? TLS_MODEL_INITIAL_EXEC + : TLS_MODEL_LOCAL_EXEC)); + return decl; + } + + /* Create and return a declaration for the __afl_prev_loc + thread-local variable. */ + static inline tree + get_afl_area_ptr_decl () + { + tree type = build_pointer_type (unsigned_char_type_node); + tree decl = build_decl (BUILTINS_LOCATION, VAR_DECL, + get_identifier ("__afl_area_ptr"), + type); + TREE_PUBLIC (decl) = 1; + DECL_EXTERNAL (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + TREE_STATIC (decl) = 1; + + return decl; + } + /* This is registered as a plugin finalize callback, to print an + instrumentation summary unless in quiet mode. */ + static void + plugin_finalize (void *, void *p) + { + opt_pass *op = (opt_pass *)p; + afl_pass &self = (afl_pass &)*op; + + if (!self.be_quiet) { + if (!self.inst_blocks) + WARNF ("No instrumentation targets found."); + else + OKF ("Instrumented %u locations (%s mode, %s, ratio %u%%).", + self.inst_blocks, + getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened"), + self.out_of_line ? G_("out of line") : G_("inline"), + self.inst_ratio); } + } - if (more_than_one != 1) continue; +#define report_fatal_error(msg) BADF(msg) - /* Bail on this block if we trip the specified ratio */ - if (R(100) >= inst_ratio) continue; + std::list allowListFiles; + std::list allowListFunctions; + std::list denyListFiles; + std::list denyListFunctions; - /* Make up cur_loc */ - unsigned int rand_loc = R(MAP_SIZE); - tree cur_loc = build_int_cst(uint32_type_node, rand_loc); + /* Note: this ignore check is also called in isInInstrumentList() */ + bool isIgnoreFunction(function *F) { - /* Update bitmap via external call */ - /* to quote: - * /+ Trace a basic block with some ID +/ - * void __afl_trace(u32 x); - */ + // Starting from "LLVMFuzzer" these are functions used in libfuzzer based + // fuzzing campaign installations, e.g. oss-fuzz - fcall = gimple_build_call( - fndecl, 1, - cur_loc); /* generate the function _call_ to above built reference, with - *1* parameter -> the random const for the location */ - gimple_seq_add_stmt(&seq, fcall); /* and insert into a sequence */ + static const char *ignoreList[] = { - /* Done - grab the entry to the block and insert sequence */ - bentry = gsi_after_labels(bb); - gsi_insert_seq_before(&bentry, seq, GSI_SAME_STMT); + "asan.", + "llvm.", + "sancov.", + "__ubsan_", + "ign.", + "__afl_", + "_fini", + "__libc_csu", + "__asan", + "__msan", + "__cmplog", + "__sancov", + "msan.", + "LLVMFuzzer", + "__decide_deferred", + "maybe_duplicate_stderr", + "discard_output", + "close_stdout", + "dup_and_close_stderr", + "maybe_close_fd_mask", + "ExecuteFilesOnyByOne" - ++finst_blocks; + }; - } + const char *name = IDENTIFIER_POINTER (DECL_NAME (F->decl)); + int len = IDENTIFIER_LENGTH (DECL_NAME (F->decl)); + + for (auto const &ignoreListFunc : ignoreList) { - /* Say something nice. */ - if (!be_quiet) { + if (strncmp (name, ignoreListFunc, len) == 0) { return true; } + + } - if (!finst_blocks) - WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST), - function_name(fun)); - else if (finst_blocks < fcnt_blocks) - OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST), - finst_blocks, fcnt_blocks, function_name(fun)); - else - OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST), finst_blocks, - function_name(fun)); + return false; } - return 0; + void initInstrumentList() { + + char *allowlist = getenv("AFL_GCC_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_GCC_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_GCC_WHITELIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); + char *denylist = getenv("AFL_GCC_DENYLIST"); + if (!denylist) denylist = getenv("AFL_GCC_BLOCKLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_DENYLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); + + if (allowlist && denylist) + FATAL( + "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST " + "but not both!"); + + if (allowlist) { + + std::string line; + std::ifstream fileStream; + fileStream.open(allowlist); + if (!fileStream) report_fatal_error("Unable to open AFL_GCC_ALLOWLIST"); + getline(fileStream, line); -} + while (fileStream) { + + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str()); + + } -static unsigned int inline_instrument(function *fun) { - - /* Instrument all the things! */ - basic_block bb; - unsigned finst_blocks = 0; - unsigned fcnt_blocks = 0; - tree one = build_int_cst(unsigned_char_type_node, 1); - // tree zero = build_int_cst(unsigned_char_type_node, 0); - - /* Set up global type declarations */ - tree map_type = build_pointer_type(unsigned_char_type_node); - tree map_ptr_g = - build_decl(UNKNOWN_LOCATION, VAR_DECL, - get_identifier_with_length("__afl_area_ptr", 14), map_type); - TREE_USED(map_ptr_g) = 1; - TREE_STATIC(map_ptr_g) = 1; /* Defined elsewhere */ - DECL_EXTERNAL(map_ptr_g) = 1; /* External linkage */ - DECL_PRESERVE_P(map_ptr_g) = 1; - DECL_ARTIFICIAL(map_ptr_g) = 1; /* Injected by compiler */ - rest_of_decl_compilation(map_ptr_g, 1, 0); - - tree prev_loc_g = build_decl(UNKNOWN_LOCATION, VAR_DECL, - get_identifier_with_length("__afl_prev_loc", 14), - uint32_type_node); - TREE_USED(prev_loc_g) = 1; - TREE_STATIC(prev_loc_g) = 1; /* Defined elsewhere */ - DECL_EXTERNAL(prev_loc_g) = 1; /* External linkage */ - DECL_PRESERVE_P(prev_loc_g) = 1; - DECL_ARTIFICIAL(prev_loc_g) = 1; /* Injected by compiler */ - set_decl_tls_model(prev_loc_g, TLS_MODEL_REAL); /* TLS attribute */ - rest_of_decl_compilation(prev_loc_g, 1, 0); - - FOR_EACH_BB_FN(bb, fun) { - - gimple_seq seq = NULL; - gimple_stmt_iterator bentry; - ++fcnt_blocks; - - // only instrument if this basic block is the destination of a previous - // basic block that has multiple successors - // this gets rid of ~5-10% of instrumentations that are unnecessary - // result: a little more speed and less map pollution - - int more_than_one = -1; - edge ep; - edge_iterator eip; - FOR_EACH_EDGE(ep, eip, bb->preds) { - - int count = 0; - if (more_than_one == -1) more_than_one = 0; - - basic_block Pred = ep->src; - edge es; - edge_iterator eis; - FOR_EACH_EDGE(es, eis, Pred->succs) { - - basic_block Succ = es->dest; - if (Succ != NULL) count++; + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + allowListFiles.push_back(line); + else + allowListFunctions.push_back(line); + getline(fileStream, line); + + } } - if (count > 1) more_than_one = 1; + if (debug) + SAYF(cMGN "[D] " cRST + "loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); } - if (more_than_one != 1) continue; - - /* Bail on this block if we trip the specified ratio */ - if (R(100) >= inst_ratio) continue; - - /* Make up cur_loc */ - - unsigned int rand_loc = R(MAP_SIZE); - tree cur_loc = build_int_cst(uint32_type_node, rand_loc); - - /* Load prev_loc, xor with cur_loc */ - // gimple_assign - tree prev_loc = create_tmp_var_raw(uint32_type_node, "prev_loc"); - gassign *g = gimple_build_assign(prev_loc, VAR_DECL, prev_loc_g); - gimple_seq_add_stmt(&seq, g); // load prev_loc - update_stmt(g); - - // gimple_assign - tree area_off = create_tmp_var_raw(uint32_type_node, "area_off"); - g = gimple_build_assign(area_off, BIT_XOR_EXPR, prev_loc, cur_loc); - gimple_seq_add_stmt(&seq, g); // area_off = prev_loc ^ cur_loc - update_stmt(g); - - /* Update bitmap */ - - // gimple_assign - tree map_ptr = create_tmp_var(map_type, "map_ptr"); - tree map_ptr2 = create_tmp_var(map_type, "map_ptr2"); - - g = gimple_build_assign(map_ptr, map_ptr_g); - gimple_seq_add_stmt(&seq, g); // map_ptr = __afl_area_ptr - update_stmt(g); - -#if 1 - #if 0 - tree addr = build2(ADDR_EXPR, map_type, map_ptr, area_off); - g = gimple_build_assign(map_ptr2, MODIFY_EXPR, addr); - gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off - update_stmt(g); - #else - g = gimple_build_assign(map_ptr2, PLUS_EXPR, map_ptr, area_off); - gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off - update_stmt(g); - #endif - - // gimple_assign - tree tmp1 = create_tmp_var_raw(unsigned_char_type_node, "tmp1"); - g = gimple_build_assign(tmp1, MEM_REF, map_ptr2); - gimple_seq_add_stmt(&seq, g); // tmp1 = *map_ptr2 - update_stmt(g); -#else - tree atIndex = build2(PLUS_EXPR, uint32_type_node, map_ptr, area_off); - tree array_address = build1(ADDR_EXPR, map_type, atIndex); - tree array_access = build1(INDIRECT_REF, map_type, array_address); - tree tmp1 = create_tmp_var(unsigned_char_type_node, "tmp1"); - g = gimple_build_assign(tmp1, array_access); - gimple_seq_add_stmt(&seq, g); // tmp1 = *(map_ptr + area_off) - update_stmt(g); -#endif - // gimple_assign - tree tmp2 = create_tmp_var_raw(unsigned_char_type_node, "tmp2"); - g = gimple_build_assign(tmp2, PLUS_EXPR, tmp1, one); - gimple_seq_add_stmt(&seq, g); // tmp2 = tmp1 + 1 - update_stmt(g); + if (denylist) { - // TODO: neverZero: here we have to check if tmp3 == 0 - // and add 1 if so + std::string line; + std::ifstream fileStream; + fileStream.open(denylist); + if (!fileStream) report_fatal_error("Unable to open AFL_GCC_DENYLIST"); + getline(fileStream, line); - // gimple_assign - // tree map_ptr3 = create_tmp_var_raw(map_type, "map_ptr3"); - g = gimple_build_assign(map_ptr2, INDIRECT_REF, tmp2); - gimple_seq_add_stmt(&seq, g); // *map_ptr2 = tmp2 - update_stmt(g); + while (fileStream) { - /* Set prev_loc to cur_loc >> 1 */ + int is_file = -1; + std::size_t npos; + std::string original_line = line; - // gimple_assign - tree shifted_loc = build_int_cst(TREE_TYPE(prev_loc_g), rand_loc >> 1); - tree prev_loc2 = create_tmp_var_raw(uint32_type_node, "prev_loc2"); - g = gimple_build_assign(prev_loc2, shifted_loc); - gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1 - update_stmt(g); - g = gimple_build_assign(prev_loc_g, prev_loc2); - gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1 - update_stmt(g); + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); - /* Done - grab the entry to the block and insert sequence */ + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); - bentry = gsi_after_labels(bb); - gsi_insert_seq_before(&bentry, seq, GSI_NEW_STMT); + if (line.compare(0, 4, "fun:") == 0) { - ++finst_blocks; + is_file = 0; + line = line.substr(4); - } + } else if (line.compare(0, 9, "function:") == 0) { - /* Say something nice. */ - if (!be_quiet) { + is_file = 0; + line = line.substr(9); - if (!finst_blocks) - WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST), - function_name(fun)); - else if (finst_blocks < fcnt_blocks) - OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST), - finst_blocks, fcnt_blocks, function_name(fun)); - else - OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST), finst_blocks, - function_name(fun)); + } else if (line.compare(0, 4, "src:") == 0) { - } + is_file = 1; + line = line.substr(4); - return 0; + } else if (line.compare(0, 7, "source:") == 0) { -} + is_file = 1; + line = line.substr(7); -/* -------------------------------------------------------------------------- */ -/* -- Boilerplate and initialization ---------------------------------------- */ + } -static const struct pass_data afl_pass_data = { + if (line.find(":") != std::string::npos) { - .type = GIMPLE_PASS, - .name = "afl-inst", - .optinfo_flags = OPTGROUP_NONE, + FATAL("invalid line in AFL_GCC_DENYLIST: %s", original_line.c_str()); - .tv_id = TV_NONE, - .properties_required = 0, - .properties_provided = 0, - .properties_destroyed = 0, - .todo_flags_start = 0, - // NOTE(aseipp): it's very, very important to include - // at least 'TODO_update_ssa' here so that GCC will - // properly update the resulting SSA form, e.g., to - // include new PHI nodes for newly added symbols or - // names. Do not remove this. Do not taunt Happy Fun - // Ball. - .todo_flags_finish = TODO_update_ssa | TODO_verify_il | TODO_cleanup_cfg, + } -}; + if (line.length() > 0) { -namespace { + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function -class afl_pass : public gimple_opt_pass { + if (is_file == 1) + denyListFiles.push_back(line); + else + denyListFunctions.push_back(line); + getline(fileStream, line); - private: - bool do_ext_call; + } - public: - afl_pass(bool ext_call, gcc::context *g) - : gimple_opt_pass(afl_pass_data, g), do_ext_call(ext_call) { + } + + if (debug) + SAYF(cMGN "[D] " cRST + "loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); + + } } - unsigned int execute(function *fun) override { + std::string getSourceName(function *F) { + + return DECL_SOURCE_FILE(F->decl); + + } - if (!myInstrumentList.empty()) { + bool isInInstrumentList(function *F) { - bool instrumentBlock = false; - std::string instFilename; - unsigned int instLine = 0; + bool return_default = true; - /* EXPR_FILENAME - This macro returns the name of the file in which the entity was declared, - as a char*. For an entity declared implicitly by the compiler (like - __builtin_ memcpy), this will be the string "". - */ - const char *fname = DECL_SOURCE_FILE(fun->decl); + // is this a function with code? If it is external we don't instrument it + // anyway and it can't be in the instrument file list. Or if it is it is + // ignored. + if (isIgnoreFunction(F)) return false; - if (0 != strncmp("", fname, 10) && - 0 != strncmp("", fname, 10)) { + if (!denyListFiles.empty() || !denyListFunctions.empty()) { - instFilename = fname; - instLine = DECL_SOURCE_LINE(fun->decl); + if (!denyListFunctions.empty()) { - /* Continue only if we know where we actually are */ - if (!instFilename.empty()) { + std::string instFunction = IDENTIFIER_POINTER (DECL_NAME (F->decl)); - for (std::list::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { + for (std::list::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. */ - if (instFilename.length() >= it->length()) { + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - if (instFilename.compare(instFilename.length() - it->length(), - it->length(), *it) == 0) { + if (instFunction.length() >= it->length()) { - instrumentBlock = true; - break; + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - } + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the deny function list, " + "not instrumenting ... \n", + instFunction.c_str()); + return false; - } + } - } + } - } + } } - /* Either we couldn't figure out our location or the location is - * not in the instrument list, so we skip instrumentation. */ - if (!instrumentBlock) { + if (!denyListFiles.empty()) { - if (!be_quiet) { + std::string source_file = getSourceName(F); - if (!instFilename.empty()) - SAYF(cYEL "[!] " cBRI - "Not in instrument list, skipping %s line %u...\n", - instFilename.c_str(), instLine); - else - SAYF(cYEL "[!] " cBRI "No filename information found, skipping it"); + if (!source_file.empty()) { - } + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { - return 0; + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - } + if (source_file.length() >= it->length()) { - } + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - return do_ext_call ? ext_call_instrument(fun) : inline_instrument(fun); + return false; - } + } -}; /* class afl_pass */ + } -} // namespace + } -static struct opt_pass *make_afl_pass(bool ext_call, gcc::context *ctxt) { + } else { - return new afl_pass(ext_call, ctxt); + // we could not find out the location. in this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER (DECL_NAME (F->decl))); -} + } -/* -------------------------------------------------------------------------- */ -/* -- Initialization -------------------------------------------------------- */ + } -int plugin_is_GPL_compatible = 1; + } -static struct plugin_info afl_plugin_info = { + // if we do not have a instrument file list return true + if (!allowListFiles.empty() || !allowListFunctions.empty()) { - .version = "20200519", - .help = "AFL++ gcc plugin\n", + return_default = false; -}; + if (!allowListFunctions.empty()) { -int plugin_init(struct plugin_name_args * plugin_info, - struct plugin_gcc_version *version) { + std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - struct register_pass_info afl_pass_info; - struct timeval tv; - struct timezone tz; - u32 rand_seed; + for (std::list::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { - /* Setup random() so we get Actually Random(TM) outputs from R() */ - gettimeofday(&tv, &tz); - rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); - SR(rand_seed); + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - /* Pass information */ - afl_pass_info.pass = make_afl_pass(inst_ext, g); - afl_pass_info.reference_pass_name = "ssa"; - afl_pass_info.ref_pass_instance_number = 1; - afl_pass_info.pos_op = PASS_POS_INSERT_AFTER; + if (instFunction.length() >= it->length()) { - if (!plugin_default_version_check(version, &gcc_version)) { + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - FATAL(G_("Incompatible gcc/plugin versions! Expected GCC %d.%d"), - GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR); + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allow function list, " + "instrumenting ... \n", + instFunction.c_str()); + return true; - } + } - /* Show a banner */ - if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { + } - SAYF(G_(cCYA "afl-gcc-pass" VERSION cRST - " initially by , maintainer: hexcoder-\n")); + } - } else + } - be_quiet = 1; + if (!allowListFiles.empty()) { - /* Decide instrumentation ratio */ - char *inst_ratio_str = getenv("AFL_INST_RATIO"); + std::string source_file = getSourceName(F); - if (inst_ratio_str) { + if (!source_file.empty()) { - if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || - inst_ratio > 100) - FATAL(G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)")); - else { + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { - if (!be_quiet) - ACTF(G_("%s instrumentation at ratio of %u%% in %s mode."), - inst_ext ? G_("Call-based") : G_("Inline"), inst_ratio, - getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened")); + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - } + if (source_file.length() >= it->length()) { - } + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - char *instInstrumentListFilename = getenv("AFL_GCC_INSTRUMENT_FILE"); - if (!instInstrumentListFilename) - instInstrumentListFilename = getenv("AFL_GCC_WHITELIST"); - if (instInstrumentListFilename) { + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allowlist (%s), " + "instrumenting ... \n", + IDENTIFIER_POINTER (DECL_NAME (F->decl)), + source_file.c_str()); + return true; - std::string line; - std::ifstream fileStream; - fileStream.open(instInstrumentListFilename); - if (!fileStream) PFATAL("Unable to open AFL_GCC_INSTRUMENT_FILE"); - getline(fileStream, line); - while (fileStream) { + } - myInstrumentList.push_back(line); - getline(fileStream, line); + } - } + } - } else if (!be_quiet && (getenv("AFL_LLVM_WHITELIST") || + } else { - getenv("AFL_LLVM_INSTRUMENT_FILE"))) { + // we could not find out the location. In this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER (DECL_NAME (F->decl))); + return false; - SAYF(cYEL "[-] " cRST - "AFL_LLVM_INSTRUMENT_FILE environment variable detected - did " - "you mean AFL_GCC_INSTRUMENT_FILE?\n"); + } + + } + + } + + return return_default; } - /* Go go gadget */ - register_callback(plugin_info->base_name, PLUGIN_INFO, NULL, - &afl_plugin_info); - register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, NULL, - &afl_pass_info); - return 0; + +}; + +static struct plugin_info afl_plugin = + { + .version = "20200907", + .help = G_("AFL gcc plugin\n\ +\n\ +Set AFL_QUIET in the environment to silence it.\n\ +\n\ +Set AFL_INST_RATIO in the environment to a number from 0 to 100\n\ +to control how likely a block will be chosen for instrumentation.\n\ +\n\ +Specify -frandom-seed for reproducible instrumentation.\n\ +"), + }; } +/* This is the function GCC calls when loading a plugin. Initialize + and register further callbacks. */ +int +plugin_init (struct plugin_name_args *info, + struct plugin_gcc_version *version) +{ + if (!plugin_default_version_check (version, &gcc_version)) + FATAL (G_("GCC and plugin have incompatible versions, expected GCC %d.%d"), + GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR); + + /* Show a banner. */ + bool quiet = false; + if (isatty (2) && !getenv ("AFL_QUIET")) + SAYF (cCYA "afl-gcc-pass " cBRI VERSION cRST " by \n"); + else + quiet = true; + + /* Decide instrumentation ratio. */ + int inst_ratio = 100; + if (char *inst_ratio_str = getenv ("AFL_INST_RATIO")) + if (sscanf (inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || + inst_ratio > 100) + FATAL (G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)")); + + /* Initialize the random number generator with GCC's random seed, in + case it was specified in the command line's -frandom-seed for + reproducible instrumentation. */ + srandom (get_random_seed (false)); + + const char *name = info->base_name; + register_callback (name, PLUGIN_INFO, NULL, &afl_plugin); + + afl_pass *aflp = new afl_pass (quiet, inst_ratio); + struct register_pass_info pass_info = + { + .pass = aflp, + .reference_pass_name = "ssa", + .ref_pass_instance_number = 1, + .pos_op = PASS_POS_INSERT_AFTER, + }; + register_callback (name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); + register_callback (name, PLUGIN_FINISH, afl_pass::plugin_finalize, + pass_info.pass); + + if (!quiet) + ACTF(G_("%s instrumentation at ratio of %u%% in %s mode."), + aflp->out_of_line ? G_("Call-based") : G_("Inline"), inst_ratio, + getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened")); + + return 0; +} diff --git a/src/afl-cc.c b/src/afl-cc.c index ddda3845..78245d4b 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1335,6 +1335,12 @@ int main(int argc, char **argv, char **envp) { AFL_REAL_LD, AFL_CLANG_FLTO); #endif + SAYF( + "\nGCC Plugin-specific environment variables:\n" + "AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" + "AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" + "AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by " + "filename\n"); } SAYF( diff --git a/test/test-performance.sh b/test/test-performance.sh index cee46060..cd6eea64 100755 --- a/test/test-performance.sh +++ b/test/test-performance.sh @@ -117,6 +117,30 @@ test -e ../afl-clang-fast -a -e ../afl-fuzz && { } || $ECHO "$RED[!] llvm_mode instrumentation failed" } || $ECHO "$YELLOW[-] llvm_mode is not compiled, cannot test" +$ECHO "$BLUE[*] Testing: gcc_plugin" +GCCP=x +test -e ../afl-gcc-fast -a -e ../afl-fuzz && { + ../afl-gcc-fast -o test-instr.gccp ../test-instr.c > /dev/null 2>&1 + test -e test-instr.gccp && { + $ECHO "$GREEN[+] gcc_plugin compilation succeeded" + mkdir -p in + echo 0 > in/in + $ECHO "$GREY[*] running afl-fuzz for gcc_plugin for 30 seconds" + { + ../afl-fuzz -V 30 -s 123 -m ${MEM_LIMIT} -i in -o out-gccp -- ./test-instr.gccp + } >>errors 2>&1 + test -n "$( ls out-gccp/queue/id:000002* 2> /dev/null )" && { + GCCP=`grep execs_done out-gccp/fuzzer_stats | awk '{print$3}'` + } || { + echo CUT---------------------------------------------------------------- + cat errors + echo CUT---------------------------------------------------------------- + $ECHO "$RED[!] afl-fuzz is not working correctly with gcc_plugin" + } + rm -rf in out-gccp errors test-instr.gccp + } || $ECHO "$RED[!] gcc_plugin instrumentation failed" +} || $ECHO "$YELLOW[-] gcc_plugin is not compiled, cannot test" + $ECHO "$BLUE[*] Testing: qemu_mode" QEMU=x test -e ../afl-qemu-trace -a -e ../afl-fuzz && { @@ -147,6 +171,9 @@ LAST_GCC= LOW_LLVM= HIGH_LLVM= LAST_LLVM= +LOW_GCCP= +HIGH_GCCP= +LAST_GCCP= LOW_QEMU= HIGH_QEMU= LAST_QEMU= @@ -155,12 +182,15 @@ test -s $FILE && { while read LINE; do G=`echo $LINE | awk '{print$1}'` L=`echo $LINE | awk '{print$2}'` - Q=`echo $LINE | awk '{print$3}'` + P=`echo $LINE | awk '{print$3}'` + Q=`echo $LINE | awk '{print$4}'` test "$G" = x && G= test "$L" = x && L= + test "$P" = x && P= test "$Q" = x && Q= test -n "$G" && LAST_GCC=$G test -n "$L" && LAST_LLVM=$L + test -n "$P" && LAST_GCCP=$P test -n "$Q" && LAST_QEMU=$Q test -n "$G" -a -z "$LOW_GCC" && LOW_GCC=$G || { test -n "$G" -a "$G" -lt "$LOW_GCC" 2> /dev/null && LOW_GCC=$G @@ -168,6 +198,9 @@ test -s $FILE && { test -n "$L" -a -z "$LOW_LLVM" && LOW_LLVM=$L || { test -n "$L" -a "$L" -lt "$LOW_LLVM" 2> /dev/null && LOW_LLVM=$L } + test -n "$P" -a -z "$LOW_GCCP" && LOW_GCCP=$P || { + test -n "$P" -a "$P" -lt "$LOW_GCCP" 2> /dev/null && LOW_GCCP=$P + } test -n "$Q" -a -z "$LOW_QEMU" && LOW_QEMU=$Q || { test -n "$Q" -a "$Q" -lt "$LOW_QEMU" 2> /dev/null && LOW_QEMU=$Q } @@ -177,6 +210,9 @@ test -s $FILE && { test -n "$L" -a -z "$HIGH_LLVM" && HIGH_LLVM=$L || { test -n "$L" -a "$L" -gt "$HIGH_LLVM" 2> /dev/null && HIGH_LLVM=$L } + test -n "$P" -a -z "$HIGH_GCCP" && HIGH_GCCP=$P || { + test -n "$P" -a "$P" -gt "$HIGH_GCCP" 2> /dev/null && HIGH_GCCP=$P + } test -n "$Q" -a -z "$HIGH_QEMU" && HIGH_QEMU=$Q || { test -n "$Q" -a "$Q" -gt "$HIGH_QEMU" 2> /dev/null && HIGH_QEMU=$Q } @@ -184,11 +220,12 @@ test -s $FILE && { $ECHO "$YELLOW[!] Reading saved data from $FILE completed, please compare the results:" $ECHO "$BLUE[!] afl-cc: lowest=$LOW_GCC highest=$HIGH_GCC last=$LAST_GCC current=$GCC" $ECHO "$BLUE[!] llvm_mode: lowest=$LOW_LLVM highest=$HIGH_LLVM last=$LAST_LLVM current=$LLVM" + $ECHO "$BLUE[!] gcc_plugin: lowest=$LOW_GCCP highest=$HIGH_GCCP last=$LAST_GCCP current=$GCCP" $ECHO "$BLUE[!] qemu_mode: lowest=$LOW_QEMU highest=$HIGH_QEMU last=$LAST_QEMU current=$QEMU" } || { $ECHO "$YELLOW[!] First run, just saving data" - $ECHO "$BLUE[!] afl-gcc=$GCC llvm_mode=$LLVM qemu_mode=$QEMU" + $ECHO "$BLUE[!] afl-gcc=$GCC llvm_mode=$LLVM gcc_plugin=$GCCP qemu_mode=$QEMU" } -echo "$GCC $LLVM $QEMU" >> $FILE +echo "$GCC $LLVM $GCCP $QEMU" >> $FILE $ECHO "$GREY[*] done." $ECHO "$RESET" -- cgit 1.4.1 From c091340a85694c5de1125a93366f2733959487f5 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 8 Sep 2020 16:15:31 +0200 Subject: new gcc_plugin integration --- GNUmakefile.llvm | 2 +- README.md | 2 ++ docs/Changelog.md | 2 ++ docs/INSTALL.md | 2 +- src/afl-cc.c | 76 +++++++++++++++++++++++++++---------------------------- 5 files changed, 44 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 3eefdf90..604fb291 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -229,7 +229,7 @@ CFLAGS_SAFE := -Wall -g -Wno-pointer-sign -I ./include/ -I ./instrumentation -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ -DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \ -DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \ - -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \ + -Wno-deprecated -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \ -DAFL_REAL_LD=\"$(AFL_REAL_LD)\" \ -DAFL_CLANG_LDPATH=\"$(AFL_CLANG_LDPATH)\" \ -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \ diff --git a/README.md b/README.md index c886489d..fb59835c 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ behaviours: only one compiler: afl-cc. All previous compilers now symlink to this one compiler. All instrumentation source code is now in the `instrumentation/` folder. + * The gcc_plugin was replaced with a new version submitted by AdaCore, that + supports more features, thank you! * qemu_mode got upgraded to QEMU 5.1, but to be able to build this a current ninja build tool version and python3 setuptools are required. qemu_mode also got new options like snapshotting, instrumenting specific diff --git a/docs/Changelog.md b/docs/Changelog.md index 73613452..c42ab629 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -25,6 +25,8 @@ sending a mail to . skipped. They are used for splicing though. - set the default power schedule to the superiour "seek" schedule - instrumentation + - We received an enhanced gcc_plugin module from AdaCore, thank you + very much!! - not overriding -Ox or -fno-unroll-loops anymore - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz -x dictionary of string comparisons found during compilation diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 93a46caf..8e1e266f 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -28,7 +28,7 @@ If you are using clang, please review README.llvm.md; the LLVM integration mode can offer substantial performance gains compared to the traditional approach. -Likewise, if you are using GCC, please review gcc_plugin/README.md. +Likewise, if you are using GCC, please review instrumentation/README.gcc_plugin.md. You may have to change several settings to get optimal results (most notably, disable crash reporting utilities and switch to a different CPU governor), but diff --git a/src/afl-cc.c b/src/afl-cc.c index 78245d4b..47a33cd0 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1196,7 +1196,7 @@ int main(int argc, char **argv, char **envp) { " - NGRAM-{2-16}\n" " [GCC_PLUGIN] gcc plugin: %s%s\n" " CLASSIC DEFAULT no yes yes no no no " - " simple\n" + " yes\n" " [GCC] simple gcc: %s%s\n" " CLASSIC DEFAULT no no no no no no " " no\n\n", @@ -1270,8 +1270,29 @@ int main(int argc, char **argv, char **envp) { " AFL_CXX: path to the C++ compiler to use\n" " AFL_DEBUG: enable developer debugging output\n" " AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" - " AFL_HARDEN: adds code hardening to catch memory bugs\n" + " AFL_NO_BUILTIN: no builtins for string compare functions (for " + "libtokencap.so)\n" + " AFL_PATH: path to instrumenting pass and runtime " + "(afl-compiler-rt.*o)\n" " AFL_INST_RATIO: percentage of branches to instrument\n" + " AFL_QUIET: suppress verbose output\n" + " AFL_HARDEN: adds code hardening to catch memory bugs\n" + " AFL_USE_ASAN: activate address sanitizer\n" + " AFL_USE_CFISAN: activate control flow sanitizer\n" + " AFL_USE_MSAN: activate memory sanitizer\n" + " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n"); + + if (have_gcc_plugin) + SAYF( + "\nGCC Plugin-specific environment variables:\n" + " AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" + " AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" + " AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by filename\n"); + + if (have_llvm) + SAYF( + "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " + "variables:\n" #if LLVM_MAJOR < 9 " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" #else @@ -1288,25 +1309,13 @@ int main(int argc, char **argv, char **envp) { "functions\n" " AFL_LLVM_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: enable " "instrument allow/\n" - " deny listing (selective instrumentation)\n" - " AFL_NO_BUILTIN: no builtins for string compare functions (for " - "libtokencap.so)\n" - " AFL_PATH: path to instrumenting pass and runtime " - "(afl-compiler-rt.*o)\n" - " AFL_LLVM_DOCUMENT_IDS: document edge IDs given to which function " - "(LTO only)\n" - " AFL_QUIET: suppress verbose output\n" - " AFL_USE_ASAN: activate address sanitizer\n" - " AFL_USE_CFISAN: activate control flow sanitizer\n" - " AFL_USE_MSAN: activate memory sanitizer\n" - " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n"); + " deny listing (selective instrumentation)\n"); + if (have_llvm) SAYF( - "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " - "variables:\n" " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen mutator)\n" - " AFL_LLVM_INSTRUMENT: set instrumentation mode: CLASSIC, INSTRIM, " - "PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-16\n" + " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" + " CLASSIC, INSTRIM, PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-16\n" " You can also use the old environment variables instead:\n" " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n" " AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n" @@ -1315,36 +1324,27 @@ int main(int argc, char **argv, char **envp) { " AFL_LLVM_CTX: use context sensitive coverage (for CLASSIC and " "INSTRIM)\n" " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " - "CLASSIC and INSTRIM)\n"); + "CLASSIC & INSTRIM)\n"); #ifdef AFL_CLANG_FLTO - SAYF( + if (have_lto) + SAYF( "\nLTO/afl-clang-lto specific environment variables:\n" - "AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), e.g. " + " AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), e.g. " "0x10000\n" - "AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding " - "functions they are in into this file\n" - "AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " + " AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding functions\n" + " into this file\n" + " AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " "global var\n" - "AFL_LLVM_LTO_STARTID: from which ID to start counting from for a " + " AFL_LLVM_LTO_STARTID: from which ID to start counting from for a " "bb\n" - "AFL_REAL_LD: use this lld linker instead of the compiled in path\n" - "\nafl-clang-lto was built with linker target \"%s\" and LTO flags " - "\"%s\"\n" - "If anything fails - be sure to read README.lto.md!\n", - AFL_REAL_LD, AFL_CLANG_FLTO); + " AFL_REAL_LD: use this lld linker instead of the compiled in path\n" + "If anything fails - be sure to read README.lto.md!\n"); #endif - - SAYF( - "\nGCC Plugin-specific environment variables:\n" - "AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" - "AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" - "AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by " - "filename\n"); } SAYF( - "For any information on the available instrumentations and options " + "\nFor any information on the available instrumentations and options " "please \n" "consult the README.md, especially section 3.1 about instrumenting " "targets.\n\n"); -- cgit 1.4.1 From 2802245da77062cdadb7d4ceb09d3d083761cf56 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 8 Sep 2020 17:15:32 +0200 Subject: update instrumenation/README.instrument_file.md for gcc_plugin --- instrumentation/README.instrument_list.md | 15 ++++- src/afl-fuzz-init.c | 3 +- src/afl-fuzz-mutators.c | 4 +- src/afl-fuzz.c | 98 +++++++++++++++---------------- 4 files changed, 64 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/instrumentation/README.instrument_list.md b/instrumentation/README.instrument_list.md index 60474ec6..122be2b6 100644 --- a/instrumentation/README.instrument_list.md +++ b/instrumentation/README.instrument_list.md @@ -1,8 +1,8 @@ # Using afl++ with partial instrumentation This file describes how to selectively instrument only source files - or functions that are of interest to you using the LLVM instrumentation - provided by afl++. + or functions that are of interest to you using the LLVM and GCC_PLUGIN + instrumentation provided by afl++. ## 1) Description and purpose @@ -22,11 +22,17 @@ https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumen The llvm sancov list format is fully supported by afl++, however afl++ has more flexibility. -## 2) Building the LLVM module +## 2a) Building the LLVM module The new code is part of the existing afl++ LLVM module in the instrumentation/ subdirectory. There is nothing specifically to do for the build :) +## 2b) Building the GCC module + +The new code is part of the existing afl++ GCC_PLUGIN module in the +instrumentation/ subdirectory. There is nothing specifically to do for +the build :) + ## 3) How to use the partial instrumentation mode In order to build with partial instrumentation, you need to build with @@ -37,6 +43,9 @@ AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST set with a filename. That file should contain the file names or functions that are to be instrumented (AFL_LLVM_ALLOWLIST) or are specifically NOT to be instrumented (AFL_LLVM_DENYLIST). +GCC_PLUGIN: you can use either AFL_LLVM_ALLOWLIST or AFL_GCC_ALLOWLIST (or the +same for _DENYLIST), both work. + For matching to succeed, the function/file name that is being compiled must end in the function/file name entry contained in this instrument file list. That is to avoid breaking the match when absolute paths are used during compilation. diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index a5ebbcd8..29c8c6fa 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -1775,7 +1775,6 @@ int check_main_node_exists(afl_state_t *afl) { void setup_dirs_fds(afl_state_t *afl) { u8 *tmp; - s32 fd; ACTF("Setting up output directories..."); @@ -1901,7 +1900,7 @@ void setup_dirs_fds(afl_state_t *afl) { /* Gnuplot output file. */ tmp = alloc_printf("%s/plot_data", afl->out_dir); - fd = open(tmp, O_WRONLY | O_CREAT | O_EXCL, 0600); + int fd = open(tmp, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fd < 0) { PFATAL("Unable to create '%s'", tmp); } ck_free(tmp); diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index d24b7db9..c4d7233c 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -93,9 +93,9 @@ void setup_custom_mutators(afl_state_t *afl) { } - struct custom_mutator *mutator = load_custom_mutator_py(afl, module_name); + struct custom_mutator *m = load_custom_mutator_py(afl, module_name); afl->custom_mutators_count++; - list_append(&afl->custom_mutator_list, mutator); + list_append(&afl->custom_mutator_list, m); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 5b96ef45..9196d78b 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -698,7 +698,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->swarm_now = 0; if (afl->limit_time_puppet == 0) { afl->key_puppet = 1; } - int i; + int j; int tmp_swarm = 0; if (afl->g_now > afl->g_max) { afl->g_now = 0; } @@ -711,70 +711,70 @@ int main(int argc, char **argv_orig, char **envp) { double total_puppet_temp = 0.0; afl->swarm_fitness[tmp_swarm] = 0.0; - for (i = 0; i < operator_num; ++i) { + for (j = 0; j < operator_num; ++j) { - afl->stage_finds_puppet[tmp_swarm][i] = 0; - afl->probability_now[tmp_swarm][i] = 0.0; - afl->x_now[tmp_swarm][i] = + afl->stage_finds_puppet[tmp_swarm][j] = 0; + afl->probability_now[tmp_swarm][j] = 0.0; + afl->x_now[tmp_swarm][j] = ((double)(random() % 7000) * 0.0001 + 0.1); - total_puppet_temp += afl->x_now[tmp_swarm][i]; - afl->v_now[tmp_swarm][i] = 0.1; - afl->L_best[tmp_swarm][i] = 0.5; - afl->G_best[i] = 0.5; - afl->eff_best[tmp_swarm][i] = 0.0; + total_puppet_temp += afl->x_now[tmp_swarm][j]; + afl->v_now[tmp_swarm][j] = 0.1; + afl->L_best[tmp_swarm][j] = 0.5; + afl->G_best[j] = 0.5; + afl->eff_best[tmp_swarm][j] = 0.0; } - for (i = 0; i < operator_num; ++i) { + for (j = 0; j < operator_num; ++j) { - afl->stage_cycles_puppet_v2[tmp_swarm][i] = - afl->stage_cycles_puppet[tmp_swarm][i]; - afl->stage_finds_puppet_v2[tmp_swarm][i] = - afl->stage_finds_puppet[tmp_swarm][i]; - afl->x_now[tmp_swarm][i] = - afl->x_now[tmp_swarm][i] / total_puppet_temp; + afl->stage_cycles_puppet_v2[tmp_swarm][j] = + afl->stage_cycles_puppet[tmp_swarm][j]; + afl->stage_finds_puppet_v2[tmp_swarm][j] = + afl->stage_finds_puppet[tmp_swarm][j]; + afl->x_now[tmp_swarm][j] = + afl->x_now[tmp_swarm][j] / total_puppet_temp; } double x_temp = 0.0; - for (i = 0; i < operator_num; ++i) { + for (j = 0; j < operator_num; ++j) { - afl->probability_now[tmp_swarm][i] = 0.0; - afl->v_now[tmp_swarm][i] = - afl->w_now * afl->v_now[tmp_swarm][i] + + afl->probability_now[tmp_swarm][j] = 0.0; + afl->v_now[tmp_swarm][j] = + afl->w_now * afl->v_now[tmp_swarm][j] + RAND_C * - (afl->L_best[tmp_swarm][i] - afl->x_now[tmp_swarm][i]) + - RAND_C * (afl->G_best[i] - afl->x_now[tmp_swarm][i]); + (afl->L_best[tmp_swarm][j] - afl->x_now[tmp_swarm][j]) + + RAND_C * (afl->G_best[j] - afl->x_now[tmp_swarm][j]); - afl->x_now[tmp_swarm][i] += afl->v_now[tmp_swarm][i]; + afl->x_now[tmp_swarm][j] += afl->v_now[tmp_swarm][j]; - if (afl->x_now[tmp_swarm][i] > v_max) { + if (afl->x_now[tmp_swarm][j] > v_max) { - afl->x_now[tmp_swarm][i] = v_max; + afl->x_now[tmp_swarm][j] = v_max; - } else if (afl->x_now[tmp_swarm][i] < v_min) { + } else if (afl->x_now[tmp_swarm][j] < v_min) { - afl->x_now[tmp_swarm][i] = v_min; + afl->x_now[tmp_swarm][j] = v_min; } - x_temp += afl->x_now[tmp_swarm][i]; + x_temp += afl->x_now[tmp_swarm][j]; } - for (i = 0; i < operator_num; ++i) { + for (j = 0; j < operator_num; ++j) { - afl->x_now[tmp_swarm][i] = afl->x_now[tmp_swarm][i] / x_temp; - if (likely(i != 0)) { + afl->x_now[tmp_swarm][j] = afl->x_now[tmp_swarm][j] / x_temp; + if (likely(j != 0)) { - afl->probability_now[tmp_swarm][i] = - afl->probability_now[tmp_swarm][i - 1] + - afl->x_now[tmp_swarm][i]; + afl->probability_now[tmp_swarm][j] = + afl->probability_now[tmp_swarm][j - 1] + + afl->x_now[tmp_swarm][j]; } else { - afl->probability_now[tmp_swarm][i] = afl->x_now[tmp_swarm][i]; + afl->probability_now[tmp_swarm][j] = afl->x_now[tmp_swarm][j]; } @@ -789,13 +789,13 @@ int main(int argc, char **argv_orig, char **envp) { } - for (i = 0; i < operator_num; ++i) { + for (j = 0; j < operator_num; ++j) { - afl->core_operator_finds_puppet[i] = 0; - afl->core_operator_finds_puppet_v2[i] = 0; - afl->core_operator_cycles_puppet[i] = 0; - afl->core_operator_cycles_puppet_v2[i] = 0; - afl->core_operator_cycles_puppet_v3[i] = 0; + afl->core_operator_finds_puppet[j] = 0; + afl->core_operator_finds_puppet_v2[j] = 0; + afl->core_operator_cycles_puppet[j] = 0; + afl->core_operator_cycles_puppet_v2[j] = 0; + afl->core_operator_cycles_puppet_v3[j] = 0; } @@ -1010,10 +1010,10 @@ int main(int argc, char **argv_orig, char **envp) { u8 *afl_preload = getenv("AFL_PRELOAD"); u8 *buf; - s32 i, afl_preload_size = strlen(afl_preload); - for (i = 0; i < afl_preload_size; ++i) { + s32 j, afl_preload_size = strlen(afl_preload); + for (j = 0; j < afl_preload_size; ++j) { - if (afl_preload[i] == ',') { + if (afl_preload[j] == ',') { PFATAL( "Comma (',') is not allowed in AFL_PRELOAD when -Q is " @@ -1188,10 +1188,10 @@ int main(int argc, char **argv_orig, char **envp) { if (!afl->fsrv.out_file) { - u32 i = optind + 1; - while (argv[i]) { + u32 j = optind + 1; + while (argv[j]) { - u8 *aa_loc = strstr(argv[i], "@@"); + u8 *aa_loc = strstr(argv[j], "@@"); if (aa_loc && !afl->fsrv.out_file) { @@ -1214,7 +1214,7 @@ int main(int argc, char **argv_orig, char **envp) { } - ++i; + ++j; } -- cgit 1.4.1 From ab744abc4b3c90bee355807e7b6e40ba86f23e74 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 8 Sep 2020 17:54:01 +0200 Subject: code-format --- instrumentation/afl-compiler-rt.o.c | 12 +- instrumentation/afl-gcc-pass.so.cc | 888 ++++++++++++++++++------------------ src/afl-cc.c | 105 +++-- src/afl-forkserver.c | 6 +- 4 files changed, 505 insertions(+), 506 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 05e2d50d..0e8b97a2 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -38,8 +38,8 @@ #include #include -#if ! __GNUC__ -#include "llvm/Config/llvm-config.h" +#if !__GNUC__ + #include "llvm/Config/llvm-config.h" #endif #ifdef __linux__ @@ -117,12 +117,12 @@ void __afl_trace(const u32 x) { u8 *p = &__afl_area_ptr[prev ^ x]; #if 1 /* enable for neverZero feature. */ -# if __GNUC__ - u8 c = __builtin_add_overflow (*p, 1, p); + #if __GNUC__ + u8 c = __builtin_add_overflow(*p, 1, p); *p += c; -# else + #else *p += 1 + ((u8)(1 + *p == 0); -# endif + #endif #else ++*p; #endif diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index 6d6f4636..f8d5fd9e 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -132,10 +132,10 @@ #include #ifdef likely -# undef likely + #undef likely #endif #ifdef unlikely -# undef unlikely + #undef unlikely #endif #include @@ -166,8 +166,8 @@ int plugin_is_GPL_compatible = 1; namespace { -static const struct pass_data afl_pass_data = - { +static const struct pass_data afl_pass_data = { + .type = GIMPLE_PASS, .name = "afl", .optinfo_flags = OPTGROUP_NONE, @@ -176,26 +176,27 @@ static const struct pass_data afl_pass_data = .properties_provided = 0, .properties_destroyed = 0, .todo_flags_start = 0, - .todo_flags_finish = (TODO_update_ssa - | TODO_cleanup_cfg - | TODO_verify_il), - }; + .todo_flags_finish = (TODO_update_ssa | TODO_cleanup_cfg | TODO_verify_il), + +}; struct afl_pass : gimple_opt_pass { - afl_pass (bool quiet, unsigned int ratio) - : gimple_opt_pass (afl_pass_data, g), - be_quiet (quiet), - debug (!!getenv ("AFL_DEBUG")), - inst_ratio (ratio), + + afl_pass(bool quiet, unsigned int ratio) + : gimple_opt_pass(afl_pass_data, g), + be_quiet(quiet), + debug(!!getenv("AFL_DEBUG")), + inst_ratio(ratio), #ifdef AFL_GCC_OUT_OF_LINE - out_of_line (!!(AFL_GCC_OUT_OF_LINE)), + out_of_line(!!(AFL_GCC_OUT_OF_LINE)), #else - out_of_line (getenv ("AFL_GCC_OUT_OF_LINE")), + out_of_line(getenv("AFL_GCC_OUT_OF_LINE")), #endif - neverZero (!getenv ("AFL_GCC_SKIP_NEVERZERO")), - inst_blocks (0) - { - initInstrumentList (); + neverZero(!getenv("AFL_GCC_SKIP_NEVERZERO")), + inst_blocks(0) { + + initInstrumentList(); + } /* Are we outputting to a non-terminal, or running with AFL_QUIET @@ -218,252 +219,240 @@ struct afl_pass : gimple_opt_pass { /* Count instrumented blocks. */ int inst_blocks; - virtual unsigned int - execute (function *fn) - { - if (!isInInstrumentList(fn)) - return 0; + virtual unsigned int execute(function *fn) { + + if (!isInInstrumentList(fn)) return 0; int blocks = 0; /* These are temporaries used by inline instrumentation only, that are live throughout the function. */ - tree ploc = NULL, indx = NULL, map = NULL, map_ptr = NULL, - ntry = NULL, cntr = NULL, xaddc = NULL, xincr = NULL; + tree ploc = NULL, indx = NULL, map = NULL, map_ptr = NULL, ntry = NULL, + cntr = NULL, xaddc = NULL, xincr = NULL; basic_block bb; - FOR_EACH_BB_FN (bb, fn) - { - if (!instrument_block_p (bb)) - continue; - - /* Generate the block identifier. */ - unsigned bid = R (MAP_SIZE); - tree bidt = build_int_cst (sizetype, bid); - - gimple_seq seq = NULL; - - if (out_of_line) - { - static tree afl_trace = get_afl_trace_decl (); - - /* Call __afl_trace with bid, the new location; */ - gcall *call = gimple_build_call (afl_trace, 1, bidt); - gimple_seq_add_stmt (&seq, call); - } - else - { - static tree afl_prev_loc = get_afl_prev_loc_decl (); - static tree afl_area_ptr = get_afl_area_ptr_decl (); - - /* Load __afl_prev_loc to a temporary ploc. */ - if (blocks == 0) - ploc = create_tmp_var (TREE_TYPE (afl_prev_loc), ".afl_prev_loc"); - gimple *load_loc = gimple_build_assign (ploc, afl_prev_loc); - gimple_seq_add_stmt (&seq, load_loc); - - /* Compute the index into the map referenced by area_ptr - that we're to update: indx = (sizetype) ploc ^ bid. */ - if (blocks == 0) - indx = create_tmp_var (TREE_TYPE (bidt), ".afl_index"); - gimple *conv_ploc - = gimple_build_assign (indx, - fold_convert (TREE_TYPE (indx), - ploc)); - gimple_seq_add_stmt (&seq, conv_ploc); - gimple *xor_loc = gimple_build_assign (indx, BIT_XOR_EXPR, - indx, bidt); - gimple_seq_add_stmt (&seq, xor_loc); - - /* Compute the address of that map element. */ - if (blocks == 0) - { - map = afl_area_ptr; - map_ptr = create_tmp_var (TREE_TYPE (afl_area_ptr), - ".afl_map_ptr"); - ntry = create_tmp_var (TREE_TYPE (afl_area_ptr), - ".afl_map_entry"); - } - gimple *idx_map = gimple_build_assign (ntry, POINTER_PLUS_EXPR, - map_ptr, indx); - gimple_seq_add_stmt (&seq, idx_map); - - /* Increment the counter in idx_map. */ - tree memref = build2 (MEM_REF, TREE_TYPE (TREE_TYPE (ntry)), - ntry, build_zero_cst (TREE_TYPE (ntry))); - if (blocks == 0) - cntr = create_tmp_var (TREE_TYPE (memref), ".afl_edge_count"); - - gimple *load_cntr = gimple_build_assign (cntr, memref); - gimple_seq_add_stmt (&seq, load_cntr); - - tree cntrb = cntr; - tree incrv = build_one_cst (TREE_TYPE (cntr)); - - if (neverZero) - { - /* NeverZero: if count wrapped around to zero, advance to - one. */ - if (blocks == 0) - { - xaddc = create_tmp_var (build_complex_type - (TREE_TYPE (memref)), - ".afl_edge_xaddc"); - xincr = create_tmp_var (TREE_TYPE (memref), - ".afl_edge_xincr"); - } - - auto_vec vargs (2); - vargs.quick_push (cntr); - vargs.quick_push (incrv); - gcall *add1_cntr - = gimple_build_call_internal_vec (IFN_ADD_OVERFLOW, vargs); - gimple_call_set_lhs (add1_cntr, xaddc); - gimple_seq_add_stmt (&seq, add1_cntr); - - cntrb = build1 (REALPART_EXPR, TREE_TYPE (cntr), xaddc); - incrv = build1 (IMAGPART_EXPR, TREE_TYPE (xincr), xaddc); - } - - gimple *incr_cntr = gimple_build_assign (cntr, PLUS_EXPR, - cntrb, incrv); - gimple_seq_add_stmt (&seq, incr_cntr); - - gimple *store_cntr = gimple_build_assign (unshare_expr (memref), - cntr); - gimple_seq_add_stmt (&seq, store_cntr); - - /* Store bid >> 1 in __afl_prev_loc. */ - gimple *shift_loc = gimple_build_assign (ploc, - build_int_cst - (TREE_TYPE (ploc), - bid >> 1)); - gimple_seq_add_stmt (&seq, shift_loc); - gimple *store_loc = gimple_build_assign (afl_prev_loc, ploc); - gimple_seq_add_stmt (&seq, store_loc); - } - - /* Insert the generated sequence. */ - gimple_stmt_iterator insp = gsi_after_labels (bb); - gsi_insert_seq_before (&insp, seq, GSI_SAME_STMT); - - /* Bump this function's instrumented block counter. */ - blocks++; + FOR_EACH_BB_FN(bb, fn) { + + if (!instrument_block_p(bb)) continue; + + /* Generate the block identifier. */ + unsigned bid = R(MAP_SIZE); + tree bidt = build_int_cst(sizetype, bid); + + gimple_seq seq = NULL; + + if (out_of_line) { + + static tree afl_trace = get_afl_trace_decl(); + + /* Call __afl_trace with bid, the new location; */ + gcall *call = gimple_build_call(afl_trace, 1, bidt); + gimple_seq_add_stmt(&seq, call); + + } else { + + static tree afl_prev_loc = get_afl_prev_loc_decl(); + static tree afl_area_ptr = get_afl_area_ptr_decl(); + + /* Load __afl_prev_loc to a temporary ploc. */ + if (blocks == 0) + ploc = create_tmp_var(TREE_TYPE(afl_prev_loc), ".afl_prev_loc"); + gimple *load_loc = gimple_build_assign(ploc, afl_prev_loc); + gimple_seq_add_stmt(&seq, load_loc); + + /* Compute the index into the map referenced by area_ptr + that we're to update: indx = (sizetype) ploc ^ bid. */ + if (blocks == 0) indx = create_tmp_var(TREE_TYPE(bidt), ".afl_index"); + gimple *conv_ploc = + gimple_build_assign(indx, fold_convert(TREE_TYPE(indx), ploc)); + gimple_seq_add_stmt(&seq, conv_ploc); + gimple *xor_loc = gimple_build_assign(indx, BIT_XOR_EXPR, indx, bidt); + gimple_seq_add_stmt(&seq, xor_loc); + + /* Compute the address of that map element. */ + if (blocks == 0) { + + map = afl_area_ptr; + map_ptr = create_tmp_var(TREE_TYPE(afl_area_ptr), ".afl_map_ptr"); + ntry = create_tmp_var(TREE_TYPE(afl_area_ptr), ".afl_map_entry"); + + } + + gimple *idx_map = + gimple_build_assign(ntry, POINTER_PLUS_EXPR, map_ptr, indx); + gimple_seq_add_stmt(&seq, idx_map); + + /* Increment the counter in idx_map. */ + tree memref = build2(MEM_REF, TREE_TYPE(TREE_TYPE(ntry)), ntry, + build_zero_cst(TREE_TYPE(ntry))); + if (blocks == 0) + cntr = create_tmp_var(TREE_TYPE(memref), ".afl_edge_count"); + + gimple *load_cntr = gimple_build_assign(cntr, memref); + gimple_seq_add_stmt(&seq, load_cntr); + + tree cntrb = cntr; + tree incrv = build_one_cst(TREE_TYPE(cntr)); + + if (neverZero) { + + /* NeverZero: if count wrapped around to zero, advance to + one. */ + if (blocks == 0) { + + xaddc = create_tmp_var(build_complex_type(TREE_TYPE(memref)), + ".afl_edge_xaddc"); + xincr = create_tmp_var(TREE_TYPE(memref), ".afl_edge_xincr"); + + } + + auto_vec vargs(2); + vargs.quick_push(cntr); + vargs.quick_push(incrv); + gcall *add1_cntr = + gimple_build_call_internal_vec(IFN_ADD_OVERFLOW, vargs); + gimple_call_set_lhs(add1_cntr, xaddc); + gimple_seq_add_stmt(&seq, add1_cntr); + + cntrb = build1(REALPART_EXPR, TREE_TYPE(cntr), xaddc); + incrv = build1(IMAGPART_EXPR, TREE_TYPE(xincr), xaddc); + + } + + gimple *incr_cntr = gimple_build_assign(cntr, PLUS_EXPR, cntrb, incrv); + gimple_seq_add_stmt(&seq, incr_cntr); + + gimple *store_cntr = gimple_build_assign(unshare_expr(memref), cntr); + gimple_seq_add_stmt(&seq, store_cntr); + + /* Store bid >> 1 in __afl_prev_loc. */ + gimple *shift_loc = + gimple_build_assign(ploc, build_int_cst(TREE_TYPE(ploc), bid >> 1)); + gimple_seq_add_stmt(&seq, shift_loc); + gimple *store_loc = gimple_build_assign(afl_prev_loc, ploc); + gimple_seq_add_stmt(&seq, store_loc); + } + /* Insert the generated sequence. */ + gimple_stmt_iterator insp = gsi_after_labels(bb); + gsi_insert_seq_before(&insp, seq, GSI_SAME_STMT); + + /* Bump this function's instrumented block counter. */ + blocks++; + + } + /* Aggregate the instrumented block count. */ inst_blocks += blocks; - if (blocks) - { - if (out_of_line) - return TODO_rebuild_cgraph_edges; + if (blocks) { - gimple_seq seq = NULL; + if (out_of_line) return TODO_rebuild_cgraph_edges; - /* Load afl_area_ptr into map_ptr. We want to do this only - once per function. */ - gimple *load_ptr = gimple_build_assign (map_ptr, map); - gimple_seq_add_stmt (&seq, load_ptr); + gimple_seq seq = NULL; - /* Insert it in the edge to the entry block. We don't want to - insert it in the first block, since there might be a loop - or a goto back to it. Insert in the edge, which may create - another block. */ - edge e = single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (fn)); - gsi_insert_seq_on_edge_immediate (e, seq); - } + /* Load afl_area_ptr into map_ptr. We want to do this only + once per function. */ + gimple *load_ptr = gimple_build_assign(map_ptr, map); + gimple_seq_add_stmt(&seq, load_ptr); + + /* Insert it in the edge to the entry block. We don't want to + insert it in the first block, since there might be a loop + or a goto back to it. Insert in the edge, which may create + another block. */ + edge e = single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(fn)); + gsi_insert_seq_on_edge_immediate(e, seq); + + } return 0; + } /* Decide whether to instrument block BB. Skip it due to the random distribution, or if it's the single successor of all its predecessors. */ - inline bool - instrument_block_p (basic_block bb) - { - if (R (100) >= inst_ratio) - return false; + inline bool instrument_block_p(basic_block bb) { - edge e; edge_iterator ei; - FOR_EACH_EDGE (e, ei, bb->preds) - if (!single_succ_p (e->src)) - return true; + if (R(100) >= inst_ratio) return false; + + edge e; + edge_iterator ei; + FOR_EACH_EDGE(e, ei, bb->preds) + if (!single_succ_p(e->src)) return true; return false; + } /* Create and return a declaration for the __afl_trace rt function. */ - static inline tree - get_afl_trace_decl () - { - tree type = build_function_type_list (void_type_node, - uint16_type_node, - NULL_TREE); - tree decl = build_fn_decl ("__afl_trace", type); - - TREE_PUBLIC (decl) = 1; - DECL_EXTERNAL (decl) = 1; - DECL_ARTIFICIAL (decl) = 1; + static inline tree get_afl_trace_decl() { + + tree type = + build_function_type_list(void_type_node, uint16_type_node, NULL_TREE); + tree decl = build_fn_decl("__afl_trace", type); + + TREE_PUBLIC(decl) = 1; + DECL_EXTERNAL(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; return decl; + } /* Create and return a declaration for the __afl_prev_loc thread-local variable. */ - static inline tree - get_afl_prev_loc_decl () - { - tree decl = build_decl (BUILTINS_LOCATION, VAR_DECL, - get_identifier ("__afl_prev_loc"), - uint32_type_node); - TREE_PUBLIC (decl) = 1; - DECL_EXTERNAL (decl) = 1; - DECL_ARTIFICIAL (decl) = 1; - TREE_STATIC (decl) = 1; - set_decl_tls_model (decl, - (flag_pic - ? TLS_MODEL_INITIAL_EXEC - : TLS_MODEL_LOCAL_EXEC)); + static inline tree get_afl_prev_loc_decl() { + + tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, + get_identifier("__afl_prev_loc"), uint32_type_node); + TREE_PUBLIC(decl) = 1; + DECL_EXTERNAL(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; + TREE_STATIC(decl) = 1; + set_decl_tls_model( + decl, (flag_pic ? TLS_MODEL_INITIAL_EXEC : TLS_MODEL_LOCAL_EXEC)); return decl; + } /* Create and return a declaration for the __afl_prev_loc thread-local variable. */ - static inline tree - get_afl_area_ptr_decl () - { - tree type = build_pointer_type (unsigned_char_type_node); - tree decl = build_decl (BUILTINS_LOCATION, VAR_DECL, - get_identifier ("__afl_area_ptr"), - type); - TREE_PUBLIC (decl) = 1; - DECL_EXTERNAL (decl) = 1; - DECL_ARTIFICIAL (decl) = 1; - TREE_STATIC (decl) = 1; + static inline tree get_afl_area_ptr_decl() { + + tree type = build_pointer_type(unsigned_char_type_node); + tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, + get_identifier("__afl_area_ptr"), type); + TREE_PUBLIC(decl) = 1; + DECL_EXTERNAL(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; + TREE_STATIC(decl) = 1; return decl; + } /* This is registered as a plugin finalize callback, to print an instrumentation summary unless in quiet mode. */ - static void - plugin_finalize (void *, void *p) - { + static void plugin_finalize(void *, void *p) { + opt_pass *op = (opt_pass *)p; afl_pass &self = (afl_pass &)*op; if (!self.be_quiet) { + if (!self.inst_blocks) - WARNF ("No instrumentation targets found."); + WARNF("No instrumentation targets found."); else - OKF ("Instrumented %u locations (%s mode, %s, ratio %u%%).", - self.inst_blocks, - getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened"), - self.out_of_line ? G_("out of line") : G_("inline"), - self.inst_ratio); + OKF("Instrumented %u locations (%s mode, %s, ratio %u%%).", + self.inst_blocks, + getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened"), + self.out_of_line ? G_("out of line") : G_("inline"), + self.inst_ratio); + } + } #define report_fatal_error(msg) BADF(msg) @@ -481,36 +470,36 @@ struct afl_pass : gimple_opt_pass { static const char *ignoreList[] = { - "asan.", - "llvm.", - "sancov.", - "__ubsan_", - "ign.", - "__afl_", - "_fini", - "__libc_csu", - "__asan", - "__msan", - "__cmplog", - "__sancov", - "msan.", - "LLVMFuzzer", - "__decide_deferred", - "maybe_duplicate_stderr", - "discard_output", - "close_stdout", - "dup_and_close_stderr", - "maybe_close_fd_mask", - "ExecuteFilesOnyByOne" + "asan.", + "llvm.", + "sancov.", + "__ubsan_", + "ign.", + "__afl_", + "_fini", + "__libc_csu", + "__asan", + "__msan", + "__cmplog", + "__sancov", + "msan.", + "LLVMFuzzer", + "__decide_deferred", + "maybe_duplicate_stderr", + "discard_output", + "close_stdout", + "dup_and_close_stderr", + "maybe_close_fd_mask", + "ExecuteFilesOnyByOne" }; - const char *name = IDENTIFIER_POINTER (DECL_NAME (F->decl)); - int len = IDENTIFIER_LENGTH (DECL_NAME (F->decl)); + const char *name = IDENTIFIER_POINTER(DECL_NAME(F->decl)); + int len = IDENTIFIER_LENGTH(DECL_NAME(F->decl)); for (auto const &ignoreListFunc : ignoreList) { - if (strncmp (name, ignoreListFunc, len) == 0) { return true; } + if (strncmp(name, ignoreListFunc, len) == 0) { return true; } } @@ -533,8 +522,8 @@ struct afl_pass : gimple_opt_pass { if (allowlist && denylist) FATAL( - "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST " - "but not both!"); + "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST " + "but not both!"); if (allowlist) { @@ -546,68 +535,68 @@ struct afl_pass : gimple_opt_pass { while (fileStream) { - int is_file = -1; - std::size_t npos; - std::string original_line = line; + int is_file = -1; + std::size_t npos; + std::string original_line = line; - line.erase(std::remove_if(line.begin(), line.end(), ::isspace), - line.end()); + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); - // remove # and following - if ((npos = line.find("#")) != std::string::npos) - line = line.substr(0, npos); + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); - if (line.compare(0, 4, "fun:") == 0) { + if (line.compare(0, 4, "fun:") == 0) { - is_file = 0; - line = line.substr(4); + is_file = 0; + line = line.substr(4); - } else if (line.compare(0, 9, "function:") == 0) { + } else if (line.compare(0, 9, "function:") == 0) { - is_file = 0; - line = line.substr(9); + is_file = 0; + line = line.substr(9); - } else if (line.compare(0, 4, "src:") == 0) { + } else if (line.compare(0, 4, "src:") == 0) { - is_file = 1; - line = line.substr(4); + is_file = 1; + line = line.substr(4); - } else if (line.compare(0, 7, "source:") == 0) { + } else if (line.compare(0, 7, "source:") == 0) { - is_file = 1; - line = line.substr(7); + is_file = 1; + line = line.substr(7); - } + } - if (line.find(":") != std::string::npos) { + if (line.find(":") != std::string::npos) { - FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str()); + FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str()); - } + } - if (line.length() > 0) { + if (line.length() > 0) { - // if the entry contains / or . it must be a file - if (is_file == -1) - if (line.find("/") != std::string::npos || - line.find(".") != std::string::npos) - is_file = 1; - // otherwise it is a function + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function - if (is_file == 1) - allowListFiles.push_back(line); - else - allowListFunctions.push_back(line); - getline(fileStream, line); + if (is_file == 1) + allowListFiles.push_back(line); + else + allowListFunctions.push_back(line); + getline(fileStream, line); - } + } } if (debug) - SAYF(cMGN "[D] " cRST - "loaded allowlist with %zu file and %zu function entries\n", - allowListFiles.size(), allowListFunctions.size()); + SAYF(cMGN "[D] " cRST + "loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); } @@ -621,68 +610,68 @@ struct afl_pass : gimple_opt_pass { while (fileStream) { - int is_file = -1; - std::size_t npos; - std::string original_line = line; + int is_file = -1; + std::size_t npos; + std::string original_line = line; - line.erase(std::remove_if(line.begin(), line.end(), ::isspace), - line.end()); + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); - // remove # and following - if ((npos = line.find("#")) != std::string::npos) - line = line.substr(0, npos); + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); - if (line.compare(0, 4, "fun:") == 0) { + if (line.compare(0, 4, "fun:") == 0) { - is_file = 0; - line = line.substr(4); + is_file = 0; + line = line.substr(4); - } else if (line.compare(0, 9, "function:") == 0) { + } else if (line.compare(0, 9, "function:") == 0) { - is_file = 0; - line = line.substr(9); + is_file = 0; + line = line.substr(9); - } else if (line.compare(0, 4, "src:") == 0) { + } else if (line.compare(0, 4, "src:") == 0) { - is_file = 1; - line = line.substr(4); + is_file = 1; + line = line.substr(4); - } else if (line.compare(0, 7, "source:") == 0) { + } else if (line.compare(0, 7, "source:") == 0) { - is_file = 1; - line = line.substr(7); + is_file = 1; + line = line.substr(7); - } + } - if (line.find(":") != std::string::npos) { + if (line.find(":") != std::string::npos) { - FATAL("invalid line in AFL_GCC_DENYLIST: %s", original_line.c_str()); + FATAL("invalid line in AFL_GCC_DENYLIST: %s", original_line.c_str()); - } + } - if (line.length() > 0) { + if (line.length() > 0) { - // if the entry contains / or . it must be a file - if (is_file == -1) - if (line.find("/") != std::string::npos || - line.find(".") != std::string::npos) - is_file = 1; - // otherwise it is a function + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function - if (is_file == 1) - denyListFiles.push_back(line); - else - denyListFunctions.push_back(line); - getline(fileStream, line); + if (is_file == 1) + denyListFiles.push_back(line); + else + denyListFunctions.push_back(line); + getline(fileStream, line); - } + } } if (debug) - SAYF(cMGN "[D] " cRST - "loaded denylist with %zu file and %zu function entries\n", - denyListFiles.size(), denyListFunctions.size()); + SAYF(cMGN "[D] " cRST + "loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); } @@ -707,74 +696,74 @@ struct afl_pass : gimple_opt_pass { if (!denyListFunctions.empty()) { - std::string instFunction = IDENTIFIER_POINTER (DECL_NAME (F->decl)); + std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - for (std::list::iterator it = denyListFunctions.begin(); - it != denyListFunctions.end(); ++it) { + for (std::list::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - if (instFunction.length() >= it->length()) { + if (instFunction.length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the deny function list, " - "not instrumenting ... \n", - instFunction.c_str()); - return false; + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the deny function list, " + "not instrumenting ... \n", + instFunction.c_str()); + return false; - } + } - } + } - } + } } if (!denyListFiles.empty()) { - std::string source_file = getSourceName(F); + std::string source_file = getSourceName(F); - if (!source_file.empty()) { + if (!source_file.empty()) { - for (std::list::iterator it = denyListFiles.begin(); - it != denyListFiles.end(); ++it) { + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - if (source_file.length() >= it->length()) { + if (source_file.length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - return false; + return false; - } + } - } + } - } + } - } else { + } else { - // we could not find out the location. in this case we say it is not - // in the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will be " - "instrumented (recompile with -g -O[1-3]).", - IDENTIFIER_POINTER (DECL_NAME (F->decl))); + // we could not find out the location. in this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER(DECL_NAME(F->decl))); - } + } } @@ -787,81 +776,81 @@ struct afl_pass : gimple_opt_pass { if (!allowListFunctions.empty()) { - std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); + std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - for (std::list::iterator it = allowListFunctions.begin(); - it != allowListFunctions.end(); ++it) { + for (std::list::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - if (instFunction.length() >= it->length()) { + if (instFunction.length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allow function list, " - "instrumenting ... \n", - instFunction.c_str()); - return true; + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allow function list, " + "instrumenting ... \n", + instFunction.c_str()); + return true; - } + } - } + } - } + } } if (!allowListFiles.empty()) { - std::string source_file = getSourceName(F); + std::string source_file = getSourceName(F); - if (!source_file.empty()) { + if (!source_file.empty()) { - for (std::list::iterator it = allowListFiles.begin(); - it != allowListFiles.end(); ++it) { + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - if (source_file.length() >= it->length()) { + if (source_file.length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allowlist (%s), " - "instrumenting ... \n", - IDENTIFIER_POINTER (DECL_NAME (F->decl)), - source_file.c_str()); - return true; + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allowlist (%s), " + "instrumenting ... \n", + IDENTIFIER_POINTER(DECL_NAME(F->decl)), + source_file.c_str()); + return true; - } + } - } + } - } + } - } else { + } else { - // we could not find out the location. In this case we say it is not - // in the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will not be " - "instrumented (recompile with -g -O[1-3]).", - IDENTIFIER_POINTER (DECL_NAME (F->decl))); - return false; + // we could not find out the location. In this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER(DECL_NAME(F->decl))); + return false; - } + } } @@ -871,13 +860,12 @@ struct afl_pass : gimple_opt_pass { } - }; -static struct plugin_info afl_plugin = - { - .version = "20200907", - .help = G_("AFL gcc plugin\n\ +static struct plugin_info afl_plugin = { + + .version = "20200907", + .help = G_("AFL gcc plugin\n\ \n\ Set AFL_QUIET in the environment to silence it.\n\ \n\ @@ -886,58 +874,62 @@ to control how likely a block will be chosen for instrumentation.\n\ \n\ Specify -frandom-seed for reproducible instrumentation.\n\ "), - }; -} +}; + +} // namespace /* This is the function GCC calls when loading a plugin. Initialize and register further callbacks. */ -int -plugin_init (struct plugin_name_args *info, - struct plugin_gcc_version *version) -{ - if (!plugin_default_version_check (version, &gcc_version)) - FATAL (G_("GCC and plugin have incompatible versions, expected GCC %d.%d"), - GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR); +int plugin_init(struct plugin_name_args * info, + struct plugin_gcc_version *version) { + + if (!plugin_default_version_check(version, &gcc_version)) + FATAL(G_("GCC and plugin have incompatible versions, expected GCC %d.%d"), + GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR); /* Show a banner. */ bool quiet = false; - if (isatty (2) && !getenv ("AFL_QUIET")) - SAYF (cCYA "afl-gcc-pass " cBRI VERSION cRST " by \n"); + if (isatty(2) && !getenv("AFL_QUIET")) + SAYF(cCYA "afl-gcc-pass " cBRI VERSION cRST " by \n"); else quiet = true; /* Decide instrumentation ratio. */ int inst_ratio = 100; - if (char *inst_ratio_str = getenv ("AFL_INST_RATIO")) - if (sscanf (inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || - inst_ratio > 100) - FATAL (G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)")); + if (char *inst_ratio_str = getenv("AFL_INST_RATIO")) + if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || + inst_ratio > 100) + FATAL(G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)")); /* Initialize the random number generator with GCC's random seed, in case it was specified in the command line's -frandom-seed for reproducible instrumentation. */ - srandom (get_random_seed (false)); + srandom(get_random_seed(false)); const char *name = info->base_name; - register_callback (name, PLUGIN_INFO, NULL, &afl_plugin); - - afl_pass *aflp = new afl_pass (quiet, inst_ratio); - struct register_pass_info pass_info = - { - .pass = aflp, - .reference_pass_name = "ssa", - .ref_pass_instance_number = 1, - .pos_op = PASS_POS_INSERT_AFTER, - }; - register_callback (name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); - register_callback (name, PLUGIN_FINISH, afl_pass::plugin_finalize, - pass_info.pass); + register_callback(name, PLUGIN_INFO, NULL, &afl_plugin); + + afl_pass * aflp = new afl_pass(quiet, inst_ratio); + struct register_pass_info pass_info = { + + .pass = aflp, + .reference_pass_name = "ssa", + .ref_pass_instance_number = 1, + .pos_op = PASS_POS_INSERT_AFTER, + + }; + + register_callback(name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); + register_callback(name, PLUGIN_FINISH, afl_pass::plugin_finalize, + pass_info.pass); if (!quiet) ACTF(G_("%s instrumentation at ratio of %u%% in %s mode."), - aflp->out_of_line ? G_("Call-based") : G_("Inline"), inst_ratio, - getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened")); + aflp->out_of_line ? G_("Call-based") : G_("Inline"), inst_ratio, + getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened")); return 0; + } + diff --git a/src/afl-cc.c b/src/afl-cc.c index 47a33cd0..6bee8b38 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1282,65 +1282,72 @@ int main(int argc, char **argv, char **envp) { " AFL_USE_MSAN: activate memory sanitizer\n" " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n"); - if (have_gcc_plugin) - SAYF( - "\nGCC Plugin-specific environment variables:\n" - " AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" - " AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" - " AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by filename\n"); - + if (have_gcc_plugin) + SAYF( + "\nGCC Plugin-specific environment variables:\n" + " AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" + " AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" + " AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by " + "filename\n"); + if (have_llvm) SAYF( - "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " - "variables:\n" + "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " + "variables:\n" #if LLVM_MAJOR < 9 - " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" + " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" #else - " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" + " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" #endif - " AFL_LLVM_DICT2FILE: generate an afl dictionary based on found " - "comparisons\n" - " AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n" - " AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n" - " AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n" - " AFL_LLVM_LAF_SPLIT_SWITCHES: cascaded comparisons on switches\n" - " AFL_LLVM_LAF_SPLIT_FLOATS: cascaded comparisons on floats\n" - " AFL_LLVM_LAF_TRANSFORM_COMPARES: cascade comparisons for string " - "functions\n" - " AFL_LLVM_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: enable " - "instrument allow/\n" - " deny listing (selective instrumentation)\n"); + " AFL_LLVM_DICT2FILE: generate an afl dictionary based on found " + "comparisons\n" + " AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n" + " AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n" + " AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n" + " AFL_LLVM_LAF_SPLIT_SWITCHES: cascaded comparisons on switches\n" + " AFL_LLVM_LAF_SPLIT_FLOATS: cascaded comparisons on floats\n" + " AFL_LLVM_LAF_TRANSFORM_COMPARES: cascade comparisons for string " + "functions\n" + " AFL_LLVM_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: enable " + "instrument allow/\n" + " deny listing (selective instrumentation)\n"); if (have_llvm) - SAYF( - " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen mutator)\n" - " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" - " CLASSIC, INSTRIM, PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-16\n" - " You can also use the old environment variables instead:\n" - " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n" - " AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n" - " AFL_LLVM_INSTRIM_LOOPHEAD: optimize loop tracing for speed " - "(option to INSTRIM)\n" - " AFL_LLVM_CTX: use context sensitive coverage (for CLASSIC and " - "INSTRIM)\n" - " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " - "CLASSIC & INSTRIM)\n"); + SAYF( + " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen " + "mutator)\n" + " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" + " CLASSIC, INSTRIM, PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-16\n" + " You can also use the old environment variables instead:\n" + " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n" + " AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n" + " AFL_LLVM_INSTRIM_LOOPHEAD: optimize loop tracing for speed " + "(option to INSTRIM)\n" + " AFL_LLVM_CTX: use context sensitive coverage (for CLASSIC and " + "INSTRIM)\n" + " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " + "CLASSIC & INSTRIM)\n"); #ifdef AFL_CLANG_FLTO - if (have_lto) - SAYF( - "\nLTO/afl-clang-lto specific environment variables:\n" - " AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), e.g. " - "0x10000\n" - " AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding functions\n" - " into this file\n" - " AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " - "global var\n" - " AFL_LLVM_LTO_STARTID: from which ID to start counting from for a " - "bb\n" - " AFL_REAL_LD: use this lld linker instead of the compiled in path\n" - "If anything fails - be sure to read README.lto.md!\n"); + if (have_lto) + SAYF( + "\nLTO/afl-clang-lto specific environment variables:\n" + " AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), " + "e.g. " + "0x10000\n" + " AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding " + "functions\n" + " into this file\n" + " AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " + "global var\n" + " AFL_LLVM_LTO_STARTID: from which ID to start counting from for " + "a " + "bb\n" + " AFL_REAL_LD: use this lld linker instead of the compiled in " + "path\n" + "If anything fails - be sure to read README.lto.md!\n"); #endif + } SAYF( diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index c8056b9e..33b16817 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -1043,11 +1043,11 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, } - if (fsrv->child_pid <= 0) { - + if (fsrv->child_pid <= 0) { + if (*stop_soon_p) { return 0; } FATAL("Fork server is misbehaving (OOM?)"); - + } exec_ms = read_s32_timed(fsrv->fsrv_st_fd, &fsrv->child_status, timeout, -- cgit 1.4.1 From 380051868a7531830d94d312f0f11b0e19e3284f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 10 Sep 2020 15:26:46 +0200 Subject: add libfuzzer custom mutator, minor enhancements and fixes --- GNUmakefile | 4 +- README.md | 5 + custom_mutators/README.md | 4 +- custom_mutators/grammar_mutator/README.md | 6 + .../grammar_mutator/build_grammar_mutator.sh | 17 + custom_mutators/honggfuzz/Makefile | 6 +- custom_mutators/honggfuzz/README.md | 4 +- custom_mutators/libfuzzer/FuzzerBuiltins.h | 35 + custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h | 72 ++ custom_mutators/libfuzzer/FuzzerCommand.h | 178 ++++ custom_mutators/libfuzzer/FuzzerCorpus.h | 581 ++++++++++ custom_mutators/libfuzzer/FuzzerCrossOver.cpp | 60 ++ custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp | 344 ++++++ custom_mutators/libfuzzer/FuzzerDataFlowTrace.h | 135 +++ custom_mutators/libfuzzer/FuzzerDefs.h | 75 ++ custom_mutators/libfuzzer/FuzzerDictionary.h | 118 ++ custom_mutators/libfuzzer/FuzzerDriver.cpp | 1122 ++++++++++++++++++++ custom_mutators/libfuzzer/FuzzerExtFunctions.def | 50 + custom_mutators/libfuzzer/FuzzerExtFunctions.h | 34 + .../libfuzzer/FuzzerExtFunctionsDlsym.cpp | 60 ++ .../libfuzzer/FuzzerExtFunctionsWeak.cpp | 63 ++ .../libfuzzer/FuzzerExtFunctionsWindows.cpp | 95 ++ custom_mutators/libfuzzer/FuzzerExtraCounters.cpp | 71 ++ custom_mutators/libfuzzer/FuzzerFlags.def | 197 ++++ custom_mutators/libfuzzer/FuzzerFork.cpp | 501 +++++++++ custom_mutators/libfuzzer/FuzzerFork.h | 24 + custom_mutators/libfuzzer/FuzzerIO.cpp | 248 +++++ custom_mutators/libfuzzer/FuzzerIO.h | 112 ++ custom_mutators/libfuzzer/FuzzerIOPosix.cpp | 223 ++++ custom_mutators/libfuzzer/FuzzerIOWindows.cpp | 513 +++++++++ custom_mutators/libfuzzer/FuzzerInterface.h | 79 ++ custom_mutators/libfuzzer/FuzzerInternal.h | 173 +++ custom_mutators/libfuzzer/FuzzerLoop.cpp | 1087 +++++++++++++++++++ custom_mutators/libfuzzer/FuzzerMerge.cpp | 485 +++++++++ custom_mutators/libfuzzer/FuzzerMerge.h | 87 ++ custom_mutators/libfuzzer/FuzzerMutate.cpp | 720 +++++++++++++ custom_mutators/libfuzzer/FuzzerMutate.h | 158 +++ custom_mutators/libfuzzer/FuzzerOptions.h | 90 ++ custom_mutators/libfuzzer/FuzzerPlatform.h | 163 +++ custom_mutators/libfuzzer/FuzzerRandom.h | 38 + custom_mutators/libfuzzer/FuzzerSHA1.cpp | 269 +++++ custom_mutators/libfuzzer/FuzzerSHA1.h | 32 + custom_mutators/libfuzzer/FuzzerTracePC.cpp | 819 ++++++++++++++ custom_mutators/libfuzzer/FuzzerTracePC.h | 291 +++++ custom_mutators/libfuzzer/FuzzerUtil.cpp | 314 ++++++ custom_mutators/libfuzzer/FuzzerUtil.h | 117 ++ custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp | 205 ++++ custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp | 658 ++++++++++++ custom_mutators/libfuzzer/FuzzerUtilLinux.cpp | 43 + custom_mutators/libfuzzer/FuzzerUtilPosix.cpp | 239 +++++ custom_mutators/libfuzzer/FuzzerUtilWindows.cpp | 279 +++++ custom_mutators/libfuzzer/FuzzerValueBitMap.h | 73 ++ custom_mutators/libfuzzer/Makefile | 81 ++ custom_mutators/libfuzzer/README.md | 24 + custom_mutators/libfuzzer/libfuzzer.cpp | 147 +++ custom_mutators/libfuzzer/libfuzzer.inc | 36 + custom_mutators/symcc/symcc.c | 7 +- docs/Changelog.md | 2 + include/afl-prealloc.h | 2 +- include/alloc-inl.h | 4 +- include/list.h | 1 + src/afl-cc.c | 8 +- 62 files changed, 11668 insertions(+), 20 deletions(-) create mode 100644 custom_mutators/grammar_mutator/README.md create mode 100755 custom_mutators/grammar_mutator/build_grammar_mutator.sh create mode 100644 custom_mutators/libfuzzer/FuzzerBuiltins.h create mode 100644 custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h create mode 100644 custom_mutators/libfuzzer/FuzzerCommand.h create mode 100644 custom_mutators/libfuzzer/FuzzerCorpus.h create mode 100644 custom_mutators/libfuzzer/FuzzerCrossOver.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerDataFlowTrace.h create mode 100644 custom_mutators/libfuzzer/FuzzerDefs.h create mode 100644 custom_mutators/libfuzzer/FuzzerDictionary.h create mode 100644 custom_mutators/libfuzzer/FuzzerDriver.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctions.def create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctions.h create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerExtraCounters.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerFlags.def create mode 100644 custom_mutators/libfuzzer/FuzzerFork.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerFork.h create mode 100644 custom_mutators/libfuzzer/FuzzerIO.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerIO.h create mode 100644 custom_mutators/libfuzzer/FuzzerIOPosix.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerIOWindows.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerInterface.h create mode 100644 custom_mutators/libfuzzer/FuzzerInternal.h create mode 100644 custom_mutators/libfuzzer/FuzzerLoop.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerMerge.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerMerge.h create mode 100644 custom_mutators/libfuzzer/FuzzerMutate.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerMutate.h create mode 100644 custom_mutators/libfuzzer/FuzzerOptions.h create mode 100644 custom_mutators/libfuzzer/FuzzerPlatform.h create mode 100644 custom_mutators/libfuzzer/FuzzerRandom.h create mode 100644 custom_mutators/libfuzzer/FuzzerSHA1.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerSHA1.h create mode 100644 custom_mutators/libfuzzer/FuzzerTracePC.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerTracePC.h create mode 100644 custom_mutators/libfuzzer/FuzzerUtil.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtil.h create mode 100644 custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtilLinux.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtilPosix.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtilWindows.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerValueBitMap.h create mode 100644 custom_mutators/libfuzzer/Makefile create mode 100644 custom_mutators/libfuzzer/README.md create mode 100644 custom_mutators/libfuzzer/libfuzzer.cpp create mode 100644 custom_mutators/libfuzzer/libfuzzer.inc (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index b9a017d2..d47f8247 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -465,9 +465,9 @@ code-format: ./.custom-format.py -i instrumentation/*.h ./.custom-format.py -i instrumentation/*.cc ./.custom-format.py -i instrumentation/*.c - ./.custom-format.py -i custom_mutators/*/*.c + ./.custom-format.py -i custom_mutators/*/*.c* @#./.custom-format.py -i custom_mutators/*/*.h # destroys input.h :-( - ./.custom-format.py -i examples/*/*.c + ./.custom-format.py -i examples/*/*.c* ./.custom-format.py -i examples/*/*.h ./.custom-format.py -i test/*.c ./.custom-format.py -i qemu_mode/libcompcov/*.c diff --git a/README.md b/README.md index 2fc9d807..c2108e93 100644 --- a/README.md +++ b/README.md @@ -379,6 +379,11 @@ How to do this is described below. Then build the target. (Usually with `make`) +**NOTE**: sometimes configure and build systems are fickle and do not like +stderr output (and think this means a test failure) - which is something +afl++ like to do to show statistics. It is recommended to disable them via +`export AFL_QUIET=1`. + ##### configure For `configure` build systems this is usually done by: diff --git a/custom_mutators/README.md b/custom_mutators/README.md index 993ccaa1..0cf52746 100644 --- a/custom_mutators/README.md +++ b/custom_mutators/README.md @@ -12,9 +12,7 @@ git submodule init git submodule update ``` -otherwise just checkout the repository here with either -`git clone https://github.com/AFLplusplus/Grammar-Mutator` or -`svn co https://github.com/AFLplusplus/Grammar-Mutator`. +otherwise just use the script: `grammar_mutator/build_grammar_mutator.sh` Read the [Grammar-Mutator/README.md](Grammar-Mutator/README.md) on how to use it. diff --git a/custom_mutators/grammar_mutator/README.md b/custom_mutators/grammar_mutator/README.md new file mode 100644 index 00000000..a015744c --- /dev/null +++ b/custom_mutators/grammar_mutator/README.md @@ -0,0 +1,6 @@ +# Grammar-Mutator + +This is just a stub directory that will clone the real grammar mutator +directory. + +Execute `./build_grammar_mutator.sh` to set everything up. diff --git a/custom_mutators/grammar_mutator/build_grammar_mutator.sh b/custom_mutators/grammar_mutator/build_grammar_mutator.sh new file mode 100755 index 00000000..f3f5e164 --- /dev/null +++ b/custom_mutators/grammar_mutator/build_grammar_mutator.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +test -d Grammar-Mutator || git clone --depth=1 https://github.com/AFLplusplus/Grammar-Mutator + +cd Grammar-Mutator || exit 1 +git stash ; git pull + +wget -c https://www.antlr.org/download/antlr-4.8-complete.jar + +echo +echo +echo "All successfully prepared!" +echo "To build for your grammar just do:" +echo " cd Grammar_Mutator" +echo " make GRAMMAR_FILE=/path/to/your/grammar" +echo "You will find a JSON and RUBY grammar in Grammar_Mutator/grammars to play with." +echo diff --git a/custom_mutators/honggfuzz/Makefile b/custom_mutators/honggfuzz/Makefile index 1d46f163..5c2fcddb 100644 --- a/custom_mutators/honggfuzz/Makefile +++ b/custom_mutators/honggfuzz/Makefile @@ -1,10 +1,10 @@ CFLAGS = -O3 -funroll-loops -fPIC -Wl,-Bsymbolic -all: honggfuzz.so +all: honggfuzz-mutator.so -honggfuzz.so: honggfuzz.c input.h mangle.c ../../src/afl-performance.c - $(CC) $(CFLAGS) -I../../include -I. -shared -o honggfuzz.so honggfuzz.c mangle.c ../../src/afl-performance.c +honggfuzz-mutator.so: honggfuzz.c input.h mangle.c ../../src/afl-performance.c + $(CC) $(CFLAGS) -I../../include -I. -shared -o honggfuzz-mutator.so honggfuzz.c mangle.c ../../src/afl-performance.c update: @# seriously? --unlink is a dud option? sigh ... diff --git a/custom_mutators/honggfuzz/README.md b/custom_mutators/honggfuzz/README.md index 8824976f..e1cab281 100644 --- a/custom_mutators/honggfuzz/README.md +++ b/custom_mutators/honggfuzz/README.md @@ -1,12 +1,12 @@ # custum mutator: honggfuzz mangle -this is the very good honggfuzz mutator in mangle.c as a custom mutator +this is the honggfuzz mutator in mangle.c as a custom mutator module for afl++. It is the original mangle.c, mangle.h and honggfuzz.h with a lot of mocking around it :-) just type `make` to build -```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/honggfuzz/honggfuzz.so afl-fuzz ...``` +```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/honggfuzz/honggfuzz-mutator.so afl-fuzz ...``` > Original repository: https://github.com/google/honggfuzz > Source commit: d0fbcb0373c32436b8fb922e6937da93b17291f5 diff --git a/custom_mutators/libfuzzer/FuzzerBuiltins.h b/custom_mutators/libfuzzer/FuzzerBuiltins.h new file mode 100644 index 00000000..4c0ada82 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerBuiltins.h @@ -0,0 +1,35 @@ +//===- FuzzerBuiltins.h - Internal header for builtins ----------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Wrapper functions and marcos around builtin functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_BUILTINS_H +#define LLVM_FUZZER_BUILTINS_H + +#include "FuzzerPlatform.h" + +#if !LIBFUZZER_MSVC +#include + +#define GET_CALLER_PC() __builtin_return_address(0) + +namespace fuzzer { + +inline uint8_t Bswap(uint8_t x) { return x; } +inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); } +inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); } +inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); } + +inline uint32_t Clzll(unsigned long long X) { return __builtin_clzll(X); } +inline uint32_t Clz(unsigned long long X) { return __builtin_clz(X); } +inline int Popcountll(unsigned long long X) { return __builtin_popcountll(X); } + +} // namespace fuzzer + +#endif // !LIBFUZZER_MSVC +#endif // LLVM_FUZZER_BUILTINS_H diff --git a/custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h b/custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h new file mode 100644 index 00000000..c5bec978 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h @@ -0,0 +1,72 @@ +//===- FuzzerBuiltinsMSVC.h - Internal header for builtins ------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Wrapper functions and marcos that use intrinsics instead of builtin functions +// which cannot be compiled by MSVC. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_BUILTINS_MSVC_H +#define LLVM_FUZZER_BUILTINS_MSVC_H + +#include "FuzzerPlatform.h" + +#if LIBFUZZER_MSVC +#include +#include +#include + +// __builtin_return_address() cannot be compiled with MSVC. Use the equivalent +// from +#define GET_CALLER_PC() _ReturnAddress() + +namespace fuzzer { + +inline uint8_t Bswap(uint8_t x) { return x; } +// Use alternatives to __builtin functions from and on +// Windows since the builtins are not supported by MSVC. +inline uint16_t Bswap(uint16_t x) { return _byteswap_ushort(x); } +inline uint32_t Bswap(uint32_t x) { return _byteswap_ulong(x); } +inline uint64_t Bswap(uint64_t x) { return _byteswap_uint64(x); } + +// The functions below were mostly copied from +// compiler-rt/lib/builtins/int_lib.h which defines the __builtin functions used +// outside of Windows. +inline uint32_t Clzll(uint64_t X) { + unsigned long LeadZeroIdx = 0; + +#if !defined(_M_ARM) && !defined(_M_X64) + // Scan the high 32 bits. + if (_BitScanReverse(&LeadZeroIdx, static_cast(X >> 32))) + return static_cast(63 - (LeadZeroIdx + 32)); // Create a bit offset from the MSB. + // Scan the low 32 bits. + if (_BitScanReverse(&LeadZeroIdx, static_cast(X))) + return static_cast(63 - LeadZeroIdx); + +#else + if (_BitScanReverse64(&LeadZeroIdx, X)) return 63 - LeadZeroIdx; +#endif + return 64; +} + +inline uint32_t Clz(uint32_t X) { + unsigned long LeadZeroIdx = 0; + if (_BitScanReverse(&LeadZeroIdx, X)) return 31 - LeadZeroIdx; + return 32; +} + +inline int Popcountll(unsigned long long X) { +#if !defined(_M_ARM) && !defined(_M_X64) + return __popcnt(X) + __popcnt(X >> 32); +#else + return __popcnt64(X); +#endif +} + +} // namespace fuzzer + +#endif // LIBFUZER_MSVC +#endif // LLVM_FUZZER_BUILTINS_MSVC_H diff --git a/custom_mutators/libfuzzer/FuzzerCommand.h b/custom_mutators/libfuzzer/FuzzerCommand.h new file mode 100644 index 00000000..87308864 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerCommand.h @@ -0,0 +1,178 @@ +//===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// FuzzerCommand represents a command to run in a subprocess. It allows callers +// to manage command line arguments and output and error streams. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_COMMAND_H +#define LLVM_FUZZER_COMMAND_H + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" + +#include +#include +#include +#include + +namespace fuzzer { + +class Command final { +public: + // This command line flag is used to indicate that the remaining command line + // is immutable, meaning this flag effectively marks the end of the mutable + // argument list. + static inline const char *ignoreRemainingArgs() { + return "-ignore_remaining_args=1"; + } + + Command() : CombinedOutAndErr(false) {} + + explicit Command(const Vector &ArgsToAdd) + : Args(ArgsToAdd), CombinedOutAndErr(false) {} + + explicit Command(const Command &Other) + : Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr), + OutputFile(Other.OutputFile) {} + + Command &operator=(const Command &Other) { + Args = Other.Args; + CombinedOutAndErr = Other.CombinedOutAndErr; + OutputFile = Other.OutputFile; + return *this; + } + + ~Command() {} + + // Returns true if the given Arg is present in Args. Only checks up to + // "-ignore_remaining_args=1". + bool hasArgument(const std::string &Arg) const { + auto i = endMutableArgs(); + return std::find(Args.begin(), i, Arg) != i; + } + + // Gets all of the current command line arguments, **including** those after + // "-ignore-remaining-args=1". + const Vector &getArguments() const { return Args; } + + // Adds the given argument before "-ignore_remaining_args=1", or at the end + // if that flag isn't present. + void addArgument(const std::string &Arg) { + Args.insert(endMutableArgs(), Arg); + } + + // Adds all given arguments before "-ignore_remaining_args=1", or at the end + // if that flag isn't present. + void addArguments(const Vector &ArgsToAdd) { + Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end()); + } + + // Removes the given argument from the command argument list. Ignores any + // occurrences after "-ignore_remaining_args=1", if present. + void removeArgument(const std::string &Arg) { + auto i = endMutableArgs(); + Args.erase(std::remove(Args.begin(), i, Arg), i); + } + + // Like hasArgument, but checks for "-[Flag]=...". + bool hasFlag(const std::string &Flag) const { + std::string Arg("-" + Flag + "="); + auto IsMatch = [&](const std::string &Other) { + return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; + }; + return std::any_of(Args.begin(), endMutableArgs(), IsMatch); + } + + // Returns the value of the first instance of a given flag, or an empty string + // if the flag isn't present. Ignores any occurrences after + // "-ignore_remaining_args=1", if present. + std::string getFlagValue(const std::string &Flag) const { + std::string Arg("-" + Flag + "="); + auto IsMatch = [&](const std::string &Other) { + return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; + }; + auto i = endMutableArgs(); + auto j = std::find_if(Args.begin(), i, IsMatch); + std::string result; + if (j != i) { + result = j->substr(Arg.length()); + } + return result; + } + + // Like AddArgument, but adds "-[Flag]=[Value]". + void addFlag(const std::string &Flag, const std::string &Value) { + addArgument("-" + Flag + "=" + Value); + } + + // Like RemoveArgument, but removes "-[Flag]=...". + void removeFlag(const std::string &Flag) { + std::string Arg("-" + Flag + "="); + auto IsMatch = [&](const std::string &Other) { + return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; + }; + auto i = endMutableArgs(); + Args.erase(std::remove_if(Args.begin(), i, IsMatch), i); + } + + // Returns whether the command's stdout is being written to an output file. + bool hasOutputFile() const { return !OutputFile.empty(); } + + // Returns the currently set output file. + const std::string &getOutputFile() const { return OutputFile; } + + // Configures the command to redirect its output to the name file. + void setOutputFile(const std::string &FileName) { OutputFile = FileName; } + + // Returns whether the command's stderr is redirected to stdout. + bool isOutAndErrCombined() const { return CombinedOutAndErr; } + + // Sets whether to redirect the command's stderr to its stdout. + void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; } + + // Returns a string representation of the command. On many systems this will + // be the equivalent command line. + std::string toString() const { + std::stringstream SS; + for (auto arg : getArguments()) + SS << arg << " "; + if (hasOutputFile()) + SS << ">" << getOutputFile() << " "; + if (isOutAndErrCombined()) + SS << "2>&1 "; + std::string result = SS.str(); + if (!result.empty()) + result = result.substr(0, result.length() - 1); + return result; + } + +private: + Command(Command &&Other) = delete; + Command &operator=(Command &&Other) = delete; + + Vector::iterator endMutableArgs() { + return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); + } + + Vector::const_iterator endMutableArgs() const { + return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); + } + + // The command arguments. Args[0] is the command name. + Vector Args; + + // True indicates stderr is redirected to stdout. + bool CombinedOutAndErr; + + // If not empty, stdout is redirected to the named file. + std::string OutputFile; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_COMMAND_H diff --git a/custom_mutators/libfuzzer/FuzzerCorpus.h b/custom_mutators/libfuzzer/FuzzerCorpus.h new file mode 100644 index 00000000..daea4f52 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerCorpus.h @@ -0,0 +1,581 @@ +//===- FuzzerCorpus.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::InputCorpus +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_CORPUS +#define LLVM_FUZZER_CORPUS + +#include "FuzzerDataFlowTrace.h" +#include "FuzzerDefs.h" +#include "FuzzerIO.h" +#include "FuzzerRandom.h" +#include "FuzzerSHA1.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include +#include + +namespace fuzzer { + +struct InputInfo { + Unit U; // The actual input data. + std::chrono::microseconds TimeOfUnit; + uint8_t Sha1[kSHA1NumBytes]; // Checksum. + // Number of features that this input has and no smaller input has. + size_t NumFeatures = 0; + size_t Tmp = 0; // Used by ValidateFeatureSet. + // Stats. + size_t NumExecutedMutations = 0; + size_t NumSuccessfullMutations = 0; + bool NeverReduce = false; + bool MayDeleteFile = false; + bool Reduced = false; + bool HasFocusFunction = false; + Vector UniqFeatureSet; + Vector DataFlowTraceForFocusFunction; + // Power schedule. + bool NeedsEnergyUpdate = false; + double Energy = 0.0; + size_t SumIncidence = 0; + Vector> FeatureFreqs; + + // Delete feature Idx and its frequency from FeatureFreqs. + bool DeleteFeatureFreq(uint32_t Idx) { + if (FeatureFreqs.empty()) + return false; + + // Binary search over local feature frequencies sorted by index. + auto Lower = std::lower_bound(FeatureFreqs.begin(), FeatureFreqs.end(), + std::pair(Idx, 0)); + + if (Lower != FeatureFreqs.end() && Lower->first == Idx) { + FeatureFreqs.erase(Lower); + return true; + } + return false; + } + + // Assign more energy to a high-entropy seed, i.e., that reveals more + // information about the globally rare features in the neighborhood of the + // seed. Since we do not know the entropy of a seed that has never been + // executed we assign fresh seeds maximum entropy and let II->Energy approach + // the true entropy from above. If ScalePerExecTime is true, the computed + // entropy is scaled based on how fast this input executes compared to the + // average execution time of inputs. The faster an input executes, the more + // energy gets assigned to the input. + void UpdateEnergy(size_t GlobalNumberOfFeatures, bool ScalePerExecTime, + std::chrono::microseconds AverageUnitExecutionTime) { + Energy = 0.0; + SumIncidence = 0; + + // Apply add-one smoothing to locally discovered features. + for (auto F : FeatureFreqs) { + size_t LocalIncidence = F.second + 1; + Energy -= LocalIncidence * logl(LocalIncidence); + SumIncidence += LocalIncidence; + } + + // Apply add-one smoothing to locally undiscovered features. + // PreciseEnergy -= 0; // since logl(1.0) == 0) + SumIncidence += (GlobalNumberOfFeatures - FeatureFreqs.size()); + + // Add a single locally abundant feature apply add-one smoothing. + size_t AbdIncidence = NumExecutedMutations + 1; + Energy -= AbdIncidence * logl(AbdIncidence); + SumIncidence += AbdIncidence; + + // Normalize. + if (SumIncidence != 0) + Energy = (Energy / SumIncidence) + logl(SumIncidence); + + if (ScalePerExecTime) { + // Scaling to favor inputs with lower execution time. + uint32_t PerfScore = 100; + if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 10) + PerfScore = 10; + else if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 4) + PerfScore = 25; + else if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 2) + PerfScore = 50; + else if (TimeOfUnit.count() * 3 > AverageUnitExecutionTime.count() * 4) + PerfScore = 75; + else if (TimeOfUnit.count() * 4 < AverageUnitExecutionTime.count()) + PerfScore = 300; + else if (TimeOfUnit.count() * 3 < AverageUnitExecutionTime.count()) + PerfScore = 200; + else if (TimeOfUnit.count() * 2 < AverageUnitExecutionTime.count()) + PerfScore = 150; + + Energy *= PerfScore; + } + } + + // Increment the frequency of the feature Idx. + void UpdateFeatureFrequency(uint32_t Idx) { + NeedsEnergyUpdate = true; + + // The local feature frequencies is an ordered vector of pairs. + // If there are no local feature frequencies, push_back preserves order. + // Set the feature frequency for feature Idx32 to 1. + if (FeatureFreqs.empty()) { + FeatureFreqs.push_back(std::pair(Idx, 1)); + return; + } + + // Binary search over local feature frequencies sorted by index. + auto Lower = std::lower_bound(FeatureFreqs.begin(), FeatureFreqs.end(), + std::pair(Idx, 0)); + + // If feature Idx32 already exists, increment its frequency. + // Otherwise, insert a new pair right after the next lower index. + if (Lower != FeatureFreqs.end() && Lower->first == Idx) { + Lower->second++; + } else { + FeatureFreqs.insert(Lower, std::pair(Idx, 1)); + } + } +}; + +struct EntropicOptions { + bool Enabled; + size_t NumberOfRarestFeatures; + size_t FeatureFrequencyThreshold; + bool ScalePerExecTime; +}; + +class InputCorpus { + static const uint32_t kFeatureSetSize = 1 << 21; + static const uint8_t kMaxMutationFactor = 20; + static const size_t kSparseEnergyUpdates = 100; + + size_t NumExecutedMutations = 0; + + EntropicOptions Entropic; + +public: + InputCorpus(const std::string &OutputCorpus, EntropicOptions Entropic) + : Entropic(Entropic), OutputCorpus(OutputCorpus) { + memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature)); + memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature)); + } + ~InputCorpus() { + for (auto II : Inputs) + delete II; + } + size_t size() const { return Inputs.size(); } + size_t SizeInBytes() const { + size_t Res = 0; + for (auto II : Inputs) + Res += II->U.size(); + return Res; + } + size_t NumActiveUnits() const { + size_t Res = 0; + for (auto II : Inputs) + Res += !II->U.empty(); + return Res; + } + size_t MaxInputSize() const { + size_t Res = 0; + for (auto II : Inputs) + Res = std::max(Res, II->U.size()); + return Res; + } + void IncrementNumExecutedMutations() { NumExecutedMutations++; } + + size_t NumInputsThatTouchFocusFunction() { + return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) { + return II->HasFocusFunction; + }); + } + + size_t NumInputsWithDataFlowTrace() { + return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) { + return !II->DataFlowTraceForFocusFunction.empty(); + }); + } + + bool empty() const { return Inputs.empty(); } + const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; } + InputInfo *AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile, + bool HasFocusFunction, bool NeverReduce, + std::chrono::microseconds TimeOfUnit, + const Vector &FeatureSet, + const DataFlowTrace &DFT, const InputInfo *BaseII) { + assert(!U.empty()); + if (FeatureDebug) + Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures); + Inputs.push_back(new InputInfo()); + InputInfo &II = *Inputs.back(); + II.U = U; + II.NumFeatures = NumFeatures; + II.NeverReduce = NeverReduce; + II.TimeOfUnit = TimeOfUnit; + II.MayDeleteFile = MayDeleteFile; + II.UniqFeatureSet = FeatureSet; + II.HasFocusFunction = HasFocusFunction; + // Assign maximal energy to the new seed. + II.Energy = RareFeatures.empty() ? 1.0 : log(RareFeatures.size()); + II.SumIncidence = RareFeatures.size(); + II.NeedsEnergyUpdate = false; + std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end()); + ComputeSHA1(U.data(), U.size(), II.Sha1); + auto Sha1Str = Sha1ToString(II.Sha1); + Hashes.insert(Sha1Str); + if (HasFocusFunction) + if (auto V = DFT.Get(Sha1Str)) + II.DataFlowTraceForFocusFunction = *V; + // This is a gross heuristic. + // Ideally, when we add an element to a corpus we need to know its DFT. + // But if we don't, we'll use the DFT of its base input. + if (II.DataFlowTraceForFocusFunction.empty() && BaseII) + II.DataFlowTraceForFocusFunction = BaseII->DataFlowTraceForFocusFunction; + DistributionNeedsUpdate = true; + PrintCorpus(); + // ValidateFeatureSet(); + return &II; + } + + // Debug-only + void PrintUnit(const Unit &U) { + if (!FeatureDebug) return; + for (uint8_t C : U) { + if (C != 'F' && C != 'U' && C != 'Z') + C = '.'; + Printf("%c", C); + } + } + + // Debug-only + void PrintFeatureSet(const Vector &FeatureSet) { + if (!FeatureDebug) return; + Printf("{"); + for (uint32_t Feature: FeatureSet) + Printf("%u,", Feature); + Printf("}"); + } + + // Debug-only + void PrintCorpus() { + if (!FeatureDebug) return; + Printf("======= CORPUS:\n"); + int i = 0; + for (auto II : Inputs) { + if (std::find(II->U.begin(), II->U.end(), 'F') != II->U.end()) { + Printf("[%2d] ", i); + Printf("%s sz=%zd ", Sha1ToString(II->Sha1).c_str(), II->U.size()); + PrintUnit(II->U); + Printf(" "); + PrintFeatureSet(II->UniqFeatureSet); + Printf("\n"); + } + i++; + } + } + + void Replace(InputInfo *II, const Unit &U) { + assert(II->U.size() > U.size()); + Hashes.erase(Sha1ToString(II->Sha1)); + DeleteFile(*II); + ComputeSHA1(U.data(), U.size(), II->Sha1); + Hashes.insert(Sha1ToString(II->Sha1)); + II->U = U; + II->Reduced = true; + DistributionNeedsUpdate = true; + } + + bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); } + bool HasUnit(const std::string &H) { return Hashes.count(H); } + InputInfo &ChooseUnitToMutate(Random &Rand) { + InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)]; + assert(!II.U.empty()); + return II; + } + + InputInfo &ChooseUnitToCrossOverWith(Random &Rand, bool UniformDist) { + if (!UniformDist) { + return ChooseUnitToMutate(Rand); + } + InputInfo &II = *Inputs[Rand(Inputs.size())]; + assert(!II.U.empty()); + return II; + } + + // Returns an index of random unit from the corpus to mutate. + size_t ChooseUnitIdxToMutate(Random &Rand) { + UpdateCorpusDistribution(Rand); + size_t Idx = static_cast(CorpusDistribution(Rand)); + assert(Idx < Inputs.size()); + return Idx; + } + + void PrintStats() { + for (size_t i = 0; i < Inputs.size(); i++) { + const auto &II = *Inputs[i]; + Printf(" [% 3zd %s] sz: % 5zd runs: % 5zd succ: % 5zd focus: %d\n", i, + Sha1ToString(II.Sha1).c_str(), II.U.size(), + II.NumExecutedMutations, II.NumSuccessfullMutations, II.HasFocusFunction); + } + } + + void PrintFeatureSet() { + for (size_t i = 0; i < kFeatureSetSize; i++) { + if(size_t Sz = GetFeature(i)) + Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz); + } + Printf("\n\t"); + for (size_t i = 0; i < Inputs.size(); i++) + if (size_t N = Inputs[i]->NumFeatures) + Printf(" %zd=>%zd ", i, N); + Printf("\n"); + } + + void DeleteFile(const InputInfo &II) { + if (!OutputCorpus.empty() && II.MayDeleteFile) + RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1))); + } + + void DeleteInput(size_t Idx) { + InputInfo &II = *Inputs[Idx]; + DeleteFile(II); + Unit().swap(II.U); + II.Energy = 0.0; + II.NeedsEnergyUpdate = false; + DistributionNeedsUpdate = true; + if (FeatureDebug) + Printf("EVICTED %zd\n", Idx); + } + + void AddRareFeature(uint32_t Idx) { + // Maintain *at least* TopXRarestFeatures many rare features + // and all features with a frequency below ConsideredRare. + // Remove all other features. + while (RareFeatures.size() > Entropic.NumberOfRarestFeatures && + FreqOfMostAbundantRareFeature > Entropic.FeatureFrequencyThreshold) { + + // Find most and second most abbundant feature. + uint32_t MostAbundantRareFeatureIndices[2] = {RareFeatures[0], + RareFeatures[0]}; + size_t Delete = 0; + for (size_t i = 0; i < RareFeatures.size(); i++) { + uint32_t Idx2 = RareFeatures[i]; + if (GlobalFeatureFreqs[Idx2] >= + GlobalFeatureFreqs[MostAbundantRareFeatureIndices[0]]) { + MostAbundantRareFeatureIndices[1] = MostAbundantRareFeatureIndices[0]; + MostAbundantRareFeatureIndices[0] = Idx2; + Delete = i; + } + } + + // Remove most abundant rare feature. + RareFeatures[Delete] = RareFeatures.back(); + RareFeatures.pop_back(); + + for (auto II : Inputs) { + if (II->DeleteFeatureFreq(MostAbundantRareFeatureIndices[0])) + II->NeedsEnergyUpdate = true; + } + + // Set 2nd most abundant as the new most abundant feature count. + FreqOfMostAbundantRareFeature = + GlobalFeatureFreqs[MostAbundantRareFeatureIndices[1]]; + } + + // Add rare feature, handle collisions, and update energy. + RareFeatures.push_back(Idx); + GlobalFeatureFreqs[Idx] = 0; + for (auto II : Inputs) { + II->DeleteFeatureFreq(Idx); + + // Apply add-one smoothing to this locally undiscovered feature. + // Zero energy seeds will never be fuzzed and remain zero energy. + if (II->Energy > 0.0) { + II->SumIncidence += 1; + II->Energy += logl(II->SumIncidence) / II->SumIncidence; + } + } + + DistributionNeedsUpdate = true; + } + + bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) { + assert(NewSize); + Idx = Idx % kFeatureSetSize; + uint32_t OldSize = GetFeature(Idx); + if (OldSize == 0 || (Shrink && OldSize > NewSize)) { + if (OldSize > 0) { + size_t OldIdx = SmallestElementPerFeature[Idx]; + InputInfo &II = *Inputs[OldIdx]; + assert(II.NumFeatures > 0); + II.NumFeatures--; + if (II.NumFeatures == 0) + DeleteInput(OldIdx); + } else { + NumAddedFeatures++; + if (Entropic.Enabled) + AddRareFeature((uint32_t)Idx); + } + NumUpdatedFeatures++; + if (FeatureDebug) + Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize); + SmallestElementPerFeature[Idx] = Inputs.size(); + InputSizesPerFeature[Idx] = NewSize; + return true; + } + return false; + } + + // Increment frequency of feature Idx globally and locally. + void UpdateFeatureFrequency(InputInfo *II, size_t Idx) { + uint32_t Idx32 = Idx % kFeatureSetSize; + + // Saturated increment. + if (GlobalFeatureFreqs[Idx32] == 0xFFFF) + return; + uint16_t Freq = GlobalFeatureFreqs[Idx32]++; + + // Skip if abundant. + if (Freq > FreqOfMostAbundantRareFeature || + std::find(RareFeatures.begin(), RareFeatures.end(), Idx32) == + RareFeatures.end()) + return; + + // Update global frequencies. + if (Freq == FreqOfMostAbundantRareFeature) + FreqOfMostAbundantRareFeature++; + + // Update local frequencies. + if (II) + II->UpdateFeatureFrequency(Idx32); + } + + size_t NumFeatures() const { return NumAddedFeatures; } + size_t NumFeatureUpdates() const { return NumUpdatedFeatures; } + +private: + + static const bool FeatureDebug = false; + + size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; } + + void ValidateFeatureSet() { + if (FeatureDebug) + PrintFeatureSet(); + for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++) + if (GetFeature(Idx)) + Inputs[SmallestElementPerFeature[Idx]]->Tmp++; + for (auto II: Inputs) { + if (II->Tmp != II->NumFeatures) + Printf("ZZZ %zd %zd\n", II->Tmp, II->NumFeatures); + assert(II->Tmp == II->NumFeatures); + II->Tmp = 0; + } + } + + // Updates the probability distribution for the units in the corpus. + // Must be called whenever the corpus or unit weights are changed. + // + // Hypothesis: inputs that maximize information about globally rare features + // are interesting. + void UpdateCorpusDistribution(Random &Rand) { + // Skip update if no seeds or rare features were added/deleted. + // Sparse updates for local change of feature frequencies, + // i.e., randomly do not skip. + if (!DistributionNeedsUpdate && + (!Entropic.Enabled || Rand(kSparseEnergyUpdates))) + return; + + DistributionNeedsUpdate = false; + + size_t N = Inputs.size(); + assert(N); + Intervals.resize(N + 1); + Weights.resize(N); + std::iota(Intervals.begin(), Intervals.end(), 0); + + std::chrono::microseconds AverageUnitExecutionTime(0); + for (auto II : Inputs) { + AverageUnitExecutionTime += II->TimeOfUnit; + } + AverageUnitExecutionTime /= N; + + bool VanillaSchedule = true; + if (Entropic.Enabled) { + for (auto II : Inputs) { + if (II->NeedsEnergyUpdate && II->Energy != 0.0) { + II->NeedsEnergyUpdate = false; + II->UpdateEnergy(RareFeatures.size(), Entropic.ScalePerExecTime, + AverageUnitExecutionTime); + } + } + + for (size_t i = 0; i < N; i++) { + + if (Inputs[i]->NumFeatures == 0) { + // If the seed doesn't represent any features, assign zero energy. + Weights[i] = 0.; + } else if (Inputs[i]->NumExecutedMutations / kMaxMutationFactor > + NumExecutedMutations / Inputs.size()) { + // If the seed was fuzzed a lot more than average, assign zero energy. + Weights[i] = 0.; + } else { + // Otherwise, simply assign the computed energy. + Weights[i] = Inputs[i]->Energy; + } + + // If energy for all seeds is zero, fall back to vanilla schedule. + if (Weights[i] > 0.0) + VanillaSchedule = false; + } + } + + if (VanillaSchedule) { + for (size_t i = 0; i < N; i++) + Weights[i] = Inputs[i]->NumFeatures + ? (i + 1) * (Inputs[i]->HasFocusFunction ? 1000 : 1) + : 0.; + } + + if (FeatureDebug) { + for (size_t i = 0; i < N; i++) + Printf("%zd ", Inputs[i]->NumFeatures); + Printf("SCORE\n"); + for (size_t i = 0; i < N; i++) + Printf("%f ", Weights[i]); + Printf("Weights\n"); + } + CorpusDistribution = std::piecewise_constant_distribution( + Intervals.begin(), Intervals.end(), Weights.begin()); + } + std::piecewise_constant_distribution CorpusDistribution; + + Vector Intervals; + Vector Weights; + + std::unordered_set Hashes; + Vector Inputs; + + size_t NumAddedFeatures = 0; + size_t NumUpdatedFeatures = 0; + uint32_t InputSizesPerFeature[kFeatureSetSize]; + uint32_t SmallestElementPerFeature[kFeatureSetSize]; + + bool DistributionNeedsUpdate = true; + uint16_t FreqOfMostAbundantRareFeature = 0; + uint16_t GlobalFeatureFreqs[kFeatureSetSize] = {}; + Vector RareFeatures; + + std::string OutputCorpus; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_CORPUS diff --git a/custom_mutators/libfuzzer/FuzzerCrossOver.cpp b/custom_mutators/libfuzzer/FuzzerCrossOver.cpp new file mode 100644 index 00000000..3b3fd94a --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerCrossOver.cpp @@ -0,0 +1,60 @@ +//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Cross over test inputs. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include + +namespace fuzzer { + +// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out. +size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize) { + + assert(Size1 || Size2); + MaxOutSize = Rand(MaxOutSize) + 1; + size_t OutPos = 0; + size_t Pos1 = 0; + size_t Pos2 = 0; + size_t * InPos = &Pos1; + size_t InSize = Size1; + const uint8_t *Data = Data1; + bool CurrentlyUsingFirstData = true; + while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) { + + // Merge a part of Data into Out. + size_t OutSizeLeft = MaxOutSize - OutPos; + if (*InPos < InSize) { + + size_t InSizeLeft = InSize - *InPos; + size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft); + size_t ExtraSize = Rand(MaxExtraSize) + 1; + memcpy(Out + OutPos, Data + *InPos, ExtraSize); + OutPos += ExtraSize; + (*InPos) += ExtraSize; + + } + + // Use the other input data on the next iteration. + InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1; + InSize = CurrentlyUsingFirstData ? Size2 : Size1; + Data = CurrentlyUsingFirstData ? Data2 : Data1; + CurrentlyUsingFirstData = !CurrentlyUsingFirstData; + + } + + return OutPos; + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp b/custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp new file mode 100644 index 00000000..797a52a7 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp @@ -0,0 +1,344 @@ +//===- FuzzerDataFlowTrace.cpp - DataFlowTrace ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::DataFlowTrace +//===----------------------------------------------------------------------===// + +#include "FuzzerDataFlowTrace.h" + +#include "FuzzerCommand.h" +#include "FuzzerIO.h" +#include "FuzzerRandom.h" +#include "FuzzerSHA1.h" +#include "FuzzerUtil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static const char *kFunctionsTxt = "functions.txt"; + +bool BlockCoverage::AppendCoverage(const std::string &S) { + + std::stringstream SS(S); + return AppendCoverage(SS); + +} + +// Coverage lines have this form: +// CN X Y Z T +// where N is the number of the function, T is the total number of instrumented +// BBs, and X,Y,Z, if present, are the indecies of covered BB. +// BB #0, which is the entry block, is not explicitly listed. +bool BlockCoverage::AppendCoverage(std::istream &IN) { + + std::string L; + while (std::getline(IN, L, '\n')) { + + if (L.empty()) continue; + std::stringstream SS(L.c_str() + 1); + size_t FunctionId = 0; + SS >> FunctionId; + if (L[0] == 'F') { + + FunctionsWithDFT.insert(FunctionId); + continue; + + } + + if (L[0] != 'C') continue; + Vector CoveredBlocks; + while (true) { + + uint32_t BB = 0; + SS >> BB; + if (!SS) break; + CoveredBlocks.push_back(BB); + + } + + if (CoveredBlocks.empty()) return false; + uint32_t NumBlocks = CoveredBlocks.back(); + CoveredBlocks.pop_back(); + for (auto BB : CoveredBlocks) + if (BB >= NumBlocks) return false; + auto It = Functions.find(FunctionId); + auto &Counters = + It == Functions.end() + ? Functions.insert({FunctionId, Vector(NumBlocks)}) + .first->second + : It->second; + + if (Counters.size() != NumBlocks) return false; // wrong number of blocks. + + Counters[0]++; + for (auto BB : CoveredBlocks) + Counters[BB]++; + + } + + return true; + +} + +// Assign weights to each function. +// General principles: +// * any uncovered function gets weight 0. +// * a function with lots of uncovered blocks gets bigger weight. +// * a function with a less frequently executed code gets bigger weight. +Vector BlockCoverage::FunctionWeights(size_t NumFunctions) const { + + Vector Res(NumFunctions); + for (auto It : Functions) { + + auto FunctionID = It.first; + auto Counters = It.second; + assert(FunctionID < NumFunctions); + auto &Weight = Res[FunctionID]; + // Give higher weight if the function has a DFT. + Weight = FunctionsWithDFT.count(FunctionID) ? 1000. : 1; + // Give higher weight to functions with less frequently seen basic blocks. + Weight /= SmallestNonZeroCounter(Counters); + // Give higher weight to functions with the most uncovered basic blocks. + Weight *= NumberOfUncoveredBlocks(Counters) + 1; + + } + + return Res; + +} + +void DataFlowTrace::ReadCoverage(const std::string &DirPath) { + + Vector Files; + GetSizedFilesFromDir(DirPath, &Files); + for (auto &SF : Files) { + + auto Name = Basename(SF.File); + if (Name == kFunctionsTxt) continue; + if (!CorporaHashes.count(Name)) continue; + std::ifstream IF(SF.File); + Coverage.AppendCoverage(IF); + + } + +} + +static void DFTStringAppendToVector(Vector * DFT, + const std::string &DFTString) { + + assert(DFT->size() == DFTString.size()); + for (size_t I = 0, Len = DFT->size(); I < Len; I++) + (*DFT)[I] = DFTString[I] == '1'; + +} + +// converts a string of '0' and '1' into a Vector +static Vector DFTStringToVector(const std::string &DFTString) { + + Vector DFT(DFTString.size()); + DFTStringAppendToVector(&DFT, DFTString); + return DFT; + +} + +static bool ParseError(const char *Err, const std::string &Line) { + + Printf("DataFlowTrace: parse error: %s: Line: %s\n", Err, Line.c_str()); + return false; + +} + +// TODO(metzman): replace std::string with std::string_view for +// better performance. Need to figure our how to use string_view on Windows. +static bool ParseDFTLine(const std::string &Line, size_t *FunctionNum, + std::string *DFTString) { + + if (!Line.empty() && Line[0] != 'F') return false; // Ignore coverage. + size_t SpacePos = Line.find(' '); + if (SpacePos == std::string::npos) + return ParseError("no space in the trace line", Line); + if (Line.empty() || Line[0] != 'F') + return ParseError("the trace line doesn't start with 'F'", Line); + *FunctionNum = std::atol(Line.c_str() + 1); + const char *Beg = Line.c_str() + SpacePos + 1; + const char *End = Line.c_str() + Line.size(); + assert(Beg < End); + size_t Len = End - Beg; + for (size_t I = 0; I < Len; I++) { + + if (Beg[I] != '0' && Beg[I] != '1') + return ParseError("the trace should contain only 0 or 1", Line); + + } + + *DFTString = Beg; + return true; + +} + +bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, + Vector &CorporaFiles, Random &Rand) { + + if (DirPath.empty()) return false; + Printf("INFO: DataFlowTrace: reading from '%s'\n", DirPath.c_str()); + Vector Files; + GetSizedFilesFromDir(DirPath, &Files); + std::string L; + size_t FocusFuncIdx = SIZE_MAX; + Vector FunctionNames; + + // Collect the hashes of the corpus files. + for (auto &SF : CorporaFiles) + CorporaHashes.insert(Hash(FileToVector(SF.File))); + + // Read functions.txt + std::ifstream IF(DirPlusFile(DirPath, kFunctionsTxt)); + size_t NumFunctions = 0; + while (std::getline(IF, L, '\n')) { + + FunctionNames.push_back(L); + NumFunctions++; + if (*FocusFunction == L) FocusFuncIdx = NumFunctions - 1; + + } + + if (!NumFunctions) return false; + + if (*FocusFunction == "auto") { + + // AUTOFOCUS works like this: + // * reads the coverage data from the DFT files. + // * assigns weights to functions based on coverage. + // * chooses a random function according to the weights. + ReadCoverage(DirPath); + auto Weights = Coverage.FunctionWeights(NumFunctions); + Vector Intervals(NumFunctions + 1); + std::iota(Intervals.begin(), Intervals.end(), 0); + auto Distribution = std::piecewise_constant_distribution( + Intervals.begin(), Intervals.end(), Weights.begin()); + FocusFuncIdx = static_cast(Distribution(Rand)); + *FocusFunction = FunctionNames[FocusFuncIdx]; + assert(FocusFuncIdx < NumFunctions); + Printf("INFO: AUTOFOCUS: %zd %s\n", FocusFuncIdx, + FunctionNames[FocusFuncIdx].c_str()); + for (size_t i = 0; i < NumFunctions; i++) { + + if (!Weights[i]) continue; + Printf(" [%zd] W %g\tBB-tot %u\tBB-cov %u\tEntryFreq %u:\t%s\n", i, + Weights[i], Coverage.GetNumberOfBlocks(i), + Coverage.GetNumberOfCoveredBlocks(i), Coverage.GetCounter(i, 0), + FunctionNames[i].c_str()); + + } + + } + + if (!NumFunctions || FocusFuncIdx == SIZE_MAX || Files.size() <= 1) + return false; + + // Read traces. + size_t NumTraceFiles = 0; + size_t NumTracesWithFocusFunction = 0; + for (auto &SF : Files) { + + auto Name = Basename(SF.File); + if (Name == kFunctionsTxt) continue; + if (!CorporaHashes.count(Name)) continue; // not in the corpus. + NumTraceFiles++; + // Printf("=== %s\n", Name.c_str()); + std::ifstream IF(SF.File); + while (std::getline(IF, L, '\n')) { + + size_t FunctionNum = 0; + std::string DFTString; + if (ParseDFTLine(L, &FunctionNum, &DFTString) && + FunctionNum == FocusFuncIdx) { + + NumTracesWithFocusFunction++; + + if (FunctionNum >= NumFunctions) + return ParseError("N is greater than the number of functions", L); + Traces[Name] = DFTStringToVector(DFTString); + // Print just a few small traces. + if (NumTracesWithFocusFunction <= 3 && DFTString.size() <= 16) + Printf("%s => |%s|\n", Name.c_str(), std::string(DFTString).c_str()); + break; // No need to parse the following lines. + + } + + } + + } + + Printf( + "INFO: DataFlowTrace: %zd trace files, %zd functions, " + "%zd traces with focus function\n", + NumTraceFiles, NumFunctions, NumTracesWithFocusFunction); + return NumTraceFiles > 0; + +} + +int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, + const Vector &CorporaFiles) { + + Printf("INFO: collecting data flow: bin: %s dir: %s files: %zd\n", + DFTBinary.c_str(), DirPath.c_str(), CorporaFiles.size()); + if (CorporaFiles.empty()) { + + Printf("ERROR: can't collect data flow without corpus provided."); + return 1; + + } + + static char DFSanEnv[] = "DFSAN_OPTIONS=warn_unimplemented=0"; + putenv(DFSanEnv); + MkDir(DirPath); + for (auto &F : CorporaFiles) { + + // For every input F we need to collect the data flow and the coverage. + // Data flow collection may fail if we request too many DFSan tags at once. + // So, we start from requesting all tags in range [0,Size) and if that fails + // we then request tags in [0,Size/2) and [Size/2, Size), and so on. + // Function number => DFT. + auto OutPath = DirPlusFile(DirPath, Hash(FileToVector(F.File))); + std::unordered_map> DFTMap; + std::unordered_set Cov; + Command Cmd; + Cmd.addArgument(DFTBinary); + Cmd.addArgument(F.File); + Cmd.addArgument(OutPath); + Printf("CMD: %s\n", Cmd.toString().c_str()); + ExecuteCommand(Cmd); + + } + + // Write functions.txt if it's currently empty or doesn't exist. + auto FunctionsTxtPath = DirPlusFile(DirPath, kFunctionsTxt); + if (FileToString(FunctionsTxtPath).empty()) { + + Command Cmd; + Cmd.addArgument(DFTBinary); + Cmd.setOutputFile(FunctionsTxtPath); + ExecuteCommand(Cmd); + + } + + return 0; + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerDataFlowTrace.h b/custom_mutators/libfuzzer/FuzzerDataFlowTrace.h new file mode 100644 index 00000000..d6e3de30 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDataFlowTrace.h @@ -0,0 +1,135 @@ +//===- FuzzerDataFlowTrace.h - Internal header for the Fuzzer ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::DataFlowTrace; reads and handles a data-flow trace. +// +// A data flow trace is generated by e.g. dataflow/DataFlow.cpp +// and is stored on disk in a separate directory. +// +// The trace dir contains a file 'functions.txt' which lists function names, +// oner per line, e.g. +// ==> functions.txt <== +// Func2 +// LLVMFuzzerTestOneInput +// Func1 +// +// All other files in the dir are the traces, see dataflow/DataFlow.cpp. +// The name of the file is sha1 of the input used to generate the trace. +// +// Current status: +// the data is parsed and the summary is printed, but the data is not yet +// used in any other way. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DATA_FLOW_TRACE +#define LLVM_FUZZER_DATA_FLOW_TRACE + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" + +#include +#include +#include +#include + +namespace fuzzer { + +int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, + const Vector &CorporaFiles); + +class BlockCoverage { + public: + bool AppendCoverage(std::istream &IN); + bool AppendCoverage(const std::string &S); + + size_t NumCoveredFunctions() const { return Functions.size(); } + + uint32_t GetCounter(size_t FunctionId, size_t BasicBlockId) { + auto It = Functions.find(FunctionId); + if (It == Functions.end()) return 0; + const auto &Counters = It->second; + if (BasicBlockId < Counters.size()) + return Counters[BasicBlockId]; + return 0; + } + + uint32_t GetNumberOfBlocks(size_t FunctionId) { + auto It = Functions.find(FunctionId); + if (It == Functions.end()) return 0; + const auto &Counters = It->second; + return Counters.size(); + } + + uint32_t GetNumberOfCoveredBlocks(size_t FunctionId) { + auto It = Functions.find(FunctionId); + if (It == Functions.end()) return 0; + const auto &Counters = It->second; + uint32_t Result = 0; + for (auto Cnt: Counters) + if (Cnt) + Result++; + return Result; + } + + Vector FunctionWeights(size_t NumFunctions) const; + void clear() { Functions.clear(); } + + private: + + typedef Vector CoverageVector; + + uint32_t NumberOfCoveredBlocks(const CoverageVector &Counters) const { + uint32_t Res = 0; + for (auto Cnt : Counters) + if (Cnt) + Res++; + return Res; + } + + uint32_t NumberOfUncoveredBlocks(const CoverageVector &Counters) const { + return Counters.size() - NumberOfCoveredBlocks(Counters); + } + + uint32_t SmallestNonZeroCounter(const CoverageVector &Counters) const { + assert(!Counters.empty()); + uint32_t Res = Counters[0]; + for (auto Cnt : Counters) + if (Cnt) + Res = Min(Res, Cnt); + assert(Res); + return Res; + } + + // Function ID => vector of counters. + // Each counter represents how many input files trigger the given basic block. + std::unordered_map Functions; + // Functions that have DFT entry. + std::unordered_set FunctionsWithDFT; +}; + +class DataFlowTrace { + public: + void ReadCoverage(const std::string &DirPath); + bool Init(const std::string &DirPath, std::string *FocusFunction, + Vector &CorporaFiles, Random &Rand); + void Clear() { Traces.clear(); } + const Vector *Get(const std::string &InputSha1) const { + auto It = Traces.find(InputSha1); + if (It != Traces.end()) + return &It->second; + return nullptr; + } + + private: + // Input's sha1 => DFT for the FocusFunction. + std::unordered_map > Traces; + BlockCoverage Coverage; + std::unordered_set CorporaHashes; +}; +} // namespace fuzzer + +#endif // LLVM_FUZZER_DATA_FLOW_TRACE diff --git a/custom_mutators/libfuzzer/FuzzerDefs.h b/custom_mutators/libfuzzer/FuzzerDefs.h new file mode 100644 index 00000000..1a2752af --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDefs.h @@ -0,0 +1,75 @@ +//===- FuzzerDefs.h - Internal header for the Fuzzer ------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Basic definitions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DEFS_H +#define LLVM_FUZZER_DEFS_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace fuzzer { + +template T Min(T a, T b) { return a < b ? a : b; } +template T Max(T a, T b) { return a > b ? a : b; } + +class Random; +class Dictionary; +class DictionaryEntry; +class MutationDispatcher; +struct FuzzingOptions; +class InputCorpus; +struct InputInfo; +struct ExternalFunctions; + +// Global interface to functions that may or may not be available. +extern ExternalFunctions *EF; + +// We are using a custom allocator to give a different symbol name to STL +// containers in order to avoid ODR violations. +template + class fuzzer_allocator: public std::allocator { + public: + fuzzer_allocator() = default; + + template + fuzzer_allocator(const fuzzer_allocator&) {} + + template + struct rebind { typedef fuzzer_allocator other; }; + }; + +template +using Vector = std::vector>; + +template +using Set = std::set, fuzzer_allocator>; + +typedef Vector Unit; +typedef Vector UnitVector; +typedef int (*UserCallback)(const uint8_t *Data, size_t Size); + +int FuzzerDriver(int *argc, char ***argv, UserCallback Callback); + +uint8_t *ExtraCountersBegin(); +uint8_t *ExtraCountersEnd(); +void ClearExtraCounters(); + +extern bool RunningUserCallback; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_DEFS_H diff --git a/custom_mutators/libfuzzer/FuzzerDictionary.h b/custom_mutators/libfuzzer/FuzzerDictionary.h new file mode 100644 index 00000000..301c5d9a --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDictionary.h @@ -0,0 +1,118 @@ +//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::Dictionary +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DICTIONARY_H +#define LLVM_FUZZER_DICTIONARY_H + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" +#include "FuzzerUtil.h" +#include +#include + +namespace fuzzer { +// A simple POD sized array of bytes. +template class FixedWord { +public: + static const size_t kMaxSize = kMaxSizeT; + FixedWord() {} + FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); } + + void Set(const uint8_t *B, uint8_t S) { + assert(S <= kMaxSize); + memcpy(Data, B, S); + Size = S; + } + + bool operator==(const FixedWord &w) const { + return Size == w.Size && 0 == memcmp(Data, w.Data, Size); + } + + static size_t GetMaxSize() { return kMaxSize; } + const uint8_t *data() const { return Data; } + uint8_t size() const { return Size; } + +private: + uint8_t Size = 0; + uint8_t Data[kMaxSize]; +}; + +typedef FixedWord<64> Word; + +class DictionaryEntry { + public: + DictionaryEntry() {} + DictionaryEntry(Word W) : W(W) {} + DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {} + const Word &GetW() const { return W; } + + bool HasPositionHint() const { return PositionHint != std::numeric_limits::max(); } + size_t GetPositionHint() const { + assert(HasPositionHint()); + return PositionHint; + } + void IncUseCount() { UseCount++; } + void IncSuccessCount() { SuccessCount++; } + size_t GetUseCount() const { return UseCount; } + size_t GetSuccessCount() const {return SuccessCount; } + + void Print(const char *PrintAfter = "\n") { + PrintASCII(W.data(), W.size()); + if (HasPositionHint()) + Printf("@%zd", GetPositionHint()); + Printf("%s", PrintAfter); + } + +private: + Word W; + size_t PositionHint = std::numeric_limits::max(); + size_t UseCount = 0; + size_t SuccessCount = 0; +}; + +class Dictionary { + public: + static const size_t kMaxDictSize = 1 << 14; + + bool ContainsWord(const Word &W) const { + return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) { + return DE.GetW() == W; + }); + } + const DictionaryEntry *begin() const { return &DE[0]; } + const DictionaryEntry *end() const { return begin() + Size; } + DictionaryEntry & operator[] (size_t Idx) { + assert(Idx < Size); + return DE[Idx]; + } + void push_back(DictionaryEntry DE) { + if (Size < kMaxDictSize) + this->DE[Size++] = DE; + } + void clear() { Size = 0; } + bool empty() const { return Size == 0; } + size_t size() const { return Size; } + +private: + DictionaryEntry DE[kMaxDictSize]; + size_t Size = 0; +}; + +// Parses one dictionary entry. +// If successful, write the enty to Unit and returns true, +// otherwise returns false. +bool ParseOneDictionaryEntry(const std::string &Str, Unit *U); +// Parses the dictionary file, fills Units, returns true iff all lines +// were parsed successfully. +bool ParseDictionaryFile(const std::string &Text, Vector *Units); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_DICTIONARY_H diff --git a/custom_mutators/libfuzzer/FuzzerDriver.cpp b/custom_mutators/libfuzzer/FuzzerDriver.cpp new file mode 100644 index 00000000..9a0a32b0 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDriver.cpp @@ -0,0 +1,1122 @@ +//===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// FuzzerDriver and flag parsing. +//===----------------------------------------------------------------------===// + +#include "FuzzerCommand.h" +#include "FuzzerCorpus.h" +#include "FuzzerFork.h" +#include "FuzzerIO.h" +#include "FuzzerInterface.h" +#include "FuzzerInternal.h" +#include "FuzzerMerge.h" +#include "FuzzerMutate.h" +#include "FuzzerPlatform.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// This function should be present in the libFuzzer so that the client +// binary can test for its existence. +#if LIBFUZZER_MSVC +extern "C" void __libfuzzer_is_present() { + +} + + #if defined(_M_IX86) || defined(__i386__) + #pragma comment(linker, "/include:___libfuzzer_is_present") + #else + #pragma comment(linker, "/include:__libfuzzer_is_present") + #endif +#else +extern "C" __attribute__((used)) void __libfuzzer_is_present() { + +} + +#endif // LIBFUZZER_MSVC + +namespace fuzzer { + +// Program arguments. +struct FlagDescription { + + const char * Name; + const char * Description; + int Default; + int * IntFlag; + const char ** StrFlag; + unsigned int *UIntFlag; + +}; + +struct { +\ +#define FUZZER_DEPRECATED_FLAG(Name) +#define FUZZER_FLAG_INT(Name, Default, Description) int Name; +#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) unsigned int Name; +#define FUZZER_FLAG_STRING(Name, Description) const char *Name; +#include "FuzzerFlags.def" +#undef FUZZER_DEPRECATED_FLAG +#undef FUZZER_FLAG_INT +#undef FUZZER_FLAG_UNSIGNED +#undef FUZZER_FLAG_STRING + +} Flags; + +static const FlagDescription FlagDescriptions[]{ +\ +#define FUZZER_DEPRECATED_FLAG(Name) \ + {#Name, "Deprecated; don't use", 0, nullptr, nullptr, nullptr}, +#define FUZZER_FLAG_INT(Name, Default, Description) \ + {#Name, Description, Default, &Flags.Name, nullptr, nullptr}, +#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) \ + {#Name, Description, static_cast(Default), \ + nullptr, nullptr, &Flags.Name}, +#define FUZZER_FLAG_STRING(Name, Description) \ + {#Name, Description, 0, nullptr, &Flags.Name, nullptr}, +#include "FuzzerFlags.def" +#undef FUZZER_DEPRECATED_FLAG +#undef FUZZER_FLAG_INT +#undef FUZZER_FLAG_UNSIGNED +#undef FUZZER_FLAG_STRING + +}; + +static const size_t kNumFlags = + sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]); + +static Vector *Inputs; +static std::string * ProgName; + +static void PrintHelp() { + + Printf("Usage:\n"); + auto Prog = ProgName->c_str(); + Printf("\nTo run fuzzing pass 0 or more directories.\n"); + Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog); + + Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n"); + Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog); + + Printf("\nFlags: (strictly in form -flag=value)\n"); + size_t MaxFlagLen = 0; + for (size_t F = 0; F < kNumFlags; F++) + MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen); + + for (size_t F = 0; F < kNumFlags; F++) { + + const auto &D = FlagDescriptions[F]; + if (strstr(D.Description, "internal flag") == D.Description) continue; + Printf(" %s", D.Name); + for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++) + Printf(" "); + Printf("\t"); + Printf("%d\t%s\n", D.Default, D.Description); + + } + + Printf( + "\nFlags starting with '--' will be ignored and " + "will be passed verbatim to subprocesses.\n"); + +} + +static const char *FlagValue(const char *Param, const char *Name) { + + size_t Len = strlen(Name); + if (Param[0] == '-' && strstr(Param + 1, Name) == Param + 1 && + Param[Len + 1] == '=') + return &Param[Len + 2]; + return nullptr; + +} + +// Avoid calling stol as it triggers a bug in clang/glibc build. +static long MyStol(const char *Str) { + + long Res = 0; + long Sign = 1; + if (*Str == '-') { + + Str++; + Sign = -1; + + } + + for (size_t i = 0; Str[i]; i++) { + + char Ch = Str[i]; + if (Ch < '0' || Ch > '9') return Res; + Res = Res * 10 + (Ch - '0'); + + } + + return Res * Sign; + +} + +static bool ParseOneFlag(const char *Param) { + + if (Param[0] != '-') return false; + if (Param[1] == '-') { + + static bool PrintedWarning = false; + if (!PrintedWarning) { + + PrintedWarning = true; + Printf("INFO: libFuzzer ignores flags that start with '--'\n"); + + } + + for (size_t F = 0; F < kNumFlags; F++) + if (FlagValue(Param + 1, FlagDescriptions[F].Name)) + Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1); + return true; + + } + + for (size_t F = 0; F < kNumFlags; F++) { + + const char *Name = FlagDescriptions[F].Name; + const char *Str = FlagValue(Param, Name); + if (Str) { + + if (FlagDescriptions[F].IntFlag) { + + int Val = MyStol(Str); + *FlagDescriptions[F].IntFlag = Val; + if (Flags.verbosity >= 2) Printf("Flag: %s %d\n", Name, Val); + return true; + + } else if (FlagDescriptions[F].UIntFlag) { + + unsigned int Val = std::stoul(Str); + *FlagDescriptions[F].UIntFlag = Val; + if (Flags.verbosity >= 2) Printf("Flag: %s %u\n", Name, Val); + return true; + + } else if (FlagDescriptions[F].StrFlag) { + + *FlagDescriptions[F].StrFlag = Str; + if (Flags.verbosity >= 2) Printf("Flag: %s %s\n", Name, Str); + return true; + + } else { // Deprecated flag. + + Printf("Flag: %s: deprecated, don't use\n", Name); + return true; + + } + + } + + } + + Printf( + "\n\nWARNING: unrecognized flag '%s'; " + "use -help=1 to list all flags\n\n", + Param); + return true; + +} + +// We don't use any library to minimize dependencies. +static void ParseFlags(const Vector &Args, + const ExternalFunctions * EF) { + + for (size_t F = 0; F < kNumFlags; F++) { + + if (FlagDescriptions[F].IntFlag) + *FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default; + if (FlagDescriptions[F].UIntFlag) + *FlagDescriptions[F].UIntFlag = + static_cast(FlagDescriptions[F].Default); + if (FlagDescriptions[F].StrFlag) *FlagDescriptions[F].StrFlag = nullptr; + + } + + // Disable len_control by default, if LLVMFuzzerCustomMutator is used. + if (EF->LLVMFuzzerCustomMutator) { + + Flags.len_control = 0; + Printf( + "INFO: found LLVMFuzzerCustomMutator (%p). " + "Disabling -len_control by default.\n", + EF->LLVMFuzzerCustomMutator); + + } + + Inputs = new Vector; + for (size_t A = 1; A < Args.size(); A++) { + + if (ParseOneFlag(Args[A].c_str())) { + + if (Flags.ignore_remaining_args) break; + continue; + + } + + Inputs->push_back(Args[A]); + + } + +} + +static std::mutex Mu; + +static void PulseThread() { + + while (true) { + + SleepSeconds(600); + std::lock_guard Lock(Mu); + Printf("pulse...\n"); + + } + +} + +static void WorkerThread(const Command &BaseCmd, std::atomic *Counter, + unsigned NumJobs, std::atomic *HasErrors) { + + while (true) { + + unsigned C = (*Counter)++; + if (C >= NumJobs) break; + std::string Log = "fuzz-" + std::to_string(C) + ".log"; + Command Cmd(BaseCmd); + Cmd.setOutputFile(Log); + Cmd.combineOutAndErr(); + if (Flags.verbosity) { + + std::string CommandLine = Cmd.toString(); + Printf("%s\n", CommandLine.c_str()); + + } + + int ExitCode = ExecuteCommand(Cmd); + if (ExitCode != 0) *HasErrors = true; + std::lock_guard Lock(Mu); + Printf("================== Job %u exited with exit code %d ============\n", + C, ExitCode); + fuzzer::CopyFileToErr(Log); + + } + +} + +static void ValidateDirectoryExists(const std::string &Path, + bool CreateDirectory) { + + if (Path.empty()) { + + Printf("ERROR: Provided directory path is an empty string\n"); + exit(1); + + } + + if (IsDirectory(Path)) return; + + if (CreateDirectory) { + + if (!MkDirRecursive(Path)) { + + Printf("ERROR: Failed to create directory \"%s\"\n", Path.c_str()); + exit(1); + + } + + return; + + } + + Printf("ERROR: The required directory \"%s\" does not exist\n", Path.c_str()); + exit(1); + +} + +std::string CloneArgsWithoutX(const Vector &Args, const char *X1, + const char *X2) { + + std::string Cmd; + for (auto &S : Args) { + + if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2)) continue; + Cmd += S + " "; + + } + + return Cmd; + +} + +static int RunInMultipleProcesses(const Vector &Args, + unsigned NumWorkers, unsigned NumJobs) { + + std::atomic Counter(0); + std::atomic HasErrors(false); + Command Cmd(Args); + Cmd.removeFlag("jobs"); + Cmd.removeFlag("workers"); + Vector V; + std::thread Pulse(PulseThread); + Pulse.detach(); + for (unsigned i = 0; i < NumWorkers; i++) + V.push_back(std::thread(WorkerThread, std::ref(Cmd), &Counter, NumJobs, + &HasErrors)); + for (auto &T : V) + T.join(); + return HasErrors ? 1 : 0; + +} + +static void RssThread(Fuzzer *F, size_t RssLimitMb) { + + while (true) { + + SleepSeconds(1); + size_t Peak = GetPeakRSSMb(); + if (Peak > RssLimitMb) F->RssLimitCallback(); + + } + +} + +static void StartRssThread(Fuzzer *F, size_t RssLimitMb) { + + if (!RssLimitMb) return; + std::thread T(RssThread, F, RssLimitMb); + T.detach(); + +} + +int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) { + + Unit U = FileToVector(InputFilePath); + if (MaxLen && MaxLen < U.size()) U.resize(MaxLen); + F->ExecuteCallback(U.data(), U.size()); + F->TryDetectingAMemoryLeak(U.data(), U.size(), true); + return 0; + +} + +static bool AllInputsAreFiles() { + + if (Inputs->empty()) return false; + for (auto &Path : *Inputs) + if (!IsFile(Path)) return false; + return true; + +} + +static std::string GetDedupTokenFromCmdOutput(const std::string &S) { + + auto Beg = S.find("DEDUP_TOKEN:"); + if (Beg == std::string::npos) return ""; + auto End = S.find('\n', Beg); + if (End == std::string::npos) return ""; + return S.substr(Beg, End - Beg); + +} + +int CleanseCrashInput(const Vector &Args, + const FuzzingOptions & Options) { + + if (Inputs->size() != 1 || !Flags.exact_artifact_path) { + + Printf( + "ERROR: -cleanse_crash should be given one input file and" + " -exact_artifact_path\n"); + exit(1); + + } + + std::string InputFilePath = Inputs->at(0); + std::string OutputFilePath = Flags.exact_artifact_path; + Command Cmd(Args); + Cmd.removeFlag("cleanse_crash"); + + assert(Cmd.hasArgument(InputFilePath)); + Cmd.removeArgument(InputFilePath); + + auto TmpFilePath = TempPath("CleanseCrashInput", ".repro"); + Cmd.addArgument(TmpFilePath); + Cmd.setOutputFile(getDevNull()); + Cmd.combineOutAndErr(); + + std::string CurrentFilePath = InputFilePath; + auto U = FileToVector(CurrentFilePath); + size_t Size = U.size(); + + const Vector ReplacementBytes = {' ', 0xff}; + for (int NumAttempts = 0; NumAttempts < 5; NumAttempts++) { + + bool Changed = false; + for (size_t Idx = 0; Idx < Size; Idx++) { + + Printf("CLEANSE[%d]: Trying to replace byte %zd of %zd\n", NumAttempts, + Idx, Size); + uint8_t OriginalByte = U[Idx]; + if (ReplacementBytes.end() != std::find(ReplacementBytes.begin(), + ReplacementBytes.end(), + OriginalByte)) + continue; + for (auto NewByte : ReplacementBytes) { + + U[Idx] = NewByte; + WriteToFile(U, TmpFilePath); + auto ExitCode = ExecuteCommand(Cmd); + RemoveFile(TmpFilePath); + if (!ExitCode) { + + U[Idx] = OriginalByte; + + } else { + + Changed = true; + Printf("CLEANSE: Replaced byte %zd with 0x%x\n", Idx, NewByte); + WriteToFile(U, OutputFilePath); + break; + + } + + } + + } + + if (!Changed) break; + + } + + return 0; + +} + +int MinimizeCrashInput(const Vector &Args, + const FuzzingOptions & Options) { + + if (Inputs->size() != 1) { + + Printf("ERROR: -minimize_crash should be given one input file\n"); + exit(1); + + } + + std::string InputFilePath = Inputs->at(0); + Command BaseCmd(Args); + BaseCmd.removeFlag("minimize_crash"); + BaseCmd.removeFlag("exact_artifact_path"); + assert(BaseCmd.hasArgument(InputFilePath)); + BaseCmd.removeArgument(InputFilePath); + if (Flags.runs <= 0 && Flags.max_total_time == 0) { + + Printf( + "INFO: you need to specify -runs=N or " + "-max_total_time=N with -minimize_crash=1\n" + "INFO: defaulting to -max_total_time=600\n"); + BaseCmd.addFlag("max_total_time", "600"); + + } + + BaseCmd.combineOutAndErr(); + + std::string CurrentFilePath = InputFilePath; + while (true) { + + Unit U = FileToVector(CurrentFilePath); + Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n", + CurrentFilePath.c_str(), U.size()); + + Command Cmd(BaseCmd); + Cmd.addArgument(CurrentFilePath); + + Printf("CRASH_MIN: executing: %s\n", Cmd.toString().c_str()); + std::string CmdOutput; + bool Success = ExecuteCommand(Cmd, &CmdOutput); + if (Success) { + + Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str()); + exit(1); + + } + + Printf( + "CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize " + "it further\n", + CurrentFilePath.c_str(), U.size()); + auto DedupToken1 = GetDedupTokenFromCmdOutput(CmdOutput); + if (!DedupToken1.empty()) + Printf("CRASH_MIN: DedupToken1: %s\n", DedupToken1.c_str()); + + std::string ArtifactPath = + Flags.exact_artifact_path + ? Flags.exact_artifact_path + : Options.ArtifactPrefix + "minimized-from-" + Hash(U); + Cmd.addFlag("minimize_crash_internal_step", "1"); + Cmd.addFlag("exact_artifact_path", ArtifactPath); + Printf("CRASH_MIN: executing: %s\n", Cmd.toString().c_str()); + CmdOutput.clear(); + Success = ExecuteCommand(Cmd, &CmdOutput); + Printf("%s", CmdOutput.c_str()); + if (Success) { + + if (Flags.exact_artifact_path) { + + CurrentFilePath = Flags.exact_artifact_path; + WriteToFile(U, CurrentFilePath); + + } + + Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n", + CurrentFilePath.c_str(), U.size()); + break; + + } + + auto DedupToken2 = GetDedupTokenFromCmdOutput(CmdOutput); + if (!DedupToken2.empty()) + Printf("CRASH_MIN: DedupToken2: %s\n", DedupToken2.c_str()); + + if (DedupToken1 != DedupToken2) { + + if (Flags.exact_artifact_path) { + + CurrentFilePath = Flags.exact_artifact_path; + WriteToFile(U, CurrentFilePath); + + } + + Printf( + "CRASH_MIN: mismatch in dedup tokens" + " (looks like a different bug). Won't minimize further\n"); + break; + + } + + CurrentFilePath = ArtifactPath; + Printf("*********************************\n"); + + } + + return 0; + +} + +int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { + + assert(Inputs->size() == 1); + std::string InputFilePath = Inputs->at(0); + Unit U = FileToVector(InputFilePath); + Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size()); + if (U.size() < 2) { + + Printf("INFO: The input is small enough, exiting\n"); + exit(0); + + } + + F->SetMaxInputLen(U.size()); + F->SetMaxMutationLen(U.size() - 1); + F->MinimizeCrashLoop(U); + Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n"); + exit(0); + return 0; + +} + +void Merge(Fuzzer *F, FuzzingOptions &Options, const Vector &Args, + const Vector &Corpora, const char *CFPathOrNull) { + + if (Corpora.size() < 2) { + + Printf("INFO: Merge requires two or more corpus dirs\n"); + exit(0); + + } + + Vector OldCorpus, NewCorpus; + GetSizedFilesFromDir(Corpora[0], &OldCorpus); + for (size_t i = 1; i < Corpora.size(); i++) + GetSizedFilesFromDir(Corpora[i], &NewCorpus); + std::sort(OldCorpus.begin(), OldCorpus.end()); + std::sort(NewCorpus.begin(), NewCorpus.end()); + + std::string CFPath = CFPathOrNull ? CFPathOrNull : TempPath("Merge", ".txt"); + Vector NewFiles; + Set NewFeatures, NewCov; + CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures, + {}, &NewCov, CFPath, true); + for (auto &Path : NewFiles) + F->WriteToOutputCorpus(FileToVector(Path, Options.MaxLen)); + // We are done, delete the control file if it was a temporary one. + if (!Flags.merge_control_file) RemoveFile(CFPath); + + exit(0); + +} + +int AnalyzeDictionary(Fuzzer *F, const Vector &Dict, UnitVector &Corpus) { + + Printf("Started dictionary minimization (up to %d tests)\n", + Dict.size() * Corpus.size() * 2); + + // Scores and usage count for each dictionary unit. + Vector Scores(Dict.size()); + Vector Usages(Dict.size()); + + Vector InitialFeatures; + Vector ModifiedFeatures; + for (auto &C : Corpus) { + + // Get coverage for the testcase without modifications. + F->ExecuteCallback(C.data(), C.size()); + InitialFeatures.clear(); + TPC.CollectFeatures( + [&](size_t Feature) { InitialFeatures.push_back(Feature); }); + + for (size_t i = 0; i < Dict.size(); ++i) { + + Vector Data = C; + auto StartPos = + std::search(Data.begin(), Data.end(), Dict[i].begin(), Dict[i].end()); + // Skip dictionary unit, if the testcase does not contain it. + if (StartPos == Data.end()) continue; + + ++Usages[i]; + while (StartPos != Data.end()) { + + // Replace all occurrences of dictionary unit in the testcase. + auto EndPos = StartPos + Dict[i].size(); + for (auto It = StartPos; It != EndPos; ++It) + *It ^= 0xFF; + + StartPos = + std::search(EndPos, Data.end(), Dict[i].begin(), Dict[i].end()); + + } + + // Get coverage for testcase with masked occurrences of dictionary unit. + F->ExecuteCallback(Data.data(), Data.size()); + ModifiedFeatures.clear(); + TPC.CollectFeatures( + [&](size_t Feature) { ModifiedFeatures.push_back(Feature); }); + + if (InitialFeatures == ModifiedFeatures) + --Scores[i]; + else + Scores[i] += 2; + + } + + } + + Printf("###### Useless dictionary elements. ######\n"); + for (size_t i = 0; i < Dict.size(); ++i) { + + // Dictionary units with positive score are treated as useful ones. + if (Scores[i] > 0) continue; + + Printf("\""); + PrintASCII(Dict[i].data(), Dict[i].size(), "\""); + Printf(" # Score: %d, Used: %d\n", Scores[i], Usages[i]); + + } + + Printf("###### End of useless dictionary elements. ######\n"); + return 0; + +} + +Vector ParseSeedInuts(const char *seed_inputs) { + + // Parse -seed_inputs=file1,file2,... or -seed_inputs=@seed_inputs_file + Vector Files; + if (!seed_inputs) return Files; + std::string SeedInputs; + if (Flags.seed_inputs[0] == '@') + SeedInputs = FileToString(Flags.seed_inputs + 1); // File contains list. + else + SeedInputs = Flags.seed_inputs; // seed_inputs contains the list. + if (SeedInputs.empty()) { + + Printf("seed_inputs is empty or @file does not exist.\n"); + exit(1); + + } + + // Parse SeedInputs. + size_t comma_pos = 0; + while ((comma_pos = SeedInputs.find_last_of(',')) != std::string::npos) { + + Files.push_back(SeedInputs.substr(comma_pos + 1)); + SeedInputs = SeedInputs.substr(0, comma_pos); + + } + + Files.push_back(SeedInputs); + return Files; + +} + +static Vector ReadCorpora( + const Vector &CorpusDirs, + const Vector &ExtraSeedFiles) { + + Vector SizedFiles; + size_t LastNumFiles = 0; + for (auto &Dir : CorpusDirs) { + + GetSizedFilesFromDir(Dir, &SizedFiles); + Printf("INFO: % 8zd files found in %s\n", SizedFiles.size() - LastNumFiles, + Dir.c_str()); + LastNumFiles = SizedFiles.size(); + + } + + for (auto &File : ExtraSeedFiles) + if (auto Size = FileSize(File)) SizedFiles.push_back({File, Size}); + return SizedFiles; + +} + +int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { + + using namespace fuzzer; + assert(argc && argv && "Argument pointers cannot be nullptr"); + std::string Argv0((*argv)[0]); + EF = new ExternalFunctions(); + if (EF->LLVMFuzzerInitialize) EF->LLVMFuzzerInitialize(argc, argv); + if (EF->__msan_scoped_disable_interceptor_checks) + EF->__msan_scoped_disable_interceptor_checks(); + const Vector Args(*argv, *argv + *argc); + assert(!Args.empty()); + ProgName = new std::string(Args[0]); + if (Argv0 != *ProgName) { + + Printf("ERROR: argv[0] has been modified in LLVMFuzzerInitialize\n"); + exit(1); + + } + + ParseFlags(Args, EF); + if (Flags.help) { + + PrintHelp(); + return 0; + + } + + if (Flags.close_fd_mask & 2) DupAndCloseStderr(); + if (Flags.close_fd_mask & 1) CloseStdout(); + + if (Flags.jobs > 0 && Flags.workers == 0) { + + Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs); + if (Flags.workers > 1) Printf("Running %u workers\n", Flags.workers); + + } + + if (Flags.workers > 0 && Flags.jobs > 0) + return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs); + + FuzzingOptions Options; + Options.Verbosity = Flags.verbosity; + Options.MaxLen = Flags.max_len; + Options.LenControl = Flags.len_control; + Options.KeepSeed = Flags.keep_seed; + Options.UnitTimeoutSec = Flags.timeout; + Options.ErrorExitCode = Flags.error_exitcode; + Options.TimeoutExitCode = Flags.timeout_exitcode; + Options.IgnoreTimeouts = Flags.ignore_timeouts; + Options.IgnoreOOMs = Flags.ignore_ooms; + Options.IgnoreCrashes = Flags.ignore_crashes; + Options.MaxTotalTimeSec = Flags.max_total_time; + Options.DoCrossOver = Flags.cross_over; + Options.CrossOverUniformDist = Flags.cross_over_uniform_dist; + Options.MutateDepth = Flags.mutate_depth; + Options.ReduceDepth = Flags.reduce_depth; + Options.UseCounters = Flags.use_counters; + Options.UseMemmem = Flags.use_memmem; + Options.UseCmp = Flags.use_cmp; + Options.UseValueProfile = Flags.use_value_profile; + Options.Shrink = Flags.shrink; + Options.ReduceInputs = Flags.reduce_inputs; + Options.ShuffleAtStartUp = Flags.shuffle; + Options.PreferSmall = Flags.prefer_small; + Options.ReloadIntervalSec = Flags.reload; + Options.OnlyASCII = Flags.only_ascii; + Options.DetectLeaks = Flags.detect_leaks; + Options.PurgeAllocatorIntervalSec = Flags.purge_allocator_interval; + Options.TraceMalloc = Flags.trace_malloc; + Options.RssLimitMb = Flags.rss_limit_mb; + Options.MallocLimitMb = Flags.malloc_limit_mb; + if (!Options.MallocLimitMb) Options.MallocLimitMb = Options.RssLimitMb; + if (Flags.runs >= 0) Options.MaxNumberOfRuns = Flags.runs; + if (!Inputs->empty() && !Flags.minimize_crash_internal_step) { + + // Ensure output corpus assumed to be the first arbitrary argument input + // is not a path to an existing file. + std::string OutputCorpusDir = (*Inputs)[0]; + if (!IsFile(OutputCorpusDir)) { + + Options.OutputCorpus = OutputCorpusDir; + ValidateDirectoryExists(Options.OutputCorpus, Flags.create_missing_dirs); + + } + + } + + Options.ReportSlowUnits = Flags.report_slow_units; + if (Flags.artifact_prefix) { + + Options.ArtifactPrefix = Flags.artifact_prefix; + + // Since the prefix could be a full path to a file name prefix, assume + // that if the path ends with the platform's separator that a directory + // is desired + std::string ArtifactPathDir = Options.ArtifactPrefix; + if (!IsSeparator(ArtifactPathDir[ArtifactPathDir.length() - 1])) { + + ArtifactPathDir = DirName(ArtifactPathDir); + + } + + ValidateDirectoryExists(ArtifactPathDir, Flags.create_missing_dirs); + + } + + if (Flags.exact_artifact_path) { + + Options.ExactArtifactPath = Flags.exact_artifact_path; + ValidateDirectoryExists(DirName(Options.ExactArtifactPath), + Flags.create_missing_dirs); + + } + + Vector Dictionary; + if (Flags.dict) + if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary)) return 1; + if (Flags.verbosity > 0 && !Dictionary.empty()) + Printf("Dictionary: %zd entries\n", Dictionary.size()); + bool RunIndividualFiles = AllInputsAreFiles(); + Options.SaveArtifacts = + !RunIndividualFiles || Flags.minimize_crash_internal_step; + Options.PrintNewCovPcs = Flags.print_pcs; + Options.PrintNewCovFuncs = Flags.print_funcs; + Options.PrintFinalStats = Flags.print_final_stats; + Options.PrintCorpusStats = Flags.print_corpus_stats; + Options.PrintCoverage = Flags.print_coverage; + if (Flags.exit_on_src_pos) Options.ExitOnSrcPos = Flags.exit_on_src_pos; + if (Flags.exit_on_item) Options.ExitOnItem = Flags.exit_on_item; + if (Flags.focus_function) Options.FocusFunction = Flags.focus_function; + if (Flags.data_flow_trace) Options.DataFlowTrace = Flags.data_flow_trace; + if (Flags.features_dir) { + + Options.FeaturesDir = Flags.features_dir; + ValidateDirectoryExists(Options.FeaturesDir, Flags.create_missing_dirs); + + } + + if (Flags.mutation_graph_file) + Options.MutationGraphFile = Flags.mutation_graph_file; + if (Flags.collect_data_flow) + Options.CollectDataFlow = Flags.collect_data_flow; + if (Flags.stop_file) Options.StopFile = Flags.stop_file; + Options.Entropic = Flags.entropic; + Options.EntropicFeatureFrequencyThreshold = + (size_t)Flags.entropic_feature_frequency_threshold; + Options.EntropicNumberOfRarestFeatures = + (size_t)Flags.entropic_number_of_rarest_features; + Options.EntropicScalePerExecTime = Flags.entropic_scale_per_exec_time; + if (Options.Entropic) { + + if (!Options.FocusFunction.empty()) { + + Printf( + "ERROR: The parameters `--entropic` and `--focus_function` cannot " + "be used together.\n"); + exit(1); + + } + + Printf("INFO: Running with entropic power schedule (0x%X, %d).\n", + Options.EntropicFeatureFrequencyThreshold, + Options.EntropicNumberOfRarestFeatures); + + } + + struct EntropicOptions Entropic; + Entropic.Enabled = Options.Entropic; + Entropic.FeatureFrequencyThreshold = + Options.EntropicFeatureFrequencyThreshold; + Entropic.NumberOfRarestFeatures = Options.EntropicNumberOfRarestFeatures; + Entropic.ScalePerExecTime = Options.EntropicScalePerExecTime; + + unsigned Seed = Flags.seed; + // Initialize Seed. + if (Seed == 0) + Seed = + std::chrono::system_clock::now().time_since_epoch().count() + GetPid(); + if (Flags.verbosity) Printf("INFO: Seed: %u\n", Seed); + + if (Flags.collect_data_flow && !Flags.fork && !Flags.merge) { + + if (RunIndividualFiles) + return CollectDataFlow(Flags.collect_data_flow, Flags.data_flow_trace, + ReadCorpora({}, *Inputs)); + else + return CollectDataFlow(Flags.collect_data_flow, Flags.data_flow_trace, + ReadCorpora(*Inputs, {})); + + } + + Random Rand(Seed); + auto * MD = new MutationDispatcher(Rand, Options); + auto * Corpus = new InputCorpus(Options.OutputCorpus, Entropic); + auto * F = new Fuzzer(Callback, *Corpus, *MD, Options); + + for (auto &U : Dictionary) + if (U.size() <= Word::GetMaxSize()) + MD->AddWordToManualDictionary(Word(U.data(), U.size())); + + // Threads are only supported by Chrome. Don't use them with emscripten + // for now. +#if !LIBFUZZER_EMSCRIPTEN + StartRssThread(F, Flags.rss_limit_mb); +#endif // LIBFUZZER_EMSCRIPTEN + + Options.HandleAbrt = Flags.handle_abrt; + Options.HandleAlrm = !Flags.minimize_crash; + Options.HandleBus = Flags.handle_bus; + Options.HandleFpe = Flags.handle_fpe; + Options.HandleIll = Flags.handle_ill; + Options.HandleInt = Flags.handle_int; + Options.HandleSegv = Flags.handle_segv; + Options.HandleTerm = Flags.handle_term; + Options.HandleXfsz = Flags.handle_xfsz; + Options.HandleUsr1 = Flags.handle_usr1; + Options.HandleUsr2 = Flags.handle_usr2; + SetSignalHandler(Options); + + std::atexit(Fuzzer::StaticExitCallback); + + if (Flags.minimize_crash) return MinimizeCrashInput(Args, Options); + + if (Flags.minimize_crash_internal_step) + return MinimizeCrashInputInternalStep(F, Corpus); + + if (Flags.cleanse_crash) return CleanseCrashInput(Args, Options); + + if (RunIndividualFiles) { + + Options.SaveArtifacts = false; + int Runs = std::max(1, Flags.runs); + Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(), + Inputs->size(), Runs); + for (auto &Path : *Inputs) { + + auto StartTime = system_clock::now(); + Printf("Running: %s\n", Path.c_str()); + for (int Iter = 0; Iter < Runs; Iter++) + RunOneTest(F, Path.c_str(), Options.MaxLen); + auto StopTime = system_clock::now(); + auto MS = duration_cast(StopTime - StartTime).count(); + Printf("Executed %s in %zd ms\n", Path.c_str(), (long)MS); + + } + + Printf( + "***\n" + "*** NOTE: fuzzing was not performed, you have only\n" + "*** executed the target code on a fixed set of inputs.\n" + "***\n"); + F->PrintFinalStats(); + exit(0); + + } + + if (Flags.fork) + FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork); + + if (Flags.merge) Merge(F, Options, Args, *Inputs, Flags.merge_control_file); + + if (Flags.merge_inner) { + + const size_t kDefaultMaxMergeLen = 1 << 20; + if (Options.MaxLen == 0) F->SetMaxInputLen(kDefaultMaxMergeLen); + assert(Flags.merge_control_file); + F->CrashResistantMergeInternalStep(Flags.merge_control_file); + exit(0); + + } + + if (Flags.analyze_dict) { + + size_t MaxLen = INT_MAX; // Large max length. + UnitVector InitialCorpus; + for (auto &Inp : *Inputs) { + + Printf("Loading corpus dir: %s\n", Inp.c_str()); + ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr, MaxLen, + /*ExitOnError=*/false); + + } + + if (Dictionary.empty() || Inputs->empty()) { + + Printf("ERROR: can't analyze dict without dict and corpus provided\n"); + return 1; + + } + + if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) { + + Printf("Dictionary analysis failed\n"); + exit(1); + + } + + Printf("Dictionary analysis succeeded\n"); + exit(0); + + } + + auto CorporaFiles = ReadCorpora(*Inputs, ParseSeedInuts(Flags.seed_inputs)); + F->Loop(CorporaFiles); + + if (Flags.verbosity) + Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(), + F->secondsSinceProcessStartUp()); + F->PrintFinalStats(); + + exit(0); // Don't let F destroy itself. + +} + +extern "C" ATTRIBUTE_INTERFACE int LLVMFuzzerRunDriver( + int *argc, char ***argv, int (*UserCb)(const uint8_t *Data, size_t Size)) { + + return FuzzerDriver(argc, argv, UserCb); + +} + +#include "libfuzzer.inc" + +// Storage for global ExternalFunctions object. +ExternalFunctions *EF = nullptr; + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctions.def b/custom_mutators/libfuzzer/FuzzerExtFunctions.def new file mode 100644 index 00000000..51edf844 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctions.def @@ -0,0 +1,50 @@ +//===- FuzzerExtFunctions.def - External functions --------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This defines the external function pointers that +// ``fuzzer::ExternalFunctions`` should contain and try to initialize. The +// EXT_FUNC macro must be defined at the point of inclusion. The signature of +// the macro is: +// +// EXT_FUNC(, , , ) +//===----------------------------------------------------------------------===// + +// Optional user functions +EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false); +EXT_FUNC(LLVMFuzzerCustomMutator, size_t, + (uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed), + false); +EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t, + (const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize, unsigned int Seed), + false); + +// Sanitizer functions +EXT_FUNC(__lsan_enable, void, (), false); +EXT_FUNC(__lsan_disable, void, (), false); +EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false); +EXT_FUNC(__sanitizer_acquire_crash_state, int, (), true); +EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int, + (void (*malloc_hook)(const volatile void *, size_t), + void (*free_hook)(const volatile void *)), + false); +EXT_FUNC(__sanitizer_log_write, void, (const char *buf, size_t len), false); +EXT_FUNC(__sanitizer_purge_allocator, void, (), false); +EXT_FUNC(__sanitizer_print_memory_profile, void, (size_t, size_t), false); +EXT_FUNC(__sanitizer_print_stack_trace, void, (), true); +EXT_FUNC(__sanitizer_symbolize_pc, void, + (void *, const char *fmt, char *out_buf, size_t out_buf_size), false); +EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int, + (void *pc, char *module_path, + size_t module_path_len,void **pc_offset), false); +EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true); +EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false); +EXT_FUNC(__msan_scoped_disable_interceptor_checks, void, (), false); +EXT_FUNC(__msan_scoped_enable_interceptor_checks, void, (), false); +EXT_FUNC(__msan_unpoison, void, (const volatile void *, size_t size), false); +EXT_FUNC(__msan_unpoison_param, void, (size_t n), false); diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctions.h b/custom_mutators/libfuzzer/FuzzerExtFunctions.h new file mode 100644 index 00000000..c88aac4e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctions.h @@ -0,0 +1,34 @@ +//===- FuzzerExtFunctions.h - Interface to external functions ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Defines an interface to (possibly optional) functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_EXT_FUNCTIONS_H +#define LLVM_FUZZER_EXT_FUNCTIONS_H + +#include +#include + +namespace fuzzer { + +struct ExternalFunctions { + // Initialize function pointers. Functions that are not available will be set + // to nullptr. Do not call this constructor before ``main()`` has been + // entered. + ExternalFunctions(); + +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + RETURN_TYPE(*NAME) FUNC_SIG = nullptr + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +}; +} // namespace fuzzer + +#endif diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp b/custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp new file mode 100644 index 00000000..8009b237 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp @@ -0,0 +1,60 @@ +//===- FuzzerExtFunctionsDlsym.cpp - Interface to external functions ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Implementation for operating systems that support dlsym(). We only use it on +// Apple platforms for now. We don't use this approach on Linux because it +// requires that clients of LibFuzzer pass ``--export-dynamic`` to the linker. +// That is a complication we don't wish to expose to clients right now. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_APPLE + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + #include + +using namespace fuzzer; + +template +static T GetFnPtr(const char *FnName, bool WarnIfMissing) { + + dlerror(); // Clear any previous errors. + void *Fn = dlsym(RTLD_DEFAULT, FnName); + if (Fn == nullptr) { + + if (WarnIfMissing) { + + const char *ErrorMsg = dlerror(); + Printf("WARNING: Failed to find function \"%s\".", FnName); + if (ErrorMsg) Printf(" Reason %s.", ErrorMsg); + Printf("\n"); + + } + + } + + return reinterpret_cast(Fn); + +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +\ + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = GetFnPtr(#NAME, WARN) + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_APPLE + diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp b/custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp new file mode 100644 index 00000000..c7a1d05e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp @@ -0,0 +1,63 @@ +//===- FuzzerExtFunctionsWeak.cpp - Interface to external functions -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Implementation for Linux. This relies on the linker's support for weak +// symbols. We don't use this approach on Apple platforms because it requires +// clients of LibFuzzer to pass ``-U _`` to the linker to allow +// weak symbols to be undefined. That is a complication we don't want to expose +// to clients right now. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FUCHSIA || \ + LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + +extern "C" { + + // Declare these symbols as weak to allow them to be optionally defined. + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + __attribute__((weak, visibility("default"))) RETURN_TYPE NAME FUNC_SIG + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +using namespace fuzzer; + +static void CheckFnPtr(void *FnPtr, const char *FnName, bool WarnIfMissing) { + + if (FnPtr == nullptr && WarnIfMissing) { + + Printf("WARNING: Failed to find function \"%s\".\n", FnName); + + } + +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +\ + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = ::NAME; \ + CheckFnPtr(reinterpret_cast(reinterpret_cast(::NAME)), \ + #NAME, WARN); + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +} // namespace fuzzer + +#endif + diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp b/custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp new file mode 100644 index 00000000..a727220a --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp @@ -0,0 +1,95 @@ +//=== FuzzerExtWindows.cpp - Interface to external functions --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Implementation of FuzzerExtFunctions for Windows. Uses alternatename when +// compiled with MSVC. Uses weak aliases when compiled with clang. Unfortunately +// the method each compiler supports is not supported by the other. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_WINDOWS + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + +using namespace fuzzer; + + // Intermediate macro to ensure the parameter is expanded before stringified. + #define STRINGIFY_(A) #A + #define STRINGIFY(A) STRINGIFY_(A) + + #if LIBFUZZER_MSVC + // Copied from compiler-rt/lib/sanitizer_common/sanitizer_win_defs.h + #if defined(_M_IX86) || defined(__i386__) + #define WIN_SYM_PREFIX "_" + #else + #define WIN_SYM_PREFIX + #endif + + // Declare external functions as having alternativenames, so that we can + // determine if they are not defined. + #define EXTERNAL_FUNC(Name, Default) \ + __pragma( \ + comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY( \ + Name) "=" WIN_SYM_PREFIX STRINGIFY(Default))) + #else + // Declare external functions as weak to allow them to default to a + // specified function if not defined explicitly. We must use weak symbols + // because clang's support for alternatename is not 100%, see + // https://bugs.llvm.org/show_bug.cgi?id=40218 for more details. + #define EXTERNAL_FUNC(Name, Default) \ + __attribute__((weak, alias(STRINGIFY(Default)))) + #endif // LIBFUZZER_MSVC + +extern "C" { +\ + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + RETURN_TYPE NAME##Def FUNC_SIG { \ + \ + Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \ + exit(1); \ + \ + } \ + EXTERNAL_FUNC(NAME, NAME##Def) RETURN_TYPE NAME FUNC_SIG + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +template +static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) { + + if (Fun == FunDef) { + + if (WarnIfMissing) + Printf("WARNING: Failed to find function \"%s\".\n", FnName); + return nullptr; + + } + + return Fun; + +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +\ + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = GetFnPtr(::NAME, ::NAME##Def, #NAME, WARN); + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS + diff --git a/custom_mutators/libfuzzer/FuzzerExtraCounters.cpp b/custom_mutators/libfuzzer/FuzzerExtraCounters.cpp new file mode 100644 index 00000000..3ff9b0d5 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtraCounters.cpp @@ -0,0 +1,71 @@ +//===- FuzzerExtraCounters.cpp - Extra coverage counters ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Extra coverage counters defined by user code. +//===----------------------------------------------------------------------===// + +#include "FuzzerPlatform.h" +#include + +#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \ + LIBFUZZER_OPENBSD || LIBFUZZER_FUCHSIA || LIBFUZZER_EMSCRIPTEN +__attribute__((weak)) extern uint8_t __start___libfuzzer_extra_counters; +__attribute__((weak)) extern uint8_t __stop___libfuzzer_extra_counters; + +namespace fuzzer { + +uint8_t *ExtraCountersBegin() { + + return &__start___libfuzzer_extra_counters; + +} + +uint8_t *ExtraCountersEnd() { + + return &__stop___libfuzzer_extra_counters; + +} + +ATTRIBUTE_NO_SANITIZE_ALL +void ClearExtraCounters() { // hand-written memset, don't asan-ify. + uintptr_t *Beg = reinterpret_cast(ExtraCountersBegin()); + uintptr_t *End = reinterpret_cast(ExtraCountersEnd()); + for (; Beg < End; Beg++) { + + *Beg = 0; + __asm__ __volatile__("" : : : "memory"); + + } + +} + +} // namespace fuzzer + +#else +// TODO: implement for other platforms. +namespace fuzzer { + +uint8_t *ExtraCountersBegin() { + + return nullptr; + +} + +uint8_t *ExtraCountersEnd() { + + return nullptr; + +} + +void ClearExtraCounters() { + +} + +} // namespace fuzzer + +#endif + diff --git a/custom_mutators/libfuzzer/FuzzerFlags.def b/custom_mutators/libfuzzer/FuzzerFlags.def new file mode 100644 index 00000000..c9a787e0 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerFlags.def @@ -0,0 +1,197 @@ +//===- FuzzerFlags.def - Run-time flags -------------------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the +// point of inclusion. We are not using any flag parsing library for better +// portability and independence. +//===----------------------------------------------------------------------===// +FUZZER_FLAG_INT(verbosity, 1, "Verbosity level.") +FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.") +FUZZER_FLAG_INT(runs, -1, + "Number of individual test runs (-1 for infinite runs).") +FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. " + "If 0, libFuzzer tries to guess a good value based on the corpus " + "and reports it. ") +FUZZER_FLAG_INT(len_control, 100, "Try generating small inputs first, " + "then try larger inputs over time. Specifies the rate at which the length " + "limit is increased (smaller == faster). If 0, immediately try inputs with " + "size up to max_len. Default value is 0, if LLVMFuzzerCustomMutator is used.") +FUZZER_FLAG_STRING(seed_inputs, "A comma-separated list of input files " + "to use as an additional seed corpus. Alternatively, an \"@\" followed by " + "the name of a file containing the comma-separated list.") +FUZZER_FLAG_INT(keep_seed, 0, "If 1, keep seed inputs in the corpus even if " + "they do not produce new coverage. When used with |reduce_inputs==1|, the " + "seed inputs will never be reduced. This option can be useful when seeds are" + "not properly formed for the fuzz target but still have useful snippets.") +FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.") +FUZZER_FLAG_INT(cross_over_uniform_dist, 0, "Experimental. If 1, use a " + "uniform probability distribution when choosing inputs to cross over with. " + "Some of the inputs in the corpus may never get chosen for mutation " + "depending on the input mutation scheduling policy. With this flag, all " + "inputs, regardless of the input mutation scheduling policy, can be chosen " + "as an input to cross over with. This can be particularly useful with " + "|keep_seed==1|; all the initial seed inputs, even though they do not " + "increase coverage because they are not properly formed, will still be " + "chosen as an input to cross over with.") + +FUZZER_FLAG_INT(mutate_depth, 5, + "Apply this number of consecutive mutations to each input.") +FUZZER_FLAG_INT(reduce_depth, 0, "Experimental/internal. " + "Reduce depth if mutations lose unique features") +FUZZER_FLAG_INT(shuffle, 1, "Shuffle inputs at startup") +FUZZER_FLAG_INT(prefer_small, 1, + "If 1, always prefer smaller inputs during the corpus shuffle.") +FUZZER_FLAG_INT( + timeout, 1200, + "Timeout in seconds (if positive). " + "If one unit runs more than this number of seconds the process will abort.") +FUZZER_FLAG_INT(error_exitcode, 77, "When libFuzzer itself reports a bug " + "this exit code will be used.") +FUZZER_FLAG_INT(timeout_exitcode, 70, "When libFuzzer reports a timeout " + "this exit code will be used.") +FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total " + "time in seconds to run the fuzzer.") +FUZZER_FLAG_INT(help, 0, "Print help.") +FUZZER_FLAG_INT(fork, 0, "Experimental mode where fuzzing happens " + "in a subprocess") +FUZZER_FLAG_INT(ignore_timeouts, 1, "Ignore timeouts in fork mode") +FUZZER_FLAG_INT(ignore_ooms, 1, "Ignore OOMs in fork mode") +FUZZER_FLAG_INT(ignore_crashes, 0, "Ignore crashes in fork mode") +FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be " + "merged into the 1-st corpus. Only interesting units will be taken. " + "This flag can be used to minimize a corpus.") +FUZZER_FLAG_STRING(stop_file, "Stop fuzzing ASAP if this file exists") +FUZZER_FLAG_STRING(merge_inner, "internal flag") +FUZZER_FLAG_STRING(merge_control_file, + "Specify a control file used for the merge process. " + "If a merge process gets killed it tries to leave this file " + "in a state suitable for resuming the merge. " + "By default a temporary file will be used." + "The same file can be used for multistep merge process.") +FUZZER_FLAG_INT(minimize_crash, 0, "If 1, minimizes the provided" + " crash input. Use with -runs=N or -max_total_time=N to limit " + "the number attempts." + " Use with -exact_artifact_path to specify the output." + " Combine with ASAN_OPTIONS=dedup_token_length=3 (or similar) to ensure that" + " the minimized input triggers the same crash." + ) +FUZZER_FLAG_INT(cleanse_crash, 0, "If 1, tries to cleanse the provided" + " crash input to make it contain fewer original bytes." + " Use with -exact_artifact_path to specify the output." + ) +FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag") +FUZZER_FLAG_STRING(features_dir, "internal flag. Used to dump feature sets on disk." + "Every time a new input is added to the corpus, a corresponding file in the features_dir" + " is created containing the unique features of that input." + " Features are stored in binary format.") +FUZZER_FLAG_STRING(mutation_graph_file, "Saves a graph (in DOT format) to" + " mutation_graph_file. The graph contains a vertex for each input that has" + " unique coverage; directed edges are provided between parents and children" + " where the child has unique coverage, and are recorded with the type of" + " mutation that caused the child.") +FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters") +FUZZER_FLAG_INT(use_memmem, 1, + "Use hints from intercepting memmem, strstr, etc") +FUZZER_FLAG_INT(use_value_profile, 0, + "Experimental. Use value profile to guide fuzzing.") +FUZZER_FLAG_INT(use_cmp, 1, "Use CMP traces to guide mutations") +FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus inputs.") +FUZZER_FLAG_INT(reduce_inputs, 1, + "Try to reduce the size of inputs while preserving their full feature sets") +FUZZER_FLAG_UNSIGNED(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn" + " this number of jobs in separate worker processes" + " with stdout/stderr redirected to fuzz-JOB.log.") +FUZZER_FLAG_UNSIGNED(workers, 0, + "Number of simultaneous worker processes to run the jobs." + " If zero, \"min(jobs,NumberOfCpuCores()/2)\" is used.") +FUZZER_FLAG_INT(reload, 1, + "Reload the main corpus every seconds to get new units" + " discovered by other processes. If 0, disabled") +FUZZER_FLAG_INT(report_slow_units, 10, + "Report slowest units if they run for more than this number of seconds.") +FUZZER_FLAG_INT(only_ascii, 0, + "If 1, generate only ASCII (isprint+isspace) inputs.") +FUZZER_FLAG_STRING(dict, "Experimental. Use the dictionary file.") +FUZZER_FLAG_STRING(artifact_prefix, "Write fuzzing artifacts (crash, " + "timeout, or slow inputs) as " + "$(artifact_prefix)file") +FUZZER_FLAG_STRING(exact_artifact_path, + "Write the single artifact on failure (crash, timeout) " + "as $(exact_artifact_path). This overrides -artifact_prefix " + "and will not use checksum in the file name. Do not " + "use the same path for several parallel processes.") +FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.") +FUZZER_FLAG_INT(print_funcs, 2, "If >=1, print out at most this number of " + "newly covered functions.") +FUZZER_FLAG_INT(print_final_stats, 0, "If 1, print statistics at exit.") +FUZZER_FLAG_INT(print_corpus_stats, 0, + "If 1, print statistics on corpus elements at exit.") +FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text" + " at exit.") +FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated.") +FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.") +FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.") +FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.") +FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.") +FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.") +FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.") +FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.") +FUZZER_FLAG_INT(handle_xfsz, 1, "If 1, try to intercept SIGXFSZ.") +FUZZER_FLAG_INT(handle_usr1, 1, "If 1, try to intercept SIGUSR1.") +FUZZER_FLAG_INT(handle_usr2, 1, "If 1, try to intercept SIGUSR2.") +FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; " + "if 2, close stderr; if 3, close both. " + "Be careful, this will also close e.g. stderr of asan.") +FUZZER_FLAG_INT(detect_leaks, 1, "If 1, and if LeakSanitizer is enabled " + "try to detect memory leaks during fuzzing (i.e. not only at shut down).") +FUZZER_FLAG_INT(purge_allocator_interval, 1, "Purge allocator caches and " + "quarantines every seconds. When rss_limit_mb is specified (>0), " + "purging starts when RSS exceeds 50% of rss_limit_mb. Pass " + "purge_allocator_interval=-1 to disable this functionality.") +FUZZER_FLAG_INT(trace_malloc, 0, "If >= 1 will print all mallocs/frees. " + "If >= 2 will also print stack traces.") +FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon" + "reaching this limit of RSS memory usage.") +FUZZER_FLAG_INT(malloc_limit_mb, 0, "If non-zero, the fuzzer will exit " + "if the target tries to allocate this number of Mb with one malloc call. " + "If zero (default) same limit as rss_limit_mb is applied.") +FUZZER_FLAG_STRING(exit_on_src_pos, "Exit if a newly found PC originates" + " from the given source location. Example: -exit_on_src_pos=foo.cc:123. " + "Used primarily for testing libFuzzer itself.") +FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum" + " was added to the corpus. " + "Used primarily for testing libFuzzer itself.") +FUZZER_FLAG_INT(ignore_remaining_args, 0, "If 1, ignore all arguments passed " + "after this one. Useful for fuzzers that need to do their own " + "argument parsing.") +FUZZER_FLAG_STRING(focus_function, "Experimental. " + "Fuzzing will focus on inputs that trigger calls to this function. " + "If -focus_function=auto and -data_flow_trace is used, libFuzzer " + "will choose the focus functions automatically.") +FUZZER_FLAG_INT(entropic, 0, "Experimental. Enables entropic power schedule.") +FUZZER_FLAG_INT(entropic_feature_frequency_threshold, 0xFF, "Experimental. If " + "entropic is enabled, all features which are observed less often than " + "the specified value are considered as rare.") +FUZZER_FLAG_INT(entropic_number_of_rarest_features, 100, "Experimental. If " + "entropic is enabled, we keep track of the frequencies only for the " + "Top-X least abundant features (union features that are considered as " + "rare).") +FUZZER_FLAG_INT(entropic_scale_per_exec_time, 0, "Experimental. If 1, " + "the Entropic power schedule gets scaled based on the input execution " + "time. Inputs with lower execution time get scheduled more (up to 30x). " + "Note that, if 1, fuzzer stops from being deterministic even if a " + "non-zero random seed is given.") + +FUZZER_FLAG_INT(analyze_dict, 0, "Experimental") +FUZZER_DEPRECATED_FLAG(use_clang_coverage) +FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace") +FUZZER_FLAG_STRING(collect_data_flow, + "Experimental: collect the data flow trace") + +FUZZER_FLAG_INT(create_missing_dirs, 0, "Automatically attempt to create " + "directories for arguments that would normally expect them to already " + "exist (i.e. artifact_prefix, exact_artifact_path, features_dir, corpus)") diff --git a/custom_mutators/libfuzzer/FuzzerFork.cpp b/custom_mutators/libfuzzer/FuzzerFork.cpp new file mode 100644 index 00000000..d6ffed74 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerFork.cpp @@ -0,0 +1,501 @@ +//===- FuzzerFork.cpp - run fuzzing in separate subprocesses --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Spawn and orchestrate separate fuzzing processes. +//===----------------------------------------------------------------------===// + +#include "FuzzerCommand.h" +#include "FuzzerFork.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include "FuzzerMerge.h" +#include "FuzzerSHA1.h" +#include "FuzzerTracePC.h" +#include "FuzzerUtil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +struct Stats { + + size_t number_of_executed_units = 0; + size_t peak_rss_mb = 0; + size_t average_exec_per_sec = 0; + +}; + +static Stats ParseFinalStatsFromLog(const std::string &LogPath) { + + std::ifstream In(LogPath); + std::string Line; + Stats Res; + struct { + + const char *Name; + size_t * Var; + + } NameVarPairs[] = { + + {"stat::number_of_executed_units:", &Res.number_of_executed_units}, + {"stat::peak_rss_mb:", &Res.peak_rss_mb}, + {"stat::average_exec_per_sec:", &Res.average_exec_per_sec}, + {nullptr, nullptr}, + + }; + + while (std::getline(In, Line, '\n')) { + + if (Line.find("stat::") != 0) continue; + std::istringstream ISS(Line); + std::string Name; + size_t Val; + ISS >> Name >> Val; + for (size_t i = 0; NameVarPairs[i].Name; i++) + if (Name == NameVarPairs[i].Name) *NameVarPairs[i].Var = Val; + + } + + return Res; + +} + +struct FuzzJob { + + // Inputs. + Command Cmd; + std::string CorpusDir; + std::string FeaturesDir; + std::string LogPath; + std::string SeedListPath; + std::string CFPath; + size_t JobId; + + int DftTimeInSeconds = 0; + + // Fuzzing Outputs. + int ExitCode; + + ~FuzzJob() { + + RemoveFile(CFPath); + RemoveFile(LogPath); + RemoveFile(SeedListPath); + RmDirRecursive(CorpusDir); + RmDirRecursive(FeaturesDir); + + } + +}; + +struct GlobalEnv { + + Vector Args; + Vector CorpusDirs; + std::string MainCorpusDir; + std::string TempDir; + std::string DFTDir; + std::string DataFlowBinary; + Set Features, Cov; + Set FilesWithDFT; + Vector Files; + Random * Rand; + std::chrono::system_clock::time_point ProcessStartTime; + int Verbosity = 0; + + size_t NumTimeouts = 0; + size_t NumOOMs = 0; + size_t NumCrashes = 0; + + size_t NumRuns = 0; + + std::string StopFile() { + + return DirPlusFile(TempDir, "STOP"); + + } + + size_t secondsSinceProcessStartUp() const { + + return std::chrono::duration_cast( + std::chrono::system_clock::now() - ProcessStartTime) + .count(); + + } + + FuzzJob *CreateNewJob(size_t JobId) { + + Command Cmd(Args); + Cmd.removeFlag("fork"); + Cmd.removeFlag("runs"); + Cmd.removeFlag("collect_data_flow"); + for (auto &C : CorpusDirs) // Remove all corpora from the args. + Cmd.removeArgument(C); + Cmd.addFlag("reload", "0"); // working in an isolated dir, no reload. + Cmd.addFlag("print_final_stats", "1"); + Cmd.addFlag("print_funcs", "0"); // no need to spend time symbolizing. + Cmd.addFlag("max_total_time", std::to_string(std::min((size_t)300, JobId))); + Cmd.addFlag("stop_file", StopFile()); + if (!DataFlowBinary.empty()) { + + Cmd.addFlag("data_flow_trace", DFTDir); + if (!Cmd.hasFlag("focus_function")) Cmd.addFlag("focus_function", "auto"); + + } + + auto Job = new FuzzJob; + std::string Seeds; + if (size_t CorpusSubsetSize = + std::min(Files.size(), (size_t)sqrt(Files.size() + 2))) { + + auto Time1 = std::chrono::system_clock::now(); + for (size_t i = 0; i < CorpusSubsetSize; i++) { + + auto &SF = Files[Rand->SkewTowardsLast(Files.size())]; + Seeds += (Seeds.empty() ? "" : ",") + SF; + CollectDFT(SF); + + } + + auto Time2 = std::chrono::system_clock::now(); + Job->DftTimeInSeconds = duration_cast(Time2 - Time1).count(); + + } + + if (!Seeds.empty()) { + + Job->SeedListPath = + DirPlusFile(TempDir, std::to_string(JobId) + ".seeds"); + WriteToFile(Seeds, Job->SeedListPath); + Cmd.addFlag("seed_inputs", "@" + Job->SeedListPath); + + } + + Job->LogPath = DirPlusFile(TempDir, std::to_string(JobId) + ".log"); + Job->CorpusDir = DirPlusFile(TempDir, "C" + std::to_string(JobId)); + Job->FeaturesDir = DirPlusFile(TempDir, "F" + std::to_string(JobId)); + Job->CFPath = DirPlusFile(TempDir, std::to_string(JobId) + ".merge"); + Job->JobId = JobId; + + Cmd.addArgument(Job->CorpusDir); + Cmd.addFlag("features_dir", Job->FeaturesDir); + + for (auto &D : {Job->CorpusDir, Job->FeaturesDir}) { + + RmDirRecursive(D); + MkDir(D); + + } + + Cmd.setOutputFile(Job->LogPath); + Cmd.combineOutAndErr(); + + Job->Cmd = Cmd; + + if (Verbosity >= 2) + Printf("Job %zd/%p Created: %s\n", JobId, Job, + Job->Cmd.toString().c_str()); + // Start from very short runs and gradually increase them. + return Job; + + } + + void RunOneMergeJob(FuzzJob *Job) { + + auto Stats = ParseFinalStatsFromLog(Job->LogPath); + NumRuns += Stats.number_of_executed_units; + + Vector TempFiles, MergeCandidates; + // Read all newly created inputs and their feature sets. + // Choose only those inputs that have new features. + GetSizedFilesFromDir(Job->CorpusDir, &TempFiles); + std::sort(TempFiles.begin(), TempFiles.end()); + for (auto &F : TempFiles) { + + auto FeatureFile = F.File; + FeatureFile.replace(0, Job->CorpusDir.size(), Job->FeaturesDir); + auto FeatureBytes = FileToVector(FeatureFile, 0, false); + assert((FeatureBytes.size() % sizeof(uint32_t)) == 0); + Vector NewFeatures(FeatureBytes.size() / sizeof(uint32_t)); + memcpy(NewFeatures.data(), FeatureBytes.data(), FeatureBytes.size()); + for (auto Ft : NewFeatures) { + + if (!Features.count(Ft)) { + + MergeCandidates.push_back(F); + break; + + } + + } + + } + + // if (!FilesToAdd.empty() || Job->ExitCode != 0) + Printf( + "#%zd: cov: %zd ft: %zd corp: %zd exec/s %zd " + "oom/timeout/crash: %zd/%zd/%zd time: %zds job: %zd dft_time: %d\n", + NumRuns, Cov.size(), Features.size(), Files.size(), + Stats.average_exec_per_sec, NumOOMs, NumTimeouts, NumCrashes, + secondsSinceProcessStartUp(), Job->JobId, Job->DftTimeInSeconds); + + if (MergeCandidates.empty()) return; + + Vector FilesToAdd; + Set NewFeatures, NewCov; + CrashResistantMerge(Args, {}, MergeCandidates, &FilesToAdd, Features, + &NewFeatures, Cov, &NewCov, Job->CFPath, false); + for (auto &Path : FilesToAdd) { + + auto U = FileToVector(Path); + auto NewPath = DirPlusFile(MainCorpusDir, Hash(U)); + WriteToFile(U, NewPath); + Files.push_back(NewPath); + + } + + Features.insert(NewFeatures.begin(), NewFeatures.end()); + Cov.insert(NewCov.begin(), NewCov.end()); + for (auto Idx : NewCov) + if (auto *TE = TPC.PCTableEntryByIdx(Idx)) + if (TPC.PcIsFuncEntry(TE)) + PrintPC(" NEW_FUNC: %p %F %L\n", "", + TPC.GetNextInstructionPc(TE->PC)); + + } + + void CollectDFT(const std::string &InputPath) { + + if (DataFlowBinary.empty()) return; + if (!FilesWithDFT.insert(InputPath).second) return; + Command Cmd(Args); + Cmd.removeFlag("fork"); + Cmd.removeFlag("runs"); + Cmd.addFlag("data_flow_trace", DFTDir); + Cmd.addArgument(InputPath); + for (auto &C : CorpusDirs) // Remove all corpora from the args. + Cmd.removeArgument(C); + Cmd.setOutputFile(DirPlusFile(TempDir, "dft.log")); + Cmd.combineOutAndErr(); + // Printf("CollectDFT: %s\n", Cmd.toString().c_str()); + ExecuteCommand(Cmd); + + } + +}; + +struct JobQueue { + + std::queue Qu; + std::mutex Mu; + std::condition_variable Cv; + + void Push(FuzzJob *Job) { + + { + + std::lock_guard Lock(Mu); + Qu.push(Job); + + } + + Cv.notify_one(); + + } + + FuzzJob *Pop() { + + std::unique_lock Lk(Mu); + // std::lock_guard Lock(Mu); + Cv.wait(Lk, [&] { return !Qu.empty(); }); + assert(!Qu.empty()); + auto Job = Qu.front(); + Qu.pop(); + return Job; + + } + +}; + +void WorkerThread(JobQueue *FuzzQ, JobQueue *MergeQ) { + + while (auto Job = FuzzQ->Pop()) { + + // Printf("WorkerThread: job %p\n", Job); + Job->ExitCode = ExecuteCommand(Job->Cmd); + MergeQ->Push(Job); + + } + +} + +// This is just a skeleton of an experimental -fork=1 feature. +void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + const Vector &Args, + const Vector &CorpusDirs, int NumJobs) { + + Printf("INFO: -fork=%d: fuzzing in separate process(s)\n", NumJobs); + + GlobalEnv Env; + Env.Args = Args; + Env.CorpusDirs = CorpusDirs; + Env.Rand = &Rand; + Env.Verbosity = Options.Verbosity; + Env.ProcessStartTime = std::chrono::system_clock::now(); + Env.DataFlowBinary = Options.CollectDataFlow; + + Vector SeedFiles; + for (auto &Dir : CorpusDirs) + GetSizedFilesFromDir(Dir, &SeedFiles); + std::sort(SeedFiles.begin(), SeedFiles.end()); + Env.TempDir = TempPath("FuzzWithFork", ".dir"); + Env.DFTDir = DirPlusFile(Env.TempDir, "DFT"); + RmDirRecursive(Env.TempDir); // in case there is a leftover from old runs. + MkDir(Env.TempDir); + MkDir(Env.DFTDir); + + if (CorpusDirs.empty()) + MkDir(Env.MainCorpusDir = DirPlusFile(Env.TempDir, "C")); + else + Env.MainCorpusDir = CorpusDirs[0]; + + if (Options.KeepSeed) { + + for (auto &File : SeedFiles) + Env.Files.push_back(File.File); + + } else { + + auto CFPath = DirPlusFile(Env.TempDir, "merge.txt"); + CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features, + {}, &Env.Cov, CFPath, false); + RemoveFile(CFPath); + + } + + Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs, + Env.Files.size(), Env.TempDir.c_str()); + + int ExitCode = 0; + + JobQueue FuzzQ, MergeQ; + + auto StopJobs = [&]() { + + for (int i = 0; i < NumJobs; i++) + FuzzQ.Push(nullptr); + MergeQ.Push(nullptr); + WriteToFile(Unit({1}), Env.StopFile()); + + }; + + size_t JobId = 1; + Vector Threads; + for (int t = 0; t < NumJobs; t++) { + + Threads.push_back(std::thread(WorkerThread, &FuzzQ, &MergeQ)); + FuzzQ.Push(Env.CreateNewJob(JobId++)); + + } + + while (true) { + + std::unique_ptr Job(MergeQ.Pop()); + if (!Job) break; + ExitCode = Job->ExitCode; + if (ExitCode == Options.InterruptExitCode) { + + Printf("==%lu== libFuzzer: a child was interrupted; exiting\n", GetPid()); + StopJobs(); + break; + + } + + Fuzzer::MaybeExitGracefully(); + + Env.RunOneMergeJob(Job.get()); + + // Continue if our crash is one of the ignorred ones. + if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode) + Env.NumTimeouts++; + else if (Options.IgnoreOOMs && ExitCode == Options.OOMExitCode) + Env.NumOOMs++; + else if (ExitCode != 0) { + + Env.NumCrashes++; + if (Options.IgnoreCrashes) { + + std::ifstream In(Job->LogPath); + std::string Line; + while (std::getline(In, Line, '\n')) + if (Line.find("ERROR:") != Line.npos || + Line.find("runtime error:") != Line.npos) + Printf("%s\n", Line.c_str()); + + } else { + + // And exit if we don't ignore this crash. + Printf("INFO: log from the inner process:\n%s", + FileToString(Job->LogPath).c_str()); + StopJobs(); + break; + + } + + } + + // Stop if we are over the time budget. + // This is not precise, since other threads are still running + // and we will wait while joining them. + // We also don't stop instantly: other jobs need to finish. + if (Options.MaxTotalTimeSec > 0 && + Env.secondsSinceProcessStartUp() >= (size_t)Options.MaxTotalTimeSec) { + + Printf("INFO: fuzzed for %zd seconds, wrapping up soon\n", + Env.secondsSinceProcessStartUp()); + StopJobs(); + break; + + } + + if (Env.NumRuns >= Options.MaxNumberOfRuns) { + + Printf("INFO: fuzzed for %zd iterations, wrapping up soon\n", + Env.NumRuns); + StopJobs(); + break; + + } + + FuzzQ.Push(Env.CreateNewJob(JobId++)); + + } + + for (auto &T : Threads) + T.join(); + + // The workers have terminated. Don't try to remove the directory before they + // terminate to avoid a race condition preventing cleanup on Windows. + RmDirRecursive(Env.TempDir); + + // Use the exit code from the last child process. + Printf("INFO: exiting: %d time: %zds\n", ExitCode, + Env.secondsSinceProcessStartUp()); + exit(ExitCode); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerFork.h b/custom_mutators/libfuzzer/FuzzerFork.h new file mode 100644 index 00000000..b29a43e1 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerFork.h @@ -0,0 +1,24 @@ +//===- FuzzerFork.h - run fuzzing in sub-processes --------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_FORK_H +#define LLVM_FUZZER_FORK_H + +#include "FuzzerDefs.h" +#include "FuzzerOptions.h" +#include "FuzzerRandom.h" + +#include + +namespace fuzzer { +void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + const Vector &Args, + const Vector &CorpusDirs, int NumJobs); +} // namespace fuzzer + +#endif // LLVM_FUZZER_FORK_H diff --git a/custom_mutators/libfuzzer/FuzzerIO.cpp b/custom_mutators/libfuzzer/FuzzerIO.cpp new file mode 100644 index 00000000..e0c15db4 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerIO.cpp @@ -0,0 +1,248 @@ +//===- FuzzerIO.cpp - IO utils. -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// IO functions. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerUtil.h" +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static FILE *OutputFile = stderr; + +long GetEpoch(const std::string &Path) { + + struct stat St; + if (stat(Path.c_str(), &St)) return 0; // Can't stat, be conservative. + return St.st_mtime; + +} + +Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) { + + std::ifstream T(Path, std::ios::binary); + if (ExitOnError && !T) { + + Printf("No such directory: %s; exiting\n", Path.c_str()); + exit(1); + + } + + T.seekg(0, T.end); + auto EndPos = T.tellg(); + if (EndPos < 0) return {}; + size_t FileLen = EndPos; + if (MaxSize) FileLen = std::min(FileLen, MaxSize); + + T.seekg(0, T.beg); + Unit Res(FileLen); + T.read(reinterpret_cast(Res.data()), FileLen); + return Res; + +} + +std::string FileToString(const std::string &Path) { + + std::ifstream T(Path, std::ios::binary); + return std::string((std::istreambuf_iterator(T)), + std::istreambuf_iterator()); + +} + +void CopyFileToErr(const std::string &Path) { + + Printf("%s", FileToString(Path).c_str()); + +} + +void WriteToFile(const Unit &U, const std::string &Path) { + + WriteToFile(U.data(), U.size(), Path); + +} + +void WriteToFile(const std::string &Data, const std::string &Path) { + + WriteToFile(reinterpret_cast(Data.c_str()), Data.size(), + Path); + +} + +void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path) { + + // Use raw C interface because this function may be called from a sig handler. + FILE *Out = fopen(Path.c_str(), "wb"); + if (!Out) return; + fwrite(Data, sizeof(Data[0]), Size, Out); + fclose(Out); + +} + +void AppendToFile(const std::string &Data, const std::string &Path) { + + AppendToFile(reinterpret_cast(Data.data()), Data.size(), + Path); + +} + +void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path) { + + FILE *Out = fopen(Path.c_str(), "a"); + if (!Out) return; + fwrite(Data, sizeof(Data[0]), Size, Out); + fclose(Out); + +} + +void ReadDirToVectorOfUnits(const char *Path, Vector *V, long *Epoch, + size_t MaxSize, bool ExitOnError) { + + long E = Epoch ? *Epoch : 0; + Vector Files; + ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/ true); + size_t NumLoaded = 0; + for (size_t i = 0; i < Files.size(); i++) { + + auto &X = Files[i]; + if (Epoch && GetEpoch(X) < E) continue; + NumLoaded++; + if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024) + Printf("Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path); + auto S = FileToVector(X, MaxSize, ExitOnError); + if (!S.empty()) V->push_back(S); + + } + +} + +void GetSizedFilesFromDir(const std::string &Dir, Vector *V) { + + Vector Files; + ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/ true); + for (auto &File : Files) + if (size_t Size = FileSize(File)) V->push_back({File, Size}); + +} + +std::string DirPlusFile(const std::string &DirPath, + const std::string &FileName) { + + return DirPath + GetSeparator() + FileName; + +} + +void DupAndCloseStderr() { + + int OutputFd = DuplicateFile(2); + if (OutputFd >= 0) { + + FILE *NewOutputFile = OpenFile(OutputFd, "w"); + if (NewOutputFile) { + + OutputFile = NewOutputFile; + if (EF->__sanitizer_set_report_fd) + EF->__sanitizer_set_report_fd( + reinterpret_cast(GetHandleFromFd(OutputFd))); + DiscardOutput(2); + + } + + } + +} + +void CloseStdout() { + + DiscardOutput(1); + +} + +void Printf(const char *Fmt, ...) { + + va_list ap; + va_start(ap, Fmt); + vfprintf(OutputFile, Fmt, ap); + va_end(ap); + fflush(OutputFile); + +} + +void VPrintf(bool Verbose, const char *Fmt, ...) { + + if (!Verbose) return; + va_list ap; + va_start(ap, Fmt); + vfprintf(OutputFile, Fmt, ap); + va_end(ap); + fflush(OutputFile); + +} + +static bool MkDirRecursiveInner(const std::string &Leaf) { + + // Prevent chance of potential infinite recursion + if (Leaf == ".") return true; + + const std::string &Dir = DirName(Leaf); + + if (IsDirectory(Dir)) { + + MkDir(Leaf); + return IsDirectory(Leaf); + + } + + bool ret = MkDirRecursiveInner(Dir); + if (!ret) { + + // Give up early if a previous MkDir failed + return ret; + + } + + MkDir(Leaf); + return IsDirectory(Leaf); + +} + +bool MkDirRecursive(const std::string &Dir) { + + if (Dir.empty()) return false; + + if (IsDirectory(Dir)) return true; + + return MkDirRecursiveInner(Dir); + +} + +void RmDirRecursive(const std::string &Dir) { + + IterateDirRecursive( + Dir, [](const std::string &Path) {}, + [](const std::string &Path) { RmDir(Path); }, + [](const std::string &Path) { RemoveFile(Path); }); + +} + +std::string TempPath(const char *Prefix, const char *Extension) { + + return DirPlusFile(TmpDir(), std::string("libFuzzerTemp.") + Prefix + + std::to_string(GetPid()) + Extension); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerIO.h b/custom_mutators/libfuzzer/FuzzerIO.h new file mode 100644 index 00000000..abd25110 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerIO.h @@ -0,0 +1,112 @@ +//===- FuzzerIO.h - Internal header for IO utils ----------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// IO interface. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_IO_H +#define LLVM_FUZZER_IO_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +long GetEpoch(const std::string &Path); + +Unit FileToVector(const std::string &Path, size_t MaxSize = 0, + bool ExitOnError = true); + +std::string FileToString(const std::string &Path); + +void CopyFileToErr(const std::string &Path); + +void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path); +// Write Data.c_str() to the file without terminating null character. +void WriteToFile(const std::string &Data, const std::string &Path); +void WriteToFile(const Unit &U, const std::string &Path); + +void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path); +void AppendToFile(const std::string &Data, const std::string &Path); + +void ReadDirToVectorOfUnits(const char *Path, Vector *V, + long *Epoch, size_t MaxSize, bool ExitOnError); + +// Returns "Dir/FileName" or equivalent for the current OS. +std::string DirPlusFile(const std::string &DirPath, + const std::string &FileName); + +// Returns the name of the dir, similar to the 'dirname' utility. +std::string DirName(const std::string &FileName); + +// Returns path to a TmpDir. +std::string TmpDir(); + +std::string TempPath(const char *Prefix, const char *Extension); + +bool IsInterestingCoverageFile(const std::string &FileName); + +void DupAndCloseStderr(); + +void CloseStdout(); + +void Printf(const char *Fmt, ...); +void VPrintf(bool Verbose, const char *Fmt, ...); + +// Print using raw syscalls, useful when printing at early init stages. +void RawPrint(const char *Str); + +// Platform specific functions: +bool IsFile(const std::string &Path); +bool IsDirectory(const std::string &Path); +size_t FileSize(const std::string &Path); + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + Vector *V, bool TopDir); + +bool MkDirRecursive(const std::string &Dir); +void RmDirRecursive(const std::string &Dir); + +// Iterate files and dirs inside Dir, recursively. +// Call DirPreCallback/DirPostCallback on dirs before/after +// calling FileCallback on files. +void IterateDirRecursive(const std::string &Dir, + void (*DirPreCallback)(const std::string &Dir), + void (*DirPostCallback)(const std::string &Dir), + void (*FileCallback)(const std::string &Dir)); + +struct SizedFile { + std::string File; + size_t Size; + bool operator<(const SizedFile &B) const { return Size < B.Size; } +}; + +void GetSizedFilesFromDir(const std::string &Dir, Vector *V); + +char GetSeparator(); +bool IsSeparator(char C); +// Similar to the basename utility: returns the file name w/o the dir prefix. +std::string Basename(const std::string &Path); + +FILE* OpenFile(int Fd, const char *Mode); + +int CloseFile(int Fd); + +int DuplicateFile(int Fd); + +void RemoveFile(const std::string &Path); +void RenameFile(const std::string &OldPath, const std::string &NewPath); + +intptr_t GetHandleFromFd(int fd); + +void MkDir(const std::string &Path); +void RmDir(const std::string &Path); + +const std::string &getDevNull(); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_IO_H diff --git a/custom_mutators/libfuzzer/FuzzerIOPosix.cpp b/custom_mutators/libfuzzer/FuzzerIOPosix.cpp new file mode 100644 index 00000000..36ec5a9c --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerIOPosix.cpp @@ -0,0 +1,223 @@ +//===- FuzzerIOPosix.cpp - IO utils for Posix. ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// IO functions implementation using Posix API. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_POSIX || LIBFUZZER_FUCHSIA + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + #include + #include + #include + #include + #include + #include + #include + #include + #include + +namespace fuzzer { + +bool IsFile(const std::string &Path) { + + struct stat St; + if (stat(Path.c_str(), &St)) return false; + return S_ISREG(St.st_mode); + +} + +bool IsDirectory(const std::string &Path) { + + struct stat St; + if (stat(Path.c_str(), &St)) return false; + return S_ISDIR(St.st_mode); + +} + +size_t FileSize(const std::string &Path) { + + struct stat St; + if (stat(Path.c_str(), &St)) return 0; + return St.st_size; + +} + +std::string Basename(const std::string &Path) { + + size_t Pos = Path.rfind(GetSeparator()); + if (Pos == std::string::npos) return Path; + assert(Pos < Path.size()); + return Path.substr(Pos + 1); + +} + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + Vector *V, bool TopDir) { + + auto E = GetEpoch(Dir); + if (Epoch) + if (E && *Epoch >= E) return; + + DIR *D = opendir(Dir.c_str()); + if (!D) { + + Printf("%s: %s; exiting\n", strerror(errno), Dir.c_str()); + exit(1); + + } + + while (auto E = readdir(D)) { + + std::string Path = DirPlusFile(Dir, E->d_name); + if (E->d_type == DT_REG || E->d_type == DT_LNK || + (E->d_type == DT_UNKNOWN && IsFile(Path))) + V->push_back(Path); + else if ((E->d_type == DT_DIR || + (E->d_type == DT_UNKNOWN && IsDirectory(Path))) && + *E->d_name != '.') + ListFilesInDirRecursive(Path, Epoch, V, false); + + } + + closedir(D); + if (Epoch && TopDir) *Epoch = E; + +} + +void IterateDirRecursive(const std::string &Dir, + void (*DirPreCallback)(const std::string &Dir), + void (*DirPostCallback)(const std::string &Dir), + void (*FileCallback)(const std::string &Dir)) { + + DirPreCallback(Dir); + DIR *D = opendir(Dir.c_str()); + if (!D) return; + while (auto E = readdir(D)) { + + std::string Path = DirPlusFile(Dir, E->d_name); + if (E->d_type == DT_REG || E->d_type == DT_LNK || + (E->d_type == DT_UNKNOWN && IsFile(Path))) + FileCallback(Path); + else if ((E->d_type == DT_DIR || + (E->d_type == DT_UNKNOWN && IsDirectory(Path))) && + *E->d_name != '.') + IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback); + + } + + closedir(D); + DirPostCallback(Dir); + +} + +char GetSeparator() { + + return '/'; + +} + +bool IsSeparator(char C) { + + return C == '/'; + +} + +FILE *OpenFile(int Fd, const char *Mode) { + + return fdopen(Fd, Mode); + +} + +int CloseFile(int fd) { + + return close(fd); + +} + +int DuplicateFile(int Fd) { + + return dup(Fd); + +} + +void RemoveFile(const std::string &Path) { + + unlink(Path.c_str()); + +} + +void RenameFile(const std::string &OldPath, const std::string &NewPath) { + + rename(OldPath.c_str(), NewPath.c_str()); + +} + +intptr_t GetHandleFromFd(int fd) { + + return static_cast(fd); + +} + +std::string DirName(const std::string &FileName) { + + char *Tmp = new char[FileName.size() + 1]; + memcpy(Tmp, FileName.c_str(), FileName.size() + 1); + std::string Res = dirname(Tmp); + delete[] Tmp; + return Res; + +} + +std::string TmpDir() { + + if (auto Env = getenv("TMPDIR")) return Env; + return "/tmp"; + +} + +bool IsInterestingCoverageFile(const std::string &FileName) { + + if (FileName.find("compiler-rt/lib/") != std::string::npos) + return false; // sanitizer internal. + if (FileName.find("/usr/lib/") != std::string::npos) return false; + if (FileName.find("/usr/include/") != std::string::npos) return false; + if (FileName == "") return false; + return true; + +} + +void RawPrint(const char *Str) { + + write(2, Str, strlen(Str)); + +} + +void MkDir(const std::string &Path) { + + mkdir(Path.c_str(), 0700); + +} + +void RmDir(const std::string &Path) { + + rmdir(Path.c_str()); + +} + +const std::string &getDevNull() { + + static const std::string devNull = "/dev/null"; + return devNull; + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_POSIX + diff --git a/custom_mutators/libfuzzer/FuzzerIOWindows.cpp b/custom_mutators/libfuzzer/FuzzerIOWindows.cpp new file mode 100644 index 00000000..9352984a --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerIOWindows.cpp @@ -0,0 +1,513 @@ +//===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// IO functions implementation for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_WINDOWS + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + #include + #include + #include + #include + #include + #include + #include + #include + +namespace fuzzer { + +static bool IsFile(const std::string &Path, const DWORD &FileAttributes) { + + if (FileAttributes & FILE_ATTRIBUTE_NORMAL) return true; + + if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) return false; + + HANDLE FileHandle(CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); + + if (FileHandle == INVALID_HANDLE_VALUE) { + + Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(), + GetLastError()); + return false; + + } + + DWORD FileType = GetFileType(FileHandle); + + if (FileType == FILE_TYPE_UNKNOWN) { + + Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(), + GetLastError()); + CloseHandle(FileHandle); + return false; + + } + + if (FileType != FILE_TYPE_DISK) { + + CloseHandle(FileHandle); + return false; + + } + + CloseHandle(FileHandle); + return true; + +} + +bool IsFile(const std::string &Path) { + + DWORD Att = GetFileAttributesA(Path.c_str()); + + if (Att == INVALID_FILE_ATTRIBUTES) { + + Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), GetLastError()); + return false; + + } + + return IsFile(Path, Att); + +} + +static bool IsDir(DWORD FileAttrs) { + + if (FileAttrs == INVALID_FILE_ATTRIBUTES) return false; + return FileAttrs & FILE_ATTRIBUTE_DIRECTORY; + +} + +bool IsDirectory(const std::string &Path) { + + DWORD Att = GetFileAttributesA(Path.c_str()); + + if (Att == INVALID_FILE_ATTRIBUTES) { + + Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), GetLastError()); + return false; + + } + + return IsDir(Att); + +} + +std::string Basename(const std::string &Path) { + + size_t Pos = Path.find_last_of("/\\"); + if (Pos == std::string::npos) return Path; + assert(Pos < Path.size()); + return Path.substr(Pos + 1); + +} + +size_t FileSize(const std::string &Path) { + + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) { + + DWORD LastError = GetLastError(); + if (LastError != ERROR_FILE_NOT_FOUND) + Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), LastError); + return 0; + + } + + ULARGE_INTEGER size; + size.HighPart = attr.nFileSizeHigh; + size.LowPart = attr.nFileSizeLow; + return size.QuadPart; + +} + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + Vector *V, bool TopDir) { + + auto E = GetEpoch(Dir); + if (Epoch) + if (E && *Epoch >= E) return; + + std::string Path(Dir); + assert(!Path.empty()); + if (Path.back() != '\\') Path.push_back('\\'); + Path.push_back('*'); + + // Get the first directory entry. + WIN32_FIND_DATAA FindInfo; + HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo)); + if (FindHandle == INVALID_HANDLE_VALUE) { + + if (GetLastError() == ERROR_FILE_NOT_FOUND) return; + Printf("No such file or directory: %s; exiting\n", Dir.c_str()); + exit(1); + + } + + do { + + std::string FileName = DirPlusFile(Dir, FindInfo.cFileName); + + if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + + size_t FilenameLen = strlen(FindInfo.cFileName); + if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') || + (FilenameLen == 2 && FindInfo.cFileName[0] == '.' && + FindInfo.cFileName[1] == '.')) + continue; + + ListFilesInDirRecursive(FileName, Epoch, V, false); + + } else if (IsFile(FileName, FindInfo.dwFileAttributes)) + + V->push_back(FileName); + + } while (FindNextFileA(FindHandle, &FindInfo)); + + DWORD LastError = GetLastError(); + if (LastError != ERROR_NO_MORE_FILES) + Printf("FindNextFileA failed (Error code: %lu).\n", LastError); + + FindClose(FindHandle); + + if (Epoch && TopDir) *Epoch = E; + +} + +void IterateDirRecursive(const std::string &Dir, + void (*DirPreCallback)(const std::string &Dir), + void (*DirPostCallback)(const std::string &Dir), + void (*FileCallback)(const std::string &Dir)) { + + // TODO(metzman): Implement ListFilesInDirRecursive via this function. + DirPreCallback(Dir); + + DWORD DirAttrs = GetFileAttributesA(Dir.c_str()); + if (!IsDir(DirAttrs)) return; + + std::string TargetDir(Dir); + assert(!TargetDir.empty()); + if (TargetDir.back() != '\\') TargetDir.push_back('\\'); + TargetDir.push_back('*'); + + WIN32_FIND_DATAA FindInfo; + // Find the directory's first file. + HANDLE FindHandle = FindFirstFileA(TargetDir.c_str(), &FindInfo); + if (FindHandle == INVALID_HANDLE_VALUE) { + + DWORD LastError = GetLastError(); + if (LastError != ERROR_FILE_NOT_FOUND) { + + // If the directory isn't empty, then something abnormal is going on. + Printf("FindFirstFileA failed for %s (Error code: %lu).\n", Dir.c_str(), + LastError); + + } + + return; + + } + + do { + + std::string Path = DirPlusFile(Dir, FindInfo.cFileName); + DWORD PathAttrs = FindInfo.dwFileAttributes; + if (IsDir(PathAttrs)) { + + // Is Path the current directory (".") or the parent ("..")? + if (strcmp(FindInfo.cFileName, ".") == 0 || + strcmp(FindInfo.cFileName, "..") == 0) + continue; + IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback); + + } else if (PathAttrs != INVALID_FILE_ATTRIBUTES) { + + FileCallback(Path); + + } + + } while (FindNextFileA(FindHandle, &FindInfo)); + + DWORD LastError = GetLastError(); + if (LastError != ERROR_NO_MORE_FILES) + Printf("FindNextFileA failed for %s (Error code: %lu).\n", Dir.c_str(), + LastError); + + FindClose(FindHandle); + DirPostCallback(Dir); + +} + +char GetSeparator() { + + return '\\'; + +} + +FILE *OpenFile(int Fd, const char *Mode) { + + return _fdopen(Fd, Mode); + +} + +int CloseFile(int Fd) { + + return _close(Fd); + +} + +int DuplicateFile(int Fd) { + + return _dup(Fd); + +} + +void RemoveFile(const std::string &Path) { + + _unlink(Path.c_str()); + +} + +void RenameFile(const std::string &OldPath, const std::string &NewPath) { + + rename(OldPath.c_str(), NewPath.c_str()); + +} + +intptr_t GetHandleFromFd(int fd) { + + return _get_osfhandle(fd); + +} + +bool IsSeparator(char C) { + + return C == '\\' || C == '/'; + +} + +// Parse disk designators, like "C:\". If Relative == true, also accepts: "C:". +// Returns number of characters considered if successful. +static size_t ParseDrive(const std::string &FileName, const size_t Offset, + bool Relative = true) { + + if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':') return 0; + if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) { + + if (!Relative) // Accept relative path? + return 0; + else + return 2; + + } + + return 3; + +} + +// Parse a file name, like: SomeFile.txt +// Returns number of characters considered if successful. +static size_t ParseFileName(const std::string &FileName, const size_t Offset) { + + size_t Pos = Offset; + const size_t End = FileName.size(); + for (; Pos < End && !IsSeparator(FileName[Pos]); ++Pos) + ; + return Pos - Offset; + +} + +// Parse a directory ending in separator, like: `SomeDir\` +// Returns number of characters considered if successful. +static size_t ParseDir(const std::string &FileName, const size_t Offset) { + + size_t Pos = Offset; + const size_t End = FileName.size(); + if (Pos >= End || IsSeparator(FileName[Pos])) return 0; + for (; Pos < End && !IsSeparator(FileName[Pos]); ++Pos) + ; + if (Pos >= End) return 0; + ++Pos; // Include separator. + return Pos - Offset; + +} + +// Parse a servername and share, like: `SomeServer\SomeShare\` +// Returns number of characters considered if successful. +static size_t ParseServerAndShare(const std::string &FileName, + const size_t Offset) { + + size_t Pos = Offset, Res; + if (!(Res = ParseDir(FileName, Pos))) return 0; + Pos += Res; + if (!(Res = ParseDir(FileName, Pos))) return 0; + Pos += Res; + return Pos - Offset; + +} + +// Parse the given Ref string from the position Offset, to exactly match the +// given string Patt. Returns number of characters considered if successful. +static size_t ParseCustomString(const std::string &Ref, size_t Offset, + const char *Patt) { + + size_t Len = strlen(Patt); + if (Offset + Len > Ref.size()) return 0; + return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0; + +} + +// Parse a location, like: +// \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C: +// Returns number of characters considered if successful. +static size_t ParseLocation(const std::string &FileName) { + + size_t Pos = 0, Res; + + if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) { + + Pos += Res; + if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) { + + Pos += Res; + if ((Res = ParseServerAndShare(FileName, Pos))) return Pos + Res; + return 0; + + } + + if ((Res = ParseDrive(FileName, Pos, false))) return Pos + Res; + return 0; + + } + + if (Pos < FileName.size() && IsSeparator(FileName[Pos])) { + + ++Pos; + if (Pos < FileName.size() && IsSeparator(FileName[Pos])) { + + ++Pos; + if ((Res = ParseServerAndShare(FileName, Pos))) return Pos + Res; + return 0; + + } + + return Pos; + + } + + if ((Res = ParseDrive(FileName, Pos))) return Pos + Res; + + return Pos; + +} + +std::string DirName(const std::string &FileName) { + + size_t LocationLen = ParseLocation(FileName); + size_t DirLen = 0, Res; + while ((Res = ParseDir(FileName, LocationLen + DirLen))) + DirLen += Res; + size_t FileLen = ParseFileName(FileName, LocationLen + DirLen); + + if (LocationLen + DirLen + FileLen != FileName.size()) { + + Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str()); + exit(1); + + } + + if (DirLen) { + + --DirLen; // Remove trailing separator. + if (!FileLen) { // Path ended in separator. + assert(DirLen); + // Remove file name from Dir. + while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1])) + --DirLen; + if (DirLen) // Remove trailing separator. + --DirLen; + + } + + } + + if (!LocationLen) { // Relative path. + if (!DirLen) return "."; + return std::string(".\\").append(FileName, 0, DirLen); + + } + + return FileName.substr(0, LocationLen + DirLen); + +} + +std::string TmpDir() { + + std::string Tmp; + Tmp.resize(MAX_PATH + 1); + DWORD Size = GetTempPathA(Tmp.size(), &Tmp[0]); + if (Size == 0) { + + Printf("Couldn't get Tmp path.\n"); + exit(1); + + } + + Tmp.resize(Size); + return Tmp; + +} + +bool IsInterestingCoverageFile(const std::string &FileName) { + + if (FileName.find("Program Files") != std::string::npos) return false; + if (FileName.find("compiler-rt\\lib\\") != std::string::npos) + return false; // sanitizer internal. + if (FileName == "") return false; + return true; + +} + +void RawPrint(const char *Str) { + + _write(2, Str, strlen(Str)); + +} + +void MkDir(const std::string &Path) { + + if (CreateDirectoryA(Path.c_str(), nullptr)) return; + Printf("CreateDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(), + GetLastError()); + +} + +void RmDir(const std::string &Path) { + + if (RemoveDirectoryA(Path.c_str())) return; + Printf("RemoveDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(), + GetLastError()); + +} + +const std::string &getDevNull() { + + static const std::string devNull = "NUL"; + return devNull; + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS + diff --git a/custom_mutators/libfuzzer/FuzzerInterface.h b/custom_mutators/libfuzzer/FuzzerInterface.h new file mode 100644 index 00000000..4f62822e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerInterface.h @@ -0,0 +1,79 @@ +//===- FuzzerInterface.h - Interface header for the Fuzzer ------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Define the interface between libFuzzer and the library being tested. +//===----------------------------------------------------------------------===// + +// NOTE: the libFuzzer interface is thin and in the majority of cases +// you should not include this file into your target. In 95% of cases +// all you need is to define the following function in your file: +// extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +// WARNING: keep the interface in C. + +#ifndef LLVM_FUZZER_INTERFACE_H +#define LLVM_FUZZER_INTERFACE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Define FUZZER_INTERFACE_VISIBILITY to set default visibility in a way that +// doesn't break MSVC. +#if defined(_WIN32) +#define FUZZER_INTERFACE_VISIBILITY __declspec(dllexport) +#else +#define FUZZER_INTERFACE_VISIBILITY __attribute__((visibility("default"))) +#endif + +// Mandatory user-provided target function. +// Executes the code under test with [Data, Data+Size) as the input. +// libFuzzer will invoke this function *many* times with different inputs. +// Must return 0. +FUZZER_INTERFACE_VISIBILITY int +LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +// Optional user-provided initialization function. +// If provided, this function will be called by libFuzzer once at startup. +// It may read and modify argc/argv. +// Must return 0. +FUZZER_INTERFACE_VISIBILITY int LLVMFuzzerInitialize(int *argc, char ***argv); + +// Optional user-provided custom mutator. +// Mutates raw data in [Data, Data+Size) inplace. +// Returns the new size, which is not greater than MaxSize. +// Given the same Seed produces the same mutation. +FUZZER_INTERFACE_VISIBILITY size_t +LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, + unsigned int Seed); + +// Optional user-provided custom cross-over function. +// Combines pieces of Data1 & Data2 together into Out. +// Returns the new size, which is not greater than MaxOutSize. +// Should produce the same mutation given the same Seed. +FUZZER_INTERFACE_VISIBILITY size_t +LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, uint8_t *Out, + size_t MaxOutSize, unsigned int Seed); + +// Experimental, may go away in future. +// libFuzzer-provided function to be used inside LLVMFuzzerCustomMutator. +// Mutates raw data in [Data, Data+Size) inplace. +// Returns the new size, which is not greater than MaxSize. +FUZZER_INTERFACE_VISIBILITY size_t +LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); + +#undef FUZZER_INTERFACE_VISIBILITY + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // LLVM_FUZZER_INTERFACE_H diff --git a/custom_mutators/libfuzzer/FuzzerInternal.h b/custom_mutators/libfuzzer/FuzzerInternal.h new file mode 100644 index 00000000..2b172d91 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerInternal.h @@ -0,0 +1,173 @@ +//===- FuzzerInternal.h - Internal header for the Fuzzer --------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Define the main class fuzzer::Fuzzer and most functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_INTERNAL_H +#define LLVM_FUZZER_INTERNAL_H + +#include "FuzzerDataFlowTrace.h" +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerInterface.h" +#include "FuzzerOptions.h" +#include "FuzzerSHA1.h" +#include "FuzzerValueBitMap.h" +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +using namespace std::chrono; + +class Fuzzer { +public: + + Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, + FuzzingOptions Options); + ~Fuzzer(); + void Loop(Vector &CorporaFiles); + void ReadAndExecuteSeedCorpora(Vector &CorporaFiles); + void MinimizeCrashLoop(const Unit &U); + void RereadOutputCorpus(size_t MaxSize); + + size_t secondsSinceProcessStartUp() { + return duration_cast(system_clock::now() - ProcessStartTime) + .count(); + } + + bool TimedOut() { + return Options.MaxTotalTimeSec > 0 && + secondsSinceProcessStartUp() > + static_cast(Options.MaxTotalTimeSec); + } + + size_t execPerSec() { + size_t Seconds = secondsSinceProcessStartUp(); + return Seconds ? TotalNumberOfRuns / Seconds : 0; + } + + size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; } + + static void StaticAlarmCallback(); + static void StaticCrashSignalCallback(); + static void StaticExitCallback(); + static void StaticInterruptCallback(); + static void StaticFileSizeExceedCallback(); + static void StaticGracefulExitCallback(); + + void ExecuteCallback(const uint8_t *Data, size_t Size); + bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false, + InputInfo *II = nullptr, bool ForceAddToCorpus = false, + bool *FoundUniqFeatures = nullptr); + + // Merge Corpora[1:] into Corpora[0]. + void Merge(const Vector &Corpora); + void CrashResistantMergeInternalStep(const std::string &ControlFilePath); + MutationDispatcher &GetMD() { return MD; } + void PrintFinalStats(); + void SetMaxInputLen(size_t MaxInputLen); + void SetMaxMutationLen(size_t MaxMutationLen); + void RssLimitCallback(); + + bool InFuzzingThread() const { return IsMyThread; } + size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const; + void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + bool DuringInitialCorpusExecution); + + void HandleMalloc(size_t Size); + static void MaybeExitGracefully(); + std::string WriteToOutputCorpus(const Unit &U); + +private: + void AlarmCallback(); + void CrashCallback(); + void ExitCallback(); + void CrashOnOverwrittenData(); + void InterruptCallback(); + void MutateAndTestOne(); + void PurgeAllocator(); + void ReportNewCoverage(InputInfo *II, const Unit &U); + void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size); + void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix); + void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0, + size_t Features = 0); + void PrintStatusForNewUnit(const Unit &U, const char *Text); + void CheckExitOnSrcPosOrItem(); + + static void StaticDeathCallback(); + void DumpCurrentUnit(const char *Prefix); + void DeathCallback(); + + void AllocateCurrentUnitData(); + uint8_t *CurrentUnitData = nullptr; + std::atomic CurrentUnitSize; + uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit. + + bool GracefulExitRequested = false; + + size_t TotalNumberOfRuns = 0; + size_t NumberOfNewUnitsAdded = 0; + + size_t LastCorpusUpdateRun = 0; + + bool HasMoreMallocsThanFrees = false; + size_t NumberOfLeakDetectionAttempts = 0; + + system_clock::time_point LastAllocatorPurgeAttemptTime = system_clock::now(); + + UserCallback CB; + InputCorpus &Corpus; + MutationDispatcher &MD; + FuzzingOptions Options; + DataFlowTrace DFT; + + system_clock::time_point ProcessStartTime = system_clock::now(); + system_clock::time_point UnitStartTime, UnitStopTime; + long TimeOfLongestUnitInSeconds = 0; + long EpochOfLastReadOfOutputCorpus = 0; + + size_t MaxInputLen = 0; + size_t MaxMutationLen = 0; + size_t TmpMaxMutationLen = 0; + + Vector UniqFeatureSetTmp; + + // Need to know our own thread. + static thread_local bool IsMyThread; +}; + +struct ScopedEnableMsanInterceptorChecks { + ScopedEnableMsanInterceptorChecks() { + if (EF->__msan_scoped_enable_interceptor_checks) + EF->__msan_scoped_enable_interceptor_checks(); + } + ~ScopedEnableMsanInterceptorChecks() { + if (EF->__msan_scoped_disable_interceptor_checks) + EF->__msan_scoped_disable_interceptor_checks(); + } +}; + +struct ScopedDisableMsanInterceptorChecks { + ScopedDisableMsanInterceptorChecks() { + if (EF->__msan_scoped_disable_interceptor_checks) + EF->__msan_scoped_disable_interceptor_checks(); + } + ~ScopedDisableMsanInterceptorChecks() { + if (EF->__msan_scoped_enable_interceptor_checks) + EF->__msan_scoped_enable_interceptor_checks(); + } +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_INTERNAL_H diff --git a/custom_mutators/libfuzzer/FuzzerLoop.cpp b/custom_mutators/libfuzzer/FuzzerLoop.cpp new file mode 100644 index 00000000..49187b30 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerLoop.cpp @@ -0,0 +1,1087 @@ +//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Fuzzer's main loop. +//===----------------------------------------------------------------------===// + +#include "FuzzerCorpus.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include "FuzzerMutate.h" +#include "FuzzerPlatform.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include +#include + +#if defined(__has_include) + #if __has_include() + #include + #endif +#endif + +#define NO_SANITIZE_MEMORY +#if defined(__has_feature) + #if __has_feature(memory_sanitizer) + #undef NO_SANITIZE_MEMORY + #define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) + #endif +#endif + +namespace fuzzer { + +static const size_t kMaxUnitSizeToPrint = 256; + +thread_local bool Fuzzer::IsMyThread; + +bool RunningUserCallback = false; + +// Only one Fuzzer per process. +static Fuzzer *F; + +// Leak detection is expensive, so we first check if there were more mallocs +// than frees (using the sanitizer malloc hooks) and only then try to call lsan. +struct MallocFreeTracer { + + void Start(int TraceLevel) { + + this->TraceLevel = TraceLevel; + if (TraceLevel) Printf("MallocFreeTracer: START\n"); + Mallocs = 0; + Frees = 0; + + } + + // Returns true if there were more mallocs than frees. + bool Stop() { + + if (TraceLevel) + Printf("MallocFreeTracer: STOP %zd %zd (%s)\n", Mallocs.load(), + Frees.load(), Mallocs == Frees ? "same" : "DIFFERENT"); + bool Result = Mallocs > Frees; + Mallocs = 0; + Frees = 0; + TraceLevel = 0; + return Result; + + } + + std::atomic Mallocs; + std::atomic Frees; + int TraceLevel = 0; + + std::recursive_mutex TraceMutex; + bool TraceDisabled = false; + +}; + +static MallocFreeTracer AllocTracer; + +// Locks printing and avoids nested hooks triggered from mallocs/frees in +// sanitizer. +class TraceLock { + + public: + TraceLock() : Lock(AllocTracer.TraceMutex) { + + AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled; + + } + + ~TraceLock() { + + AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled; + + } + + bool IsDisabled() const { + + // This is already inverted value. + return !AllocTracer.TraceDisabled; + + } + + private: + std::lock_guard Lock; + +}; + +ATTRIBUTE_NO_SANITIZE_MEMORY +void MallocHook(const volatile void *ptr, size_t size) { + + size_t N = AllocTracer.Mallocs++; + F->HandleMalloc(size); + if (int TraceLevel = AllocTracer.TraceLevel) { + + TraceLock Lock; + if (Lock.IsDisabled()) return; + Printf("MALLOC[%zd] %p %zd\n", N, ptr, size); + if (TraceLevel >= 2 && EF) PrintStackTrace(); + + } + +} + +ATTRIBUTE_NO_SANITIZE_MEMORY +void FreeHook(const volatile void *ptr) { + + size_t N = AllocTracer.Frees++; + if (int TraceLevel = AllocTracer.TraceLevel) { + + TraceLock Lock; + if (Lock.IsDisabled()) return; + Printf("FREE[%zd] %p\n", N, ptr); + if (TraceLevel >= 2 && EF) PrintStackTrace(); + + } + +} + +// Crash on a single malloc that exceeds the rss limit. +void Fuzzer::HandleMalloc(size_t Size) { + + if (!Options.MallocLimitMb || (Size >> 20) < (size_t)Options.MallocLimitMb) + return; + Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(), + Size); + Printf(" To change the out-of-memory limit use -rss_limit_mb=\n\n"); + PrintStackTrace(); + DumpCurrentUnit("oom-"); + Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); + _Exit(Options.OOMExitCode); // Stop right now. + +} + +Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, + FuzzingOptions Options) + : CB(CB), Corpus(Corpus), MD(MD), Options(Options) { + + if (EF->__sanitizer_set_death_callback) + EF->__sanitizer_set_death_callback(StaticDeathCallback); + assert(!F); + F = this; + TPC.ResetMaps(); + IsMyThread = true; + if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks) + EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook); + TPC.SetUseCounters(Options.UseCounters); + TPC.SetUseValueProfileMask(Options.UseValueProfile); + + if (Options.Verbosity) TPC.PrintModuleInfo(); + if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec) + EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus); + MaxInputLen = MaxMutationLen = Options.MaxLen; + TmpMaxMutationLen = 0; // Will be set once we load the corpus. + AllocateCurrentUnitData(); + CurrentUnitSize = 0; + memset(BaseSha1, 0, sizeof(BaseSha1)); + +} + +Fuzzer::~Fuzzer() { + +} + +void Fuzzer::AllocateCurrentUnitData() { + + if (CurrentUnitData || MaxInputLen == 0) return; + CurrentUnitData = new uint8_t[MaxInputLen]; + +} + +void Fuzzer::StaticDeathCallback() { + + assert(F); + F->DeathCallback(); + +} + +void Fuzzer::DumpCurrentUnit(const char *Prefix) { + + if (!CurrentUnitData) return; // Happens when running individual inputs. + ScopedDisableMsanInterceptorChecks S; + MD.PrintMutationSequence(); + Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str()); + size_t UnitSize = CurrentUnitSize; + if (UnitSize <= kMaxUnitSizeToPrint) { + + PrintHexArray(CurrentUnitData, UnitSize, "\n"); + PrintASCII(CurrentUnitData, UnitSize, "\n"); + + } + + WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize}, + Prefix); + +} + +NO_SANITIZE_MEMORY +void Fuzzer::DeathCallback() { + + DumpCurrentUnit("crash-"); + PrintFinalStats(); + +} + +void Fuzzer::StaticAlarmCallback() { + + assert(F); + F->AlarmCallback(); + +} + +void Fuzzer::StaticCrashSignalCallback() { + + assert(F); + F->CrashCallback(); + +} + +void Fuzzer::StaticExitCallback() { + + assert(F); + F->ExitCallback(); + +} + +void Fuzzer::StaticInterruptCallback() { + + assert(F); + F->InterruptCallback(); + +} + +void Fuzzer::StaticGracefulExitCallback() { + + assert(F); + F->GracefulExitRequested = true; + Printf("INFO: signal received, trying to exit gracefully\n"); + +} + +void Fuzzer::StaticFileSizeExceedCallback() { + + Printf("==%lu== ERROR: libFuzzer: file size exceeded\n", GetPid()); + exit(1); + +} + +void Fuzzer::CrashCallback() { + + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid()); + PrintStackTrace(); + Printf( + "NOTE: libFuzzer has rudimentary signal handlers.\n" + " Combine libFuzzer with AddressSanitizer or similar for better " + "crash reports.\n"); + Printf("SUMMARY: libFuzzer: deadly signal\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // Stop right now. + +} + +void Fuzzer::ExitCallback() { + + if (!RunningUserCallback) + return; // This exit did not come from the user callback + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + Printf("==%lu== ERROR: libFuzzer: fuzz target exited\n", GetPid()); + PrintStackTrace(); + Printf("SUMMARY: libFuzzer: fuzz target exited\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); + +} + +void Fuzzer::MaybeExitGracefully() { + + if (!F->GracefulExitRequested) return; + Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid()); + RmDirRecursive(TempPath("FuzzWithFork", ".dir")); + F->PrintFinalStats(); + _Exit(0); + +} + +void Fuzzer::InterruptCallback() { + + Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid()); + PrintFinalStats(); + ScopedDisableMsanInterceptorChecks S; // RmDirRecursive may call opendir(). + RmDirRecursive(TempPath("FuzzWithFork", ".dir")); + // Stop right now, don't perform any at-exit actions. + _Exit(Options.InterruptExitCode); + +} + +NO_SANITIZE_MEMORY +void Fuzzer::AlarmCallback() { + + assert(Options.UnitTimeoutSec > 0); + // In Windows and Fuchsia, Alarm callback is executed by a different thread. + // NetBSD's current behavior needs this change too. +#if !LIBFUZZER_WINDOWS && !LIBFUZZER_NETBSD && !LIBFUZZER_FUCHSIA + if (!InFuzzingThread()) return; +#endif + if (!RunningUserCallback) return; // We have not started running units yet. + size_t Seconds = + duration_cast(system_clock::now() - UnitStartTime).count(); + if (Seconds == 0) return; + if (Options.Verbosity >= 2) Printf("AlarmCallback %zd\n", Seconds); + if (Seconds >= (size_t)Options.UnitTimeoutSec) { + + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds); + Printf(" and the timeout value is %d (use -timeout=N to change)\n", + Options.UnitTimeoutSec); + DumpCurrentUnit("timeout-"); + Printf("==%lu== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(), + Seconds); + PrintStackTrace(); + Printf("SUMMARY: libFuzzer: timeout\n"); + PrintFinalStats(); + _Exit(Options.TimeoutExitCode); // Stop right now. + + } + +} + +void Fuzzer::RssLimitCallback() { + + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + Printf( + "==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n", + GetPid(), GetPeakRSSMb(), Options.RssLimitMb); + Printf(" To change the out-of-memory limit use -rss_limit_mb=\n\n"); + PrintMemoryProfile(); + DumpCurrentUnit("oom-"); + Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); + _Exit(Options.OOMExitCode); // Stop right now. + +} + +void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units, + size_t Features) { + + size_t ExecPerSec = execPerSec(); + if (!Options.Verbosity) return; + Printf("#%zd\t%s", TotalNumberOfRuns, Where); + if (size_t N = TPC.GetTotalPCCoverage()) Printf(" cov: %zd", N); + if (size_t N = Features ? Features : Corpus.NumFeatures()) + Printf(" ft: %zd", N); + if (!Corpus.empty()) { + + Printf(" corp: %zd", Corpus.NumActiveUnits()); + if (size_t N = Corpus.SizeInBytes()) { + + if (N < (1 << 14)) + Printf("/%zdb", N); + else if (N < (1 << 24)) + Printf("/%zdKb", N >> 10); + else + Printf("/%zdMb", N >> 20); + + } + + if (size_t FF = Corpus.NumInputsThatTouchFocusFunction()) + Printf(" focus: %zd", FF); + + } + + if (TmpMaxMutationLen) Printf(" lim: %zd", TmpMaxMutationLen); + if (Units) Printf(" units: %zd", Units); + + Printf(" exec/s: %zd", ExecPerSec); + Printf(" rss: %zdMb", GetPeakRSSMb()); + Printf("%s", End); + +} + +void Fuzzer::PrintFinalStats() { + + if (Options.PrintCoverage) TPC.PrintCoverage(); + if (Options.PrintCorpusStats) Corpus.PrintStats(); + if (!Options.PrintFinalStats) return; + size_t ExecPerSec = execPerSec(); + Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns); + Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec); + Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded); + Printf("stat::slowest_unit_time_sec: %zd\n", TimeOfLongestUnitInSeconds); + Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb()); + +} + +void Fuzzer::SetMaxInputLen(size_t MaxInputLen) { + + assert(this->MaxInputLen == + 0); // Can only reset MaxInputLen from 0 to non-0. + assert(MaxInputLen); + this->MaxInputLen = MaxInputLen; + this->MaxMutationLen = MaxInputLen; + AllocateCurrentUnitData(); + Printf( + "INFO: -max_len is not provided; " + "libFuzzer will not generate inputs larger than %zd bytes\n", + MaxInputLen); + +} + +void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) { + + assert(MaxMutationLen && MaxMutationLen <= MaxInputLen); + this->MaxMutationLen = MaxMutationLen; + +} + +void Fuzzer::CheckExitOnSrcPosOrItem() { + + if (!Options.ExitOnSrcPos.empty()) { + + static auto *PCsSet = new Set; + auto HandlePC = [&](const TracePC::PCTableEntry *TE) { + + if (!PCsSet->insert(TE->PC).second) return; + std::string Descr = DescribePC("%F %L", TE->PC + 1); + if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) { + + Printf("INFO: found line matching '%s', exiting.\n", + Options.ExitOnSrcPos.c_str()); + _Exit(0); + + } + + }; + + TPC.ForEachObservedPC(HandlePC); + + } + + if (!Options.ExitOnItem.empty()) { + + if (Corpus.HasUnit(Options.ExitOnItem)) { + + Printf("INFO: found item with checksum '%s', exiting.\n", + Options.ExitOnItem.c_str()); + _Exit(0); + + } + + } + +} + +void Fuzzer::RereadOutputCorpus(size_t MaxSize) { + + if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return; + Vector AdditionalCorpus; + ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus, + &EpochOfLastReadOfOutputCorpus, MaxSize, + /*ExitOnError*/ false); + if (Options.Verbosity >= 2) + Printf("Reload: read %zd new units.\n", AdditionalCorpus.size()); + bool Reloaded = false; + for (auto &U : AdditionalCorpus) { + + if (U.size() > MaxSize) U.resize(MaxSize); + if (!Corpus.HasUnit(U)) { + + if (RunOne(U.data(), U.size())) { + + CheckExitOnSrcPosOrItem(); + Reloaded = true; + + } + + } + + } + + if (Reloaded) PrintStats("RELOAD"); + +} + +void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) { + + auto TimeOfUnit = + duration_cast(UnitStopTime - UnitStartTime).count(); + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && + secondsSinceProcessStartUp() >= 2) + PrintStats("pulse "); + if (TimeOfUnit > TimeOfLongestUnitInSeconds * 1.1 && + TimeOfUnit >= Options.ReportSlowUnits) { + + TimeOfLongestUnitInSeconds = TimeOfUnit; + Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds); + WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-"); + + } + +} + +static void WriteFeatureSetToFile(const std::string & FeaturesDir, + const std::string & FileName, + const Vector &FeatureSet) { + + if (FeaturesDir.empty() || FeatureSet.empty()) return; + WriteToFile(reinterpret_cast(FeatureSet.data()), + FeatureSet.size() * sizeof(FeatureSet[0]), + DirPlusFile(FeaturesDir, FileName)); + +} + +static void RenameFeatureSetFile(const std::string &FeaturesDir, + const std::string &OldFile, + const std::string &NewFile) { + + if (FeaturesDir.empty()) return; + RenameFile(DirPlusFile(FeaturesDir, OldFile), + DirPlusFile(FeaturesDir, NewFile)); + +} + +static void WriteEdgeToMutationGraphFile(const std::string &MutationGraphFile, + const InputInfo * II, + const InputInfo * BaseII, + const std::string &MS) { + + if (MutationGraphFile.empty()) return; + + std::string Sha1 = Sha1ToString(II->Sha1); + + std::string OutputString; + + // Add a new vertex. + OutputString.append("\""); + OutputString.append(Sha1); + OutputString.append("\"\n"); + + // Add a new edge if there is base input. + if (BaseII) { + + std::string BaseSha1 = Sha1ToString(BaseII->Sha1); + OutputString.append("\""); + OutputString.append(BaseSha1); + OutputString.append("\" -> \""); + OutputString.append(Sha1); + OutputString.append("\" [label=\""); + OutputString.append(MS); + OutputString.append("\"];\n"); + + } + + AppendToFile(OutputString, MutationGraphFile); + +} + +bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, + InputInfo *II, bool ForceAddToCorpus, + bool *FoundUniqFeatures) { + + if (!Size) return false; + + ExecuteCallback(Data, Size); + auto TimeOfUnit = duration_cast(UnitStopTime - UnitStartTime); + + UniqFeatureSetTmp.clear(); + size_t FoundUniqFeaturesOfII = 0; + size_t NumUpdatesBefore = Corpus.NumFeatureUpdates(); + TPC.CollectFeatures([&](size_t Feature) { + + if (Corpus.AddFeature(Feature, Size, Options.Shrink)) + UniqFeatureSetTmp.push_back(Feature); + if (Options.Entropic) Corpus.UpdateFeatureFrequency(II, Feature); + if (Options.ReduceInputs && II && !II->NeverReduce) + if (std::binary_search(II->UniqFeatureSet.begin(), + II->UniqFeatureSet.end(), Feature)) + FoundUniqFeaturesOfII++; + + }); + + if (FoundUniqFeatures) *FoundUniqFeatures = FoundUniqFeaturesOfII; + PrintPulseAndReportSlowInput(Data, Size); + size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore; + if (NumNewFeatures || ForceAddToCorpus) { + + TPC.UpdateObservedPCs(); + auto NewII = + Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile, + TPC.ObservedFocusFunction(), ForceAddToCorpus, + TimeOfUnit, UniqFeatureSetTmp, DFT, II); + WriteFeatureSetToFile(Options.FeaturesDir, Sha1ToString(NewII->Sha1), + NewII->UniqFeatureSet); + WriteEdgeToMutationGraphFile(Options.MutationGraphFile, NewII, II, + MD.MutationSequence()); + return true; + + } + + if (II && FoundUniqFeaturesOfII && + II->DataFlowTraceForFocusFunction.empty() && + FoundUniqFeaturesOfII == II->UniqFeatureSet.size() && + II->U.size() > Size) { + + auto OldFeaturesFile = Sha1ToString(II->Sha1); + Corpus.Replace(II, {Data, Data + Size}); + RenameFeatureSetFile(Options.FeaturesDir, OldFeaturesFile, + Sha1ToString(II->Sha1)); + return true; + + } + + return false; + +} + +size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { + + assert(InFuzzingThread()); + *Data = CurrentUnitData; + return CurrentUnitSize; + +} + +void Fuzzer::CrashOnOverwrittenData() { + + Printf("==%d== ERROR: libFuzzer: fuzz target overwrites its const input\n", + GetPid()); + PrintStackTrace(); + Printf("SUMMARY: libFuzzer: overwrites-const-input\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // Stop right now. + +} + +// Compare two arrays, but not all bytes if the arrays are large. +static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) { + + const size_t Limit = 64; + if (Size <= 64) return !memcmp(A, B, Size); + // Compare first and last Limit/2 bytes. + return !memcmp(A, B, Limit / 2) && + !memcmp(A + Size - Limit / 2, B + Size - Limit / 2, Limit / 2); + +} + +void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { + + TPC.RecordInitialStack(); + TotalNumberOfRuns++; + assert(InFuzzingThread()); + // We copy the contents of Unit into a separate heap buffer + // so that we reliably find buffer overflows in it. + uint8_t *DataCopy = new uint8_t[Size]; + memcpy(DataCopy, Data, Size); + if (EF->__msan_unpoison) EF->__msan_unpoison(DataCopy, Size); + if (EF->__msan_unpoison_param) EF->__msan_unpoison_param(2); + if (CurrentUnitData && CurrentUnitData != Data) + memcpy(CurrentUnitData, Data, Size); + CurrentUnitSize = Size; + { + + ScopedEnableMsanInterceptorChecks S; + AllocTracer.Start(Options.TraceMalloc); + UnitStartTime = system_clock::now(); + TPC.ResetMaps(); + RunningUserCallback = true; + int Res = CB(DataCopy, Size); + RunningUserCallback = false; + UnitStopTime = system_clock::now(); + (void)Res; + assert(Res == 0); + HasMoreMallocsThanFrees = AllocTracer.Stop(); + + } + + if (!LooseMemeq(DataCopy, Data, Size)) CrashOnOverwrittenData(); + CurrentUnitSize = 0; + delete[] DataCopy; + +} + +std::string Fuzzer::WriteToOutputCorpus(const Unit &U) { + + if (Options.OnlyASCII) assert(IsASCII(U)); + if (Options.OutputCorpus.empty()) return ""; + std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U)); + WriteToFile(U, Path); + if (Options.Verbosity >= 2) + Printf("Written %zd bytes to %s\n", U.size(), Path.c_str()); + return Path; + +} + +void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) { + + if (!Options.SaveArtifacts) return; + std::string Path = Options.ArtifactPrefix + Prefix + Hash(U); + if (!Options.ExactArtifactPath.empty()) + Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix. + WriteToFile(U, Path); + Printf("artifact_prefix='%s'; Test unit written to %s\n", + Options.ArtifactPrefix.c_str(), Path.c_str()); + if (U.size() <= kMaxUnitSizeToPrint) + Printf("Base64: %s\n", Base64(U).c_str()); + +} + +void Fuzzer::PrintStatusForNewUnit(const Unit &U, const char *Text) { + + if (!Options.PrintNEW) return; + PrintStats(Text, ""); + if (Options.Verbosity) { + + Printf(" L: %zd/%zd ", U.size(), Corpus.MaxInputSize()); + MD.PrintMutationSequence(); + Printf("\n"); + + } + +} + +void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) { + + II->NumSuccessfullMutations++; + MD.RecordSuccessfulMutationSequence(); + PrintStatusForNewUnit(U, II->Reduced ? "REDUCE" : "NEW "); + WriteToOutputCorpus(U); + NumberOfNewUnitsAdded++; + CheckExitOnSrcPosOrItem(); // Check only after the unit is saved to corpus. + LastCorpusUpdateRun = TotalNumberOfRuns; + +} + +// Tries detecting a memory leak on the particular input that we have just +// executed before calling this function. +void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + bool DuringInitialCorpusExecution) { + + if (!HasMoreMallocsThanFrees) return; // mallocs==frees, a leak is unlikely. + if (!Options.DetectLeaks) return; + if (!DuringInitialCorpusExecution && + TotalNumberOfRuns >= Options.MaxNumberOfRuns) + return; + if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) || + !(EF->__lsan_do_recoverable_leak_check)) + return; // No lsan. + // Run the target once again, but with lsan disabled so that if there is + // a real leak we do not report it twice. + EF->__lsan_disable(); + ExecuteCallback(Data, Size); + EF->__lsan_enable(); + if (!HasMoreMallocsThanFrees) return; // a leak is unlikely. + if (NumberOfLeakDetectionAttempts++ > 1000) { + + Options.DetectLeaks = false; + Printf( + "INFO: libFuzzer disabled leak detection after every mutation.\n" + " Most likely the target function accumulates allocated\n" + " memory in a global state w/o actually leaking it.\n" + " You may try running this binary with -trace_malloc=[12]" + " to get a trace of mallocs and frees.\n" + " If LeakSanitizer is enabled in this process it will still\n" + " run on the process shutdown.\n"); + return; + + } + + // Now perform the actual lsan pass. This is expensive and we must ensure + // we don't call it too often. + if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it. + if (DuringInitialCorpusExecution) + Printf("\nINFO: a leak has been found in the initial corpus.\n\n"); + Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n"); + CurrentUnitSize = Size; + DumpCurrentUnit("leak-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // not exit() to disable lsan further on. + + } + +} + +void Fuzzer::MutateAndTestOne() { + + MD.StartMutationSequence(); + + auto &II = Corpus.ChooseUnitToMutate(MD.GetRand()); + if (Options.DoCrossOver) { + + auto &CrossOverII = Corpus.ChooseUnitToCrossOverWith( + MD.GetRand(), Options.CrossOverUniformDist); + MD.SetCrossOverWith(&CrossOverII.U); + + } + + const auto &U = II.U; + memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1)); + assert(CurrentUnitData); + size_t Size = U.size(); + assert(Size <= MaxInputLen && "Oversized Unit"); + memcpy(CurrentUnitData, U.data(), Size); + + assert(MaxMutationLen > 0); + + size_t CurrentMaxMutationLen = + Min(MaxMutationLen, Max(U.size(), TmpMaxMutationLen)); + assert(CurrentMaxMutationLen > 0); + + for (int i = 0; i < Options.MutateDepth; i++) { + + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) break; + MaybeExitGracefully(); + size_t NewSize = 0; + if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() && + Size <= CurrentMaxMutationLen) + NewSize = MD.MutateWithMask(CurrentUnitData, Size, Size, + II.DataFlowTraceForFocusFunction); + + // If MutateWithMask either failed or wasn't called, call default Mutate. + if (!NewSize) + NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen); + assert(NewSize > 0 && "Mutator returned empty unit"); + assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit"); + Size = NewSize; + II.NumExecutedMutations++; + Corpus.IncrementNumExecutedMutations(); + + bool FoundUniqFeatures = false; + bool NewCov = RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II, + /*ForceAddToCorpus*/ false, &FoundUniqFeatures); + TryDetectingAMemoryLeak(CurrentUnitData, Size, + /*DuringInitialCorpusExecution*/ false); + if (NewCov) { + + ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size}); + break; // We will mutate this input more in the next rounds. + + } + + if (Options.ReduceDepth && !FoundUniqFeatures) break; + + } + + II.NeedsEnergyUpdate = true; + +} + +void Fuzzer::PurgeAllocator() { + + if (Options.PurgeAllocatorIntervalSec < 0 || !EF->__sanitizer_purge_allocator) + return; + if (duration_cast(system_clock::now() - + LastAllocatorPurgeAttemptTime) + .count() < Options.PurgeAllocatorIntervalSec) + return; + + if (Options.RssLimitMb <= 0 || + GetPeakRSSMb() > static_cast(Options.RssLimitMb) / 2) + EF->__sanitizer_purge_allocator(); + + LastAllocatorPurgeAttemptTime = system_clock::now(); + +} + +void Fuzzer::ReadAndExecuteSeedCorpora(Vector &CorporaFiles) { + + const size_t kMaxSaneLen = 1 << 20; + const size_t kMinDefaultLen = 4096; + size_t MaxSize = 0; + size_t MinSize = -1; + size_t TotalSize = 0; + for (auto &File : CorporaFiles) { + + MaxSize = Max(File.Size, MaxSize); + MinSize = Min(File.Size, MinSize); + TotalSize += File.Size; + + } + + if (Options.MaxLen == 0) + SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxSize), kMaxSaneLen)); + assert(MaxInputLen > 0); + + // Test the callback with empty input and never try it again. + uint8_t dummy = 0; + ExecuteCallback(&dummy, 0); + + if (CorporaFiles.empty()) { + + Printf("INFO: A corpus is not provided, starting from an empty corpus\n"); + Unit U({'\n'}); // Valid ASCII input. + RunOne(U.data(), U.size()); + + } else { + + Printf( + "INFO: seed corpus: files: %zd min: %zdb max: %zdb total: %zdb" + " rss: %zdMb\n", + CorporaFiles.size(), MinSize, MaxSize, TotalSize, GetPeakRSSMb()); + if (Options.ShuffleAtStartUp) + std::shuffle(CorporaFiles.begin(), CorporaFiles.end(), MD.GetRand()); + + if (Options.PreferSmall) { + + std::stable_sort(CorporaFiles.begin(), CorporaFiles.end()); + assert(CorporaFiles.front().Size <= CorporaFiles.back().Size); + + } + + // Load and execute inputs one by one. + for (auto &SF : CorporaFiles) { + + auto U = FileToVector(SF.File, MaxInputLen, /*ExitOnError=*/false); + assert(U.size() <= MaxInputLen); + RunOne(U.data(), U.size(), /*MayDeleteFile*/ false, /*II*/ nullptr, + /*ForceAddToCorpus*/ Options.KeepSeed, + /*FoundUniqFeatures*/ nullptr); + CheckExitOnSrcPosOrItem(); + TryDetectingAMemoryLeak(U.data(), U.size(), + /*DuringInitialCorpusExecution*/ true); + + } + + } + + PrintStats("INITED"); + if (!Options.FocusFunction.empty()) { + + Printf("INFO: %zd/%zd inputs touch the focus function\n", + Corpus.NumInputsThatTouchFocusFunction(), Corpus.size()); + if (!Options.DataFlowTrace.empty()) + Printf("INFO: %zd/%zd inputs have the Data Flow Trace\n", + Corpus.NumInputsWithDataFlowTrace(), + Corpus.NumInputsThatTouchFocusFunction()); + + } + + if (Corpus.empty() && Options.MaxNumberOfRuns) { + + Printf( + "ERROR: no interesting inputs were found. " + "Is the code instrumented for coverage? Exiting.\n"); + exit(1); + + } + +} + +void Fuzzer::Loop(Vector &CorporaFiles) { + + auto FocusFunctionOrAuto = Options.FocusFunction; + DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles, + MD.GetRand()); + TPC.SetFocusFunction(FocusFunctionOrAuto); + ReadAndExecuteSeedCorpora(CorporaFiles); + DFT.Clear(); // No need for DFT any more. + TPC.SetPrintNewPCs(Options.PrintNewCovPcs); + TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs); + system_clock::time_point LastCorpusReload = system_clock::now(); + + TmpMaxMutationLen = + Min(MaxMutationLen, Max(size_t(4), Corpus.MaxInputSize())); + + while (true) { + + auto Now = system_clock::now(); + if (!Options.StopFile.empty() && + !FileToVector(Options.StopFile, 1, false).empty()) + break; + if (duration_cast(Now - LastCorpusReload).count() >= + Options.ReloadIntervalSec) { + + RereadOutputCorpus(MaxInputLen); + LastCorpusReload = system_clock::now(); + + } + + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) break; + if (TimedOut()) break; + + // Update TmpMaxMutationLen + if (Options.LenControl) { + + if (TmpMaxMutationLen < MaxMutationLen && + TotalNumberOfRuns - LastCorpusUpdateRun > + Options.LenControl * Log(TmpMaxMutationLen)) { + + TmpMaxMutationLen = + Min(MaxMutationLen, TmpMaxMutationLen + Log(TmpMaxMutationLen)); + LastCorpusUpdateRun = TotalNumberOfRuns; + + } + + } else { + + TmpMaxMutationLen = MaxMutationLen; + + } + + // Perform several mutations and runs. + MutateAndTestOne(); + + PurgeAllocator(); + + } + + PrintStats("DONE ", "\n"); + MD.PrintRecommendedDictionary(); + +} + +void Fuzzer::MinimizeCrashLoop(const Unit &U) { + + if (U.size() <= 1) return; + while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) { + + MD.StartMutationSequence(); + memcpy(CurrentUnitData, U.data(), U.size()); + for (int i = 0; i < Options.MutateDepth; i++) { + + size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen); + assert(NewSize > 0 && NewSize <= MaxMutationLen); + ExecuteCallback(CurrentUnitData, NewSize); + PrintPulseAndReportSlowInput(CurrentUnitData, NewSize); + TryDetectingAMemoryLeak(CurrentUnitData, NewSize, + /*DuringInitialCorpusExecution*/ false); + + } + + } + +} + +} // namespace fuzzer + +extern "C" { + +ATTRIBUTE_INTERFACE size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, + size_t MaxSize) { + + assert(fuzzer::F); + return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize); + +} + +} // extern "C" + diff --git a/custom_mutators/libfuzzer/FuzzerMerge.cpp b/custom_mutators/libfuzzer/FuzzerMerge.cpp new file mode 100644 index 00000000..b341f5b3 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerMerge.cpp @@ -0,0 +1,485 @@ +//===- FuzzerMerge.cpp - merging corpora ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Merging corpora. +//===----------------------------------------------------------------------===// + +#include "FuzzerCommand.h" +#include "FuzzerMerge.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include "FuzzerTracePC.h" +#include "FuzzerUtil.h" + +#include +#include +#include +#include +#include + +namespace fuzzer { + +bool Merger::Parse(const std::string &Str, bool ParseCoverage) { + + std::istringstream SS(Str); + return Parse(SS, ParseCoverage); + +} + +void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) { + + if (!Parse(IS, ParseCoverage)) { + + Printf("MERGE: failed to parse the control file (unexpected error)\n"); + exit(1); + + } + +} + +// The control file example: +// +// 3 # The number of inputs +// 1 # The number of inputs in the first corpus, <= the previous number +// file0 +// file1 +// file2 # One file name per line. +// STARTED 0 123 # FileID, file size +// FT 0 1 4 6 8 # FileID COV1 COV2 ... +// COV 0 7 8 9 # FileID COV1 COV1 +// STARTED 1 456 # If FT is missing, the input crashed while processing. +// STARTED 2 567 +// FT 2 8 9 +// COV 2 11 12 +bool Merger::Parse(std::istream &IS, bool ParseCoverage) { + + LastFailure.clear(); + std::string Line; + + // Parse NumFiles. + if (!std::getline(IS, Line, '\n')) return false; + std::istringstream L1(Line); + size_t NumFiles = 0; + L1 >> NumFiles; + if (NumFiles == 0 || NumFiles > 10000000) return false; + + // Parse NumFilesInFirstCorpus. + if (!std::getline(IS, Line, '\n')) return false; + std::istringstream L2(Line); + NumFilesInFirstCorpus = NumFiles + 1; + L2 >> NumFilesInFirstCorpus; + if (NumFilesInFirstCorpus > NumFiles) return false; + + // Parse file names. + Files.resize(NumFiles); + for (size_t i = 0; i < NumFiles; i++) + if (!std::getline(IS, Files[i].Name, '\n')) return false; + + // Parse STARTED, FT, and COV lines. + size_t ExpectedStartMarker = 0; + const size_t kInvalidStartMarker = -1; + size_t LastSeenStartMarker = kInvalidStartMarker; + Vector TmpFeatures; + Set PCs; + while (std::getline(IS, Line, '\n')) { + + std::istringstream ISS1(Line); + std::string Marker; + size_t N; + ISS1 >> Marker; + ISS1 >> N; + if (Marker == "STARTED") { + + // STARTED FILE_ID FILE_SIZE + if (ExpectedStartMarker != N) return false; + ISS1 >> Files[ExpectedStartMarker].Size; + LastSeenStartMarker = ExpectedStartMarker; + assert(ExpectedStartMarker < Files.size()); + ExpectedStartMarker++; + + } else if (Marker == "FT") { + + // FT FILE_ID COV1 COV2 COV3 ... + size_t CurrentFileIdx = N; + if (CurrentFileIdx != LastSeenStartMarker) return false; + LastSeenStartMarker = kInvalidStartMarker; + if (ParseCoverage) { + + TmpFeatures.clear(); // use a vector from outer scope to avoid resizes. + while (ISS1 >> N) + TmpFeatures.push_back(N); + std::sort(TmpFeatures.begin(), TmpFeatures.end()); + Files[CurrentFileIdx].Features = TmpFeatures; + + } + + } else if (Marker == "COV") { + + size_t CurrentFileIdx = N; + if (ParseCoverage) + while (ISS1 >> N) + if (PCs.insert(N).second) Files[CurrentFileIdx].Cov.push_back(N); + + } else { + + return false; + + } + + } + + if (LastSeenStartMarker != kInvalidStartMarker) + LastFailure = Files[LastSeenStartMarker].Name; + + FirstNotProcessedFile = ExpectedStartMarker; + return true; + +} + +size_t Merger::ApproximateMemoryConsumption() const { + + size_t Res = 0; + for (const auto &F : Files) + Res += sizeof(F) + F.Features.size() * sizeof(F.Features[0]); + return Res; + +} + +// Decides which files need to be merged (add those to NewFiles). +// Returns the number of new features added. +size_t Merger::Merge(const Set &InitialFeatures, + Set * NewFeatures, + const Set &InitialCov, Set *NewCov, + Vector *NewFiles) { + + NewFiles->clear(); + assert(NumFilesInFirstCorpus <= Files.size()); + Set AllFeatures = InitialFeatures; + + // What features are in the initial corpus? + for (size_t i = 0; i < NumFilesInFirstCorpus; i++) { + + auto &Cur = Files[i].Features; + AllFeatures.insert(Cur.begin(), Cur.end()); + + } + + // Remove all features that we already know from all other inputs. + for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) { + + auto & Cur = Files[i].Features; + Vector Tmp; + std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(), + AllFeatures.end(), std::inserter(Tmp, Tmp.begin())); + Cur.swap(Tmp); + + } + + // Sort. Give preference to + // * smaller files + // * files with more features. + std::sort(Files.begin() + NumFilesInFirstCorpus, Files.end(), + [&](const MergeFileInfo &a, const MergeFileInfo &b) -> bool { + + if (a.Size != b.Size) return a.Size < b.Size; + return a.Features.size() > b.Features.size(); + + }); + + // One greedy pass: add the file's features to AllFeatures. + // If new features were added, add this file to NewFiles. + for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) { + + auto &Cur = Files[i].Features; + // Printf("%s -> sz %zd ft %zd\n", Files[i].Name.c_str(), + // Files[i].Size, Cur.size()); + bool FoundNewFeatures = false; + for (auto Fe : Cur) { + + if (AllFeatures.insert(Fe).second) { + + FoundNewFeatures = true; + NewFeatures->insert(Fe); + + } + + } + + if (FoundNewFeatures) NewFiles->push_back(Files[i].Name); + for (auto Cov : Files[i].Cov) + if (InitialCov.find(Cov) == InitialCov.end()) NewCov->insert(Cov); + + } + + return NewFeatures->size(); + +} + +Set Merger::AllFeatures() const { + + Set S; + for (auto &File : Files) + S.insert(File.Features.begin(), File.Features.end()); + return S; + +} + +// Inner process. May crash if the target crashes. +void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { + + Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str()); + Merger M; + std::ifstream IF(CFPath); + M.ParseOrExit(IF, false); + IF.close(); + if (!M.LastFailure.empty()) + Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n", + M.LastFailure.c_str()); + + Printf( + "MERGE-INNER: %zd total files;" + " %zd processed earlier; will process %zd files now\n", + M.Files.size(), M.FirstNotProcessedFile, + M.Files.size() - M.FirstNotProcessedFile); + + std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app); + Set AllFeatures; + auto PrintStatsWrapper = [this, &AllFeatures](const char *Where) { + + this->PrintStats(Where, "\n", 0, AllFeatures.size()); + + }; + + Set AllPCs; + for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) { + + Fuzzer::MaybeExitGracefully(); + auto U = FileToVector(M.Files[i].Name); + if (U.size() > MaxInputLen) { + + U.resize(MaxInputLen); + U.shrink_to_fit(); + + } + + // Write the pre-run marker. + OF << "STARTED " << i << " " << U.size() << "\n"; + OF.flush(); // Flush is important since Command::Execute may crash. + // Run. + TPC.ResetMaps(); + ExecuteCallback(U.data(), U.size()); + // Collect coverage. We are iterating over the files in this order: + // * First, files in the initial corpus ordered by size, smallest first. + // * Then, all other files, smallest first. + // So it makes no sense to record all features for all files, instead we + // only record features that were not seen before. + Set UniqFeatures; + TPC.CollectFeatures([&](size_t Feature) { + + if (AllFeatures.insert(Feature).second) UniqFeatures.insert(Feature); + + }); + + TPC.UpdateObservedPCs(); + // Show stats. + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1))) + PrintStatsWrapper("pulse "); + if (TotalNumberOfRuns == M.NumFilesInFirstCorpus) + PrintStatsWrapper("LOADED"); + // Write the post-run marker and the coverage. + OF << "FT " << i; + for (size_t F : UniqFeatures) + OF << " " << F; + OF << "\n"; + OF << "COV " << i; + TPC.ForEachObservedPC([&](const TracePC::PCTableEntry *TE) { + + if (AllPCs.insert(TE).second) OF << " " << TPC.PCTableEntryIdx(TE); + + }); + + OF << "\n"; + OF.flush(); + + } + + PrintStatsWrapper("DONE "); + +} + +static size_t WriteNewControlFile(const std::string & CFPath, + const Vector & OldCorpus, + const Vector & NewCorpus, + const Vector &KnownFiles) { + + std::unordered_set FilesToSkip; + for (auto &SF : KnownFiles) + FilesToSkip.insert(SF.Name); + + Vector FilesToUse; + auto MaybeUseFile = [=, &FilesToUse](std::string Name) { + + if (FilesToSkip.find(Name) == FilesToSkip.end()) FilesToUse.push_back(Name); + + }; + + for (auto &SF : OldCorpus) + MaybeUseFile(SF.File); + auto FilesToUseFromOldCorpus = FilesToUse.size(); + for (auto &SF : NewCorpus) + MaybeUseFile(SF.File); + + RemoveFile(CFPath); + std::ofstream ControlFile(CFPath); + ControlFile << FilesToUse.size() << "\n"; + ControlFile << FilesToUseFromOldCorpus << "\n"; + for (auto &FN : FilesToUse) + ControlFile << FN << "\n"; + + if (!ControlFile) { + + Printf("MERGE-OUTER: failed to write to the control file: %s\n", + CFPath.c_str()); + exit(1); + + } + + return FilesToUse.size(); + +} + +// Outer process. Does not call the target code and thus should not fail. +void CrashResistantMerge(const Vector &Args, + const Vector & OldCorpus, + const Vector & NewCorpus, + Vector * NewFiles, + const Set & InitialFeatures, + Set * NewFeatures, + const Set &InitialCov, Set *NewCov, + const std::string &CFPath, bool V /*Verbose*/) { + + if (NewCorpus.empty() && OldCorpus.empty()) return; // Nothing to merge. + size_t NumAttempts = 0; + Vector KnownFiles; + if (FileSize(CFPath)) { + + VPrintf(V, "MERGE-OUTER: non-empty control file provided: '%s'\n", + CFPath.c_str()); + Merger M; + std::ifstream IF(CFPath); + if (M.Parse(IF, /*ParseCoverage=*/true)) { + + VPrintf(V, + "MERGE-OUTER: control file ok, %zd files total," + " first not processed file %zd\n", + M.Files.size(), M.FirstNotProcessedFile); + if (!M.LastFailure.empty()) + VPrintf(V, + "MERGE-OUTER: '%s' will be skipped as unlucky " + "(merge has stumbled on it the last time)\n", + M.LastFailure.c_str()); + if (M.FirstNotProcessedFile >= M.Files.size()) { + + // Merge has already been completed with the given merge control file. + if (M.Files.size() == OldCorpus.size() + NewCorpus.size()) { + + VPrintf( + V, + "MERGE-OUTER: nothing to do, merge has been completed before\n"); + exit(0); + + } + + // Number of input files likely changed, start merge from scratch, but + // reuse coverage information from the given merge control file. + VPrintf( + V, + "MERGE-OUTER: starting merge from scratch, but reusing coverage " + "information from the given control file\n"); + KnownFiles = M.Files; + + } else { + + // There is a merge in progress, continue. + NumAttempts = M.Files.size() - M.FirstNotProcessedFile; + + } + + } else { + + VPrintf(V, "MERGE-OUTER: bad control file, will overwrite it\n"); + + } + + } + + if (!NumAttempts) { + + // The supplied control file is empty or bad, create a fresh one. + VPrintf(V, + "MERGE-OUTER: " + "%zd files, %zd in the initial corpus, %zd processed earlier\n", + OldCorpus.size() + NewCorpus.size(), OldCorpus.size(), + KnownFiles.size()); + NumAttempts = WriteNewControlFile(CFPath, OldCorpus, NewCorpus, KnownFiles); + + } + + // Execute the inner process until it passes. + // Every inner process should execute at least one input. + Command BaseCmd(Args); + BaseCmd.removeFlag("merge"); + BaseCmd.removeFlag("fork"); + BaseCmd.removeFlag("collect_data_flow"); + for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) { + + Fuzzer::MaybeExitGracefully(); + VPrintf(V, "MERGE-OUTER: attempt %zd\n", Attempt); + Command Cmd(BaseCmd); + Cmd.addFlag("merge_control_file", CFPath); + Cmd.addFlag("merge_inner", "1"); + if (!V) { + + Cmd.setOutputFile(getDevNull()); + Cmd.combineOutAndErr(); + + } + + auto ExitCode = ExecuteCommand(Cmd); + if (!ExitCode) { + + VPrintf(V, "MERGE-OUTER: succesfull in %zd attempt(s)\n", Attempt); + break; + + } + + } + + // Read the control file and do the merge. + Merger M; + std::ifstream IF(CFPath); + IF.seekg(0, IF.end); + VPrintf(V, "MERGE-OUTER: the control file has %zd bytes\n", + (size_t)IF.tellg()); + IF.seekg(0, IF.beg); + M.ParseOrExit(IF, true); + IF.close(); + VPrintf(V, + "MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n", + M.ApproximateMemoryConsumption() >> 20, GetPeakRSSMb()); + + M.Files.insert(M.Files.end(), KnownFiles.begin(), KnownFiles.end()); + M.Merge(InitialFeatures, NewFeatures, InitialCov, NewCov, NewFiles); + VPrintf(V, + "MERGE-OUTER: %zd new files with %zd new features added; " + "%zd new coverage edges\n", + NewFiles->size(), NewFeatures->size(), NewCov->size()); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerMerge.h b/custom_mutators/libfuzzer/FuzzerMerge.h new file mode 100644 index 00000000..e0c6bc53 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerMerge.h @@ -0,0 +1,87 @@ +//===- FuzzerMerge.h - merging corpa ----------------------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Merging Corpora. +// +// The task: +// Take the existing corpus (possibly empty) and merge new inputs into +// it so that only inputs with new coverage ('features') are added. +// The process should tolerate the crashes, OOMs, leaks, etc. +// +// Algorithm: +// The outer process collects the set of files and writes their names +// into a temporary "control" file, then repeatedly launches the inner +// process until all inputs are processed. +// The outer process does not actually execute the target code. +// +// The inner process reads the control file and sees a) list of all the inputs +// and b) the last processed input. Then it starts processing the inputs one +// by one. Before processing every input it writes one line to control file: +// STARTED INPUT_ID INPUT_SIZE +// After processing an input it writes the following lines: +// FT INPUT_ID Feature1 Feature2 Feature3 ... +// COV INPUT_ID Coverage1 Coverage2 Coverage3 ... +// If a crash happens while processing an input the last line in the control +// file will be "STARTED INPUT_ID" and so the next process will know +// where to resume. +// +// Once all inputs are processed by the inner process(es) the outer process +// reads the control files and does the merge based entirely on the contents +// of control file. +// It uses a single pass greedy algorithm choosing first the smallest inputs +// within the same size the inputs that have more new features. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_MERGE_H +#define LLVM_FUZZER_MERGE_H + +#include "FuzzerDefs.h" + +#include +#include +#include +#include + +namespace fuzzer { + +struct MergeFileInfo { + std::string Name; + size_t Size = 0; + Vector Features, Cov; +}; + +struct Merger { + Vector Files; + size_t NumFilesInFirstCorpus = 0; + size_t FirstNotProcessedFile = 0; + std::string LastFailure; + + bool Parse(std::istream &IS, bool ParseCoverage); + bool Parse(const std::string &Str, bool ParseCoverage); + void ParseOrExit(std::istream &IS, bool ParseCoverage); + size_t Merge(const Set &InitialFeatures, Set *NewFeatures, + const Set &InitialCov, Set *NewCov, + Vector *NewFiles); + size_t ApproximateMemoryConsumption() const; + Set AllFeatures() const; +}; + +void CrashResistantMerge(const Vector &Args, + const Vector &OldCorpus, + const Vector &NewCorpus, + Vector *NewFiles, + const Set &InitialFeatures, + Set *NewFeatures, + const Set &InitialCov, + Set *NewCov, + const std::string &CFPath, + bool Verbose); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_MERGE_H diff --git a/custom_mutators/libfuzzer/FuzzerMutate.cpp b/custom_mutators/libfuzzer/FuzzerMutate.cpp new file mode 100644 index 00000000..8faf6918 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerMutate.cpp @@ -0,0 +1,720 @@ +//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Mutate a test input. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerOptions.h" +#include "FuzzerTracePC.h" + +namespace fuzzer { + +const size_t Dictionary::kMaxDictSize; + +static void PrintASCII(const Word &W, const char *PrintAfter) { + + PrintASCII(W.data(), W.size(), PrintAfter); + +} + +MutationDispatcher::MutationDispatcher(Random & Rand, + const FuzzingOptions &Options) + : Rand(Rand), Options(Options) { + + DefaultMutators.insert( + DefaultMutators.begin(), + { + + {&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"}, + {&MutationDispatcher::Mutate_InsertByte, "InsertByte"}, + {&MutationDispatcher::Mutate_InsertRepeatedBytes, + "InsertRepeatedBytes"}, + {&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"}, + {&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"}, + {&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"}, + {&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"}, + {&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"}, + {&MutationDispatcher::Mutate_CopyPart, "CopyPart"}, + {&MutationDispatcher::Mutate_CrossOver, "CrossOver"}, + {&MutationDispatcher::Mutate_AddWordFromManualDictionary, + "ManualDict"}, + {&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary, + "PersAutoDict"}, + + }); + + if (Options.UseCmp) + DefaultMutators.push_back( + {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"}); + + if (EF->LLVMFuzzerCustomMutator) + Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"}); + else + Mutators = DefaultMutators; + + if (EF->LLVMFuzzerCustomCrossOver) + Mutators.push_back( + {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"}); + +} + +static char RandCh(Random &Rand) { + + if (Rand.RandBool()) return Rand(256); + const char Special[] = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00"; + return Special[Rand(sizeof(Special) - 1)]; + +} + +size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size, + size_t MaxSize) { + + return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand()); + +} + +size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size == 0) return 0; + if (!CrossOverWith) return 0; + const Unit &Other = *CrossOverWith; + if (Other.empty()) return 0; + CustomCrossOverInPlaceHere.resize(MaxSize); + auto & U = CustomCrossOverInPlaceHere; + size_t NewSize = EF->LLVMFuzzerCustomCrossOver( + Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand()); + if (!NewSize) return 0; + assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit"); + memcpy(Data, U.data(), NewSize); + return NewSize; + +} + +size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize || Size == 0) return 0; + size_t ShuffleAmount = + Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size. + size_t ShuffleStart = Rand(Size - ShuffleAmount); + assert(ShuffleStart + ShuffleAmount <= Size); + std::shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, Rand); + return Size; + +} + +size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size <= 1) return 0; + size_t N = Rand(Size / 2) + 1; + assert(N < Size); + size_t Idx = Rand(Size - N + 1); + // Erase Data[Idx:Idx+N]. + memmove(Data + Idx, Data + Idx + N, Size - Idx - N); + // Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx); + return Size - N; + +} + +size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size >= MaxSize) return 0; + size_t Idx = Rand(Size + 1); + // Insert new value at Data[Idx]. + memmove(Data + Idx + 1, Data + Idx, Size - Idx); + Data[Idx] = RandCh(Rand); + return Size + 1; + +} + +size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data, + size_t Size, + size_t MaxSize) { + + const size_t kMinBytesToInsert = 3; + if (Size + kMinBytesToInsert >= MaxSize) return 0; + size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128); + size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert; + assert(Size + N <= MaxSize && N); + size_t Idx = Rand(Size + 1); + // Insert new values at Data[Idx]. + memmove(Data + Idx + N, Data + Idx, Size - Idx); + // Give preference to 0x00 and 0xff. + uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255); + for (size_t i = 0; i < N; i++) + Data[Idx + i] = Byte; + return Size + N; + +} + +size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + size_t Idx = Rand(Size); + Data[Idx] = RandCh(Rand); + return Size; + +} + +size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + size_t Idx = Rand(Size); + Data[Idx] ^= 1 << Rand(8); + return Size; + +} + +size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data, + size_t Size, + size_t MaxSize) { + + return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize); + +} + +size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size, + size_t MaxSize, + DictionaryEntry &DE) { + + const Word &W = DE.GetW(); + bool UsePositionHint = DE.HasPositionHint() && + DE.GetPositionHint() + W.size() < Size && + Rand.RandBool(); + if (Rand.RandBool()) { // Insert W. + if (Size + W.size() > MaxSize) return 0; + size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1); + memmove(Data + Idx + W.size(), Data + Idx, Size - Idx); + memcpy(Data + Idx, W.data(), W.size()); + Size += W.size(); + + } else { // Overwrite some bytes with W. + + if (W.size() > Size) return 0; + size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size()); + memcpy(Data + Idx, W.data(), W.size()); + + } + + return Size; + +} + +// Somewhere in the past we have observed a comparison instructions +// with arguments Arg1 Arg2. This function tries to guess a dictionary +// entry that will satisfy that comparison. +// It first tries to find one of the arguments (possibly swapped) in the +// input and if it succeeds it creates a DE with a position hint. +// Otherwise it creates a DE with one of the arguments w/o a position hint. +DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( + const void *Arg1, const void *Arg2, const void *Arg1Mutation, + const void *Arg2Mutation, size_t ArgSize, const uint8_t *Data, + size_t Size) { + + bool HandleFirst = Rand.RandBool(); + const void * ExistingBytes, *DesiredBytes; + Word W; + const uint8_t *End = Data + Size; + for (int Arg = 0; Arg < 2; Arg++) { + + ExistingBytes = HandleFirst ? Arg1 : Arg2; + DesiredBytes = HandleFirst ? Arg2Mutation : Arg1Mutation; + HandleFirst = !HandleFirst; + W.Set(reinterpret_cast(DesiredBytes), ArgSize); + const size_t kMaxNumPositions = 8; + size_t Positions[kMaxNumPositions]; + size_t NumPositions = 0; + for (const uint8_t *Cur = Data; + Cur < End && NumPositions < kMaxNumPositions; Cur++) { + + Cur = + (const uint8_t *)SearchMemory(Cur, End - Cur, ExistingBytes, ArgSize); + if (!Cur) break; + Positions[NumPositions++] = Cur - Data; + + } + + if (!NumPositions) continue; + return DictionaryEntry(W, Positions[Rand(NumPositions)]); + + } + + DictionaryEntry DE(W); + return DE; + +} + +template +DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( + T Arg1, T Arg2, const uint8_t *Data, size_t Size) { + + if (Rand.RandBool()) Arg1 = Bswap(Arg1); + if (Rand.RandBool()) Arg2 = Bswap(Arg2); + T Arg1Mutation = Arg1 + Rand(-1, 1); + T Arg2Mutation = Arg2 + Rand(-1, 1); + return MakeDictionaryEntryFromCMP(&Arg1, &Arg2, &Arg1Mutation, &Arg2Mutation, + sizeof(Arg1), Data, Size); + +} + +DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( + const Word &Arg1, const Word &Arg2, const uint8_t *Data, size_t Size) { + + return MakeDictionaryEntryFromCMP(Arg1.data(), Arg2.data(), Arg1.data(), + Arg2.data(), Arg1.size(), Data, Size); + +} + +size_t MutationDispatcher::Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, + size_t MaxSize) { + + Word W; + DictionaryEntry DE; + switch (Rand(4)) { + + case 0: { + + auto X = TPC.TORC8.Get(Rand.Rand()); + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + + } break; + + case 1: { + + auto X = TPC.TORC4.Get(Rand.Rand()); + if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool()) + DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data, + Size); + else + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + + } break; + + case 2: { + + auto X = TPC.TORCW.Get(Rand.Rand()); + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + + } break; + + case 3: + if (Options.UseMemmem) { + + auto X = TPC.MMT.Get(Rand.Rand()); + DE = DictionaryEntry(X); + + } + + break; + default: + assert(0); + + } + + if (!DE.GetW().size()) return 0; + Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE); + if (!Size) return 0; + DictionaryEntry &DERef = + CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ % + kCmpDictionaryEntriesDequeSize]; + DERef = DE; + CurrentDictionaryEntrySequence.push_back(&DERef); + return Size; + +} + +size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary( + uint8_t *Data, size_t Size, size_t MaxSize) { + + return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize); + +} + +size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data, + size_t Size, size_t MaxSize) { + + if (Size > MaxSize) return 0; + if (D.empty()) return 0; + DictionaryEntry &DE = D[Rand(D.size())]; + Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE); + if (!Size) return 0; + DE.IncUseCount(); + CurrentDictionaryEntrySequence.push_back(&DE); + return Size; + +} + +// Overwrites part of To[0,ToSize) with a part of From[0,FromSize). +// Returns ToSize. +size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize, + uint8_t *To, size_t ToSize) { + + // Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize). + size_t ToBeg = Rand(ToSize); + size_t CopySize = Rand(ToSize - ToBeg) + 1; + assert(ToBeg + CopySize <= ToSize); + CopySize = std::min(CopySize, FromSize); + size_t FromBeg = Rand(FromSize - CopySize + 1); + assert(FromBeg + CopySize <= FromSize); + memmove(To + ToBeg, From + FromBeg, CopySize); + return ToSize; + +} + +// Inserts part of From[0,ToSize) into To. +// Returns new size of To on success or 0 on failure. +size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize, + uint8_t *To, size_t ToSize, + size_t MaxToSize) { + + if (ToSize >= MaxToSize) return 0; + size_t AvailableSpace = MaxToSize - ToSize; + size_t MaxCopySize = std::min(AvailableSpace, FromSize); + size_t CopySize = Rand(MaxCopySize) + 1; + size_t FromBeg = Rand(FromSize - CopySize + 1); + assert(FromBeg + CopySize <= FromSize); + size_t ToInsertPos = Rand(ToSize + 1); + assert(ToInsertPos + CopySize <= MaxToSize); + size_t TailSize = ToSize - ToInsertPos; + if (To == From) { + + MutateInPlaceHere.resize(MaxToSize); + memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize); + memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize); + memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize); + + } else { + + memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize); + memmove(To + ToInsertPos, From + FromBeg, CopySize); + + } + + return ToSize + CopySize; + +} + +size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize || Size == 0) return 0; + // If Size == MaxSize, `InsertPartOf(...)` will + // fail so there's no point using it in this case. + if (Size == MaxSize || Rand.RandBool()) + return CopyPartOf(Data, Size, Data, Size); + else + return InsertPartOf(Data, Size, Data, Size, MaxSize); + +} + +size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + size_t B = Rand(Size); + while (B < Size && !isdigit(Data[B])) + B++; + if (B == Size) return 0; + size_t E = B; + while (E < Size && isdigit(Data[E])) + E++; + assert(B < E); + // now we have digits in [B, E). + // strtol and friends don't accept non-zero-teminated data, parse it manually. + uint64_t Val = Data[B] - '0'; + for (size_t i = B + 1; i < E; i++) + Val = Val * 10 + Data[i] - '0'; + + // Mutate the integer value. + switch (Rand(5)) { + + case 0: + Val++; + break; + case 1: + Val--; + break; + case 2: + Val /= 2; + break; + case 3: + Val *= 2; + break; + case 4: + Val = Rand(Val * Val); + break; + default: + assert(0); + + } + + // Just replace the bytes with the new ones, don't bother moving bytes. + for (size_t i = B; i < E; i++) { + + size_t Idx = E + B - i - 1; + assert(Idx >= B && Idx < E); + Data[Idx] = (Val % 10) + '0'; + Val /= 10; + + } + + return Size; + +} + +template +size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) { + + if (Size < sizeof(T)) return 0; + size_t Off = Rand(Size - sizeof(T) + 1); + assert(Off + sizeof(T) <= Size); + T Val; + if (Off < 64 && !Rand(4)) { + + Val = Size; + if (Rand.RandBool()) Val = Bswap(Val); + + } else { + + memcpy(&Val, Data + Off, sizeof(Val)); + T Add = Rand(21); + Add -= 10; + if (Rand.RandBool()) + Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes. + else + Val = Val + Add; // Add assuming current endiannes. + if (Add == 0 || Rand.RandBool()) // Maybe negate. + Val = -Val; + + } + + memcpy(Data + Off, &Val, sizeof(Val)); + return Size; + +} + +size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data, + size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + switch (Rand(4)) { + + case 3: + return ChangeBinaryInteger(Data, Size, Rand); + case 2: + return ChangeBinaryInteger(Data, Size, Rand); + case 1: + return ChangeBinaryInteger(Data, Size, Rand); + case 0: + return ChangeBinaryInteger(Data, Size, Rand); + default: + assert(0); + + } + + return 0; + +} + +size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + if (Size == 0) return 0; + if (!CrossOverWith) return 0; + const Unit &O = *CrossOverWith; + if (O.empty()) return 0; + size_t NewSize = 0; + switch (Rand(3)) { + + case 0: + MutateInPlaceHere.resize(MaxSize); + NewSize = CrossOver(Data, Size, O.data(), O.size(), + MutateInPlaceHere.data(), MaxSize); + memcpy(Data, MutateInPlaceHere.data(), NewSize); + break; + case 1: + NewSize = InsertPartOf(O.data(), O.size(), Data, Size, MaxSize); + if (!NewSize) NewSize = CopyPartOf(O.data(), O.size(), Data, Size); + break; + case 2: + NewSize = CopyPartOf(O.data(), O.size(), Data, Size); + break; + default: + assert(0); + + } + + assert(NewSize > 0 && "CrossOver returned empty unit"); + assert(NewSize <= MaxSize && "CrossOver returned overisized unit"); + return NewSize; + +} + +void MutationDispatcher::StartMutationSequence() { + + CurrentMutatorSequence.clear(); + CurrentDictionaryEntrySequence.clear(); + +} + +// Copy successful dictionary entries to PersistentAutoDictionary. +void MutationDispatcher::RecordSuccessfulMutationSequence() { + + for (auto DE : CurrentDictionaryEntrySequence) { + + // PersistentAutoDictionary.AddWithSuccessCountOne(DE); + DE->IncSuccessCount(); + assert(DE->GetW().size()); + // Linear search is fine here as this happens seldom. + if (!PersistentAutoDictionary.ContainsWord(DE->GetW())) + PersistentAutoDictionary.push_back({DE->GetW(), 1}); + + } + +} + +void MutationDispatcher::PrintRecommendedDictionary() { + + Vector V; + for (auto &DE : PersistentAutoDictionary) + if (!ManualDictionary.ContainsWord(DE.GetW())) V.push_back(DE); + if (V.empty()) return; + Printf("###### Recommended dictionary. ######\n"); + for (auto &DE : V) { + + assert(DE.GetW().size()); + Printf("\""); + PrintASCII(DE.GetW(), "\""); + Printf(" # Uses: %zd\n", DE.GetUseCount()); + + } + + Printf("###### End of recommended dictionary. ######\n"); + +} + +void MutationDispatcher::PrintMutationSequence() { + + Printf("MS: %zd ", CurrentMutatorSequence.size()); + for (auto M : CurrentMutatorSequence) + Printf("%s-", M.Name); + if (!CurrentDictionaryEntrySequence.empty()) { + + Printf(" DE: "); + for (auto DE : CurrentDictionaryEntrySequence) { + + Printf("\""); + PrintASCII(DE->GetW(), "\"-"); + + } + + } + +} + +std::string MutationDispatcher::MutationSequence() { + + std::string MS; + for (auto M : CurrentMutatorSequence) { + + MS += M.Name; + MS += "-"; + + } + + return MS; + +} + +size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) { + + return MutateImpl(Data, Size, MaxSize, Mutators); + +} + +size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size, + size_t MaxSize) { + + return MutateImpl(Data, Size, MaxSize, DefaultMutators); + +} + +// Mutates Data in place, returns new size. +size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size, + size_t MaxSize, + Vector &Mutators) { + + assert(MaxSize > 0); + // Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize), + // in which case they will return 0. + // Try several times before returning un-mutated data. + for (int Iter = 0; Iter < 100; Iter++) { + + auto M = Mutators[Rand(Mutators.size())]; + size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize); + if (NewSize && NewSize <= MaxSize) { + + if (Options.OnlyASCII) ToASCII(Data, NewSize); + CurrentMutatorSequence.push_back(M); + return NewSize; + + } + + } + + *Data = ' '; + return 1; // Fallback, should not happen frequently. + +} + +// Mask represents the set of Data bytes that are worth mutating. +size_t MutationDispatcher::MutateWithMask(uint8_t *Data, size_t Size, + size_t MaxSize, + const Vector &Mask) { + + size_t MaskedSize = std::min(Size, Mask.size()); + // * Copy the worthy bytes into a temporary array T + // * Mutate T + // * Copy T back. + // This is totally unoptimized. + auto &T = MutateWithMaskTemp; + if (T.size() < Size) T.resize(Size); + size_t OneBits = 0; + for (size_t I = 0; I < MaskedSize; I++) + if (Mask[I]) T[OneBits++] = Data[I]; + + if (!OneBits) return 0; + assert(!T.empty()); + size_t NewSize = Mutate(T.data(), OneBits, OneBits); + assert(NewSize <= OneBits); + (void)NewSize; + // Even if NewSize < OneBits we still use all OneBits bytes. + for (size_t I = 0, J = 0; I < MaskedSize; I++) + if (Mask[I]) Data[I] = T[J++]; + return Size; + +} + +void MutationDispatcher::AddWordToManualDictionary(const Word &W) { + + ManualDictionary.push_back({W, std::numeric_limits::max()}); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerMutate.h b/custom_mutators/libfuzzer/FuzzerMutate.h new file mode 100644 index 00000000..3ce3159f --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerMutate.h @@ -0,0 +1,158 @@ +//===- FuzzerMutate.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::MutationDispatcher +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_MUTATE_H +#define LLVM_FUZZER_MUTATE_H + +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerOptions.h" +#include "FuzzerRandom.h" + +namespace fuzzer { + +class MutationDispatcher { +public: + MutationDispatcher(Random &Rand, const FuzzingOptions &Options); + ~MutationDispatcher() {} + /// Indicate that we are about to start a new sequence of mutations. + void StartMutationSequence(); + /// Print the current sequence of mutations. + void PrintMutationSequence(); + /// Return the current sequence of mutations. + std::string MutationSequence(); + /// Indicate that the current sequence of mutations was successful. + void RecordSuccessfulMutationSequence(); + /// Mutates data by invoking user-provided mutator. + size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by invoking user-provided crossover. + size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by shuffling bytes. + size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by erasing bytes. + size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by inserting a byte. + size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by inserting several repeated bytes. + size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by chanding one byte. + size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by chanding one bit. + size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by copying/inserting a part of data into a different place. + size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Mutates data by adding a word from the manual dictionary. + size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size, + size_t MaxSize); + + /// Mutates data by adding a word from the TORC. + size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Mutates data by adding a word from the persistent automatic dictionary. + size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size, + size_t MaxSize); + + /// Tries to find an ASCII integer in Data, changes it to another ASCII int. + size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize); + /// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways. + size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize); + + /// CrossOver Data with CrossOverWith. + size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Applies one of the configured mutations. + /// Returns the new size of data which could be up to MaxSize. + size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Applies one of the configured mutations to the bytes of Data + /// that have '1' in Mask. + /// Mask.size() should be >= Size. + size_t MutateWithMask(uint8_t *Data, size_t Size, size_t MaxSize, + const Vector &Mask); + + /// Applies one of the default mutations. Provided as a service + /// to mutation authors. + size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Creates a cross-over of two pieces of Data, returns its size. + size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2, + size_t Size2, uint8_t *Out, size_t MaxOutSize); + + void AddWordToManualDictionary(const Word &W); + + void PrintRecommendedDictionary(); + + void SetCrossOverWith(const Unit *U) { CrossOverWith = U; } + + Random &GetRand() { return Rand; } + + private: + struct Mutator { + size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max); + const char *Name; + }; + + size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size, + size_t MaxSize); + size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize, + Vector &Mutators); + + size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To, + size_t ToSize, size_t MaxToSize); + size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To, + size_t ToSize); + size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize, + DictionaryEntry &DE); + + template + DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2, + const uint8_t *Data, size_t Size); + DictionaryEntry MakeDictionaryEntryFromCMP(const Word &Arg1, const Word &Arg2, + const uint8_t *Data, size_t Size); + DictionaryEntry MakeDictionaryEntryFromCMP(const void *Arg1, const void *Arg2, + const void *Arg1Mutation, + const void *Arg2Mutation, + size_t ArgSize, + const uint8_t *Data, size_t Size); + + Random &Rand; + const FuzzingOptions Options; + + // Dictionary provided by the user via -dict=DICT_FILE. + Dictionary ManualDictionary; + // Temporary dictionary modified by the fuzzer itself, + // recreated periodically. + Dictionary TempAutoDictionary; + // Persistent dictionary modified by the fuzzer, consists of + // entries that led to successful discoveries in the past mutations. + Dictionary PersistentAutoDictionary; + + Vector CurrentDictionaryEntrySequence; + + static const size_t kCmpDictionaryEntriesDequeSize = 16; + DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize]; + size_t CmpDictionaryEntriesDequeIdx = 0; + + const Unit *CrossOverWith = nullptr; + Vector MutateInPlaceHere; + Vector MutateWithMaskTemp; + // CustomCrossOver needs its own buffer as a custom implementation may call + // LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere. + Vector CustomCrossOverInPlaceHere; + + Vector Mutators; + Vector DefaultMutators; + Vector CurrentMutatorSequence; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_MUTATE_H diff --git a/custom_mutators/libfuzzer/FuzzerOptions.h b/custom_mutators/libfuzzer/FuzzerOptions.h new file mode 100644 index 00000000..706e1c64 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerOptions.h @@ -0,0 +1,90 @@ +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::FuzzingOptions +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_OPTIONS_H +#define LLVM_FUZZER_OPTIONS_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +struct FuzzingOptions { + int Verbosity = 1; + size_t MaxLen = 0; + size_t LenControl = 1000; + bool KeepSeed = false; + int UnitTimeoutSec = 300; + int TimeoutExitCode = 70; + int OOMExitCode = 71; + int InterruptExitCode = 72; + int ErrorExitCode = 77; + bool IgnoreTimeouts = true; + bool IgnoreOOMs = true; + bool IgnoreCrashes = false; + int MaxTotalTimeSec = 0; + int RssLimitMb = 0; + int MallocLimitMb = 0; + bool DoCrossOver = true; + bool CrossOverUniformDist = false; + int MutateDepth = 5; + bool ReduceDepth = false; + bool UseCounters = false; + bool UseMemmem = true; + bool UseCmp = false; + int UseValueProfile = false; + bool Shrink = false; + bool ReduceInputs = false; + int ReloadIntervalSec = 1; + bool ShuffleAtStartUp = true; + bool PreferSmall = true; + size_t MaxNumberOfRuns = -1L; + int ReportSlowUnits = 10; + bool OnlyASCII = false; + bool Entropic = false; + size_t EntropicFeatureFrequencyThreshold = 0xFF; + size_t EntropicNumberOfRarestFeatures = 100; + bool EntropicScalePerExecTime = false; + std::string OutputCorpus; + std::string ArtifactPrefix = "./"; + std::string ExactArtifactPath; + std::string ExitOnSrcPos; + std::string ExitOnItem; + std::string FocusFunction; + std::string DataFlowTrace; + std::string CollectDataFlow; + std::string FeaturesDir; + std::string MutationGraphFile; + std::string StopFile; + bool SaveArtifacts = true; + bool PrintNEW = true; // Print a status line when new units are found; + bool PrintNewCovPcs = false; + int PrintNewCovFuncs = 0; + bool PrintFinalStats = false; + bool PrintCorpusStats = false; + bool PrintCoverage = false; + bool DumpCoverage = false; + bool DetectLeaks = true; + int PurgeAllocatorIntervalSec = 1; + int TraceMalloc = 0; + bool HandleAbrt = false; + bool HandleAlrm = false; + bool HandleBus = false; + bool HandleFpe = false; + bool HandleIll = false; + bool HandleInt = false; + bool HandleSegv = false; + bool HandleTerm = false; + bool HandleXfsz = false; + bool HandleUsr1 = false; + bool HandleUsr2 = false; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_OPTIONS_H diff --git a/custom_mutators/libfuzzer/FuzzerPlatform.h b/custom_mutators/libfuzzer/FuzzerPlatform.h new file mode 100644 index 00000000..8befdb88 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerPlatform.h @@ -0,0 +1,163 @@ +//===-- FuzzerPlatform.h --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Common platform macros. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_PLATFORM_H +#define LLVM_FUZZER_PLATFORM_H + +// Platform detection. +#ifdef __linux__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 1 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __APPLE__ +#define LIBFUZZER_APPLE 1 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __NetBSD__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 1 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __FreeBSD__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 1 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __OpenBSD__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 1 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif _WIN32 +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 1 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __Fuchsia__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 1 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __EMSCRIPTEN__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 1 +#else +#error "Support for your platform has not been implemented" +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +// MSVC compiler is being used. +#define LIBFUZZER_MSVC 1 +#else +#define LIBFUZZER_MSVC 0 +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#define LIBFUZZER_POSIX \ + (LIBFUZZER_APPLE || LIBFUZZER_LINUX || LIBFUZZER_NETBSD || \ + LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN) + +#ifdef __x86_64 +#if __has_attribute(target) +#define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt"))) +#else +#define ATTRIBUTE_TARGET_POPCNT +#endif +#else +#define ATTRIBUTE_TARGET_POPCNT +#endif + +#ifdef __clang__ // avoid gcc warning. +#if __has_attribute(no_sanitize) +#define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +#else +#define ATTRIBUTE_NO_SANITIZE_MEMORY +#endif +#define ALWAYS_INLINE __attribute__((always_inline)) +#else +#define ATTRIBUTE_NO_SANITIZE_MEMORY +#define ALWAYS_INLINE +#endif // __clang__ + +#if LIBFUZZER_WINDOWS +#define ATTRIBUTE_NO_SANITIZE_ADDRESS +#else +#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +#endif + +#if LIBFUZZER_WINDOWS +#define ATTRIBUTE_ALIGNED(X) __declspec(align(X)) +#define ATTRIBUTE_INTERFACE __declspec(dllexport) +// This is used for __sancov_lowest_stack which is needed for +// -fsanitize-coverage=stack-depth. That feature is not yet available on +// Windows, so make the symbol static to avoid linking errors. +#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC static +#define ATTRIBUTE_NOINLINE __declspec(noinline) +#else +#define ATTRIBUTE_ALIGNED(X) __attribute__((aligned(X))) +#define ATTRIBUTE_INTERFACE __attribute__((visibility("default"))) +#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \ + ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local + +#define ATTRIBUTE_NOINLINE __attribute__((noinline)) +#endif + +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_ADDRESS +#elif __has_feature(memory_sanitizer) +#define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_MEMORY +#else +#define ATTRIBUTE_NO_SANITIZE_ALL +#endif +#else +#define ATTRIBUTE_NO_SANITIZE_ALL +#endif + +#endif // LLVM_FUZZER_PLATFORM_H diff --git a/custom_mutators/libfuzzer/FuzzerRandom.h b/custom_mutators/libfuzzer/FuzzerRandom.h new file mode 100644 index 00000000..659283ee --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerRandom.h @@ -0,0 +1,38 @@ +//===- FuzzerRandom.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::Random +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_RANDOM_H +#define LLVM_FUZZER_RANDOM_H + +#include + +namespace fuzzer { +class Random : public std::minstd_rand { + public: + Random(unsigned int seed) : std::minstd_rand(seed) {} + result_type operator()() { return this->std::minstd_rand::operator()(); } + size_t Rand() { return this->operator()(); } + size_t RandBool() { return Rand() % 2; } + size_t SkewTowardsLast(size_t n) { + size_t T = this->operator()(n * n); + size_t Res = sqrt(T); + return Res; + } + size_t operator()(size_t n) { return n ? Rand() % n : 0; } + intptr_t operator()(intptr_t From, intptr_t To) { + assert(From < To); + intptr_t RangeSize = To - From + 1; + return operator()(RangeSize) + From; + } +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_RANDOM_H diff --git a/custom_mutators/libfuzzer/FuzzerSHA1.cpp b/custom_mutators/libfuzzer/FuzzerSHA1.cpp new file mode 100644 index 00000000..0a58b661 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerSHA1.cpp @@ -0,0 +1,269 @@ +//===- FuzzerSHA1.h - Private copy of the SHA1 implementation ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This code is taken from public domain +// (http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c) +// and modified by adding anonymous namespace, adding an interface +// function fuzzer::ComputeSHA1() and removing unnecessary code. +// +// lib/Fuzzer can not use SHA1 implementation from openssl because +// openssl may not be available and because we may be fuzzing openssl itself. +// For the same reason we do not want to depend on SHA1 from LLVM tree. +//===----------------------------------------------------------------------===// + +#include "FuzzerSHA1.h" +#include "FuzzerDefs.h" +#include "FuzzerPlatform.h" + +/* This code is public-domain - it is based on libcrypt + * placed in the public domain by Wei Dai and other contributors. + */ + +#include +#include +#include +#include + +namespace { // Added for LibFuzzer + +#ifdef __BIG_ENDIAN__ + #define SHA_BIG_ENDIAN +// Windows is always little endian and MSVC doesn't have +#elif defined __LITTLE_ENDIAN__ || LIBFUZZER_WINDOWS +/* override */ +#elif defined __BYTE_ORDER + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define SHA_BIG_ENDIAN + #endif +#else // ! defined __LITTLE_ENDIAN__ + #include // machine/endian.h + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define SHA_BIG_ENDIAN + #endif +#endif + +/* header */ + +#define HASH_LENGTH 20 +#define BLOCK_LENGTH 64 + +typedef struct sha1nfo { + + uint32_t buffer[BLOCK_LENGTH / 4]; + uint32_t state[HASH_LENGTH / 4]; + uint32_t byteCount; + uint8_t bufferOffset; + uint8_t keyBuffer[BLOCK_LENGTH]; + uint8_t innerHash[HASH_LENGTH]; + +} sha1nfo; + +/* public API - prototypes - TODO: doxygen*/ + +/** + */ +void sha1_init(sha1nfo *s); +/** + */ +void sha1_writebyte(sha1nfo *s, uint8_t data); +/** + */ +void sha1_write(sha1nfo *s, const char *data, size_t len); +/** + */ +uint8_t *sha1_result(sha1nfo *s); + +/* code */ +#define SHA1_K0 0x5a827999 +#define SHA1_K20 0x6ed9eba1 +#define SHA1_K40 0x8f1bbcdc +#define SHA1_K60 0xca62c1d6 + +void sha1_init(sha1nfo *s) { + + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->state[4] = 0xc3d2e1f0; + s->byteCount = 0; + s->bufferOffset = 0; + +} + +uint32_t sha1_rol32(uint32_t number, uint8_t bits) { + + return ((number << bits) | (number >> (32 - bits))); + +} + +void sha1_hashBlock(sha1nfo *s) { + + uint8_t i; + uint32_t a, b, c, d, e, t; + + a = s->state[0]; + b = s->state[1]; + c = s->state[2]; + d = s->state[3]; + e = s->state[4]; + for (i = 0; i < 80; i++) { + + if (i >= 16) { + + t = s->buffer[(i + 13) & 15] ^ s->buffer[(i + 8) & 15] ^ + s->buffer[(i + 2) & 15] ^ s->buffer[i & 15]; + s->buffer[i & 15] = sha1_rol32(t, 1); + + } + + if (i < 20) { + + t = (d ^ (b & (c ^ d))) + SHA1_K0; + + } else if (i < 40) { + + t = (b ^ c ^ d) + SHA1_K20; + + } else if (i < 60) { + + t = ((b & c) | (d & (b | c))) + SHA1_K40; + + } else { + + t = (b ^ c ^ d) + SHA1_K60; + + } + + t += sha1_rol32(a, 5) + e + s->buffer[i & 15]; + e = d; + d = c; + c = sha1_rol32(b, 30); + b = a; + a = t; + + } + + s->state[0] += a; + s->state[1] += b; + s->state[2] += c; + s->state[3] += d; + s->state[4] += e; + +} + +void sha1_addUncounted(sha1nfo *s, uint8_t data) { + + uint8_t *const b = (uint8_t *)s->buffer; +#ifdef SHA_BIG_ENDIAN + b[s->bufferOffset] = data; +#else + b[s->bufferOffset ^ 3] = data; +#endif + s->bufferOffset++; + if (s->bufferOffset == BLOCK_LENGTH) { + + sha1_hashBlock(s); + s->bufferOffset = 0; + + } + +} + +void sha1_writebyte(sha1nfo *s, uint8_t data) { + + ++s->byteCount; + sha1_addUncounted(s, data); + +} + +void sha1_write(sha1nfo *s, const char *data, size_t len) { + + for (; len--;) + sha1_writebyte(s, (uint8_t)*data++); + +} + +void sha1_pad(sha1nfo *s) { + + // Implement SHA-1 padding (fips180-2 §5.1.1) + + // Pad with 0x80 followed by 0x00 until the end of the block + sha1_addUncounted(s, 0x80); + while (s->bufferOffset != 56) + sha1_addUncounted(s, 0x00); + + // Append length in the last 8 bytes + sha1_addUncounted(s, 0); // We're only using 32 bit lengths + sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths + sha1_addUncounted(s, 0); // So zero pad the top bits + sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8 + sha1_addUncounted( + s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as + sha1_addUncounted(s, s->byteCount >> 13); // byte. + sha1_addUncounted(s, s->byteCount >> 5); + sha1_addUncounted(s, s->byteCount << 3); + +} + +uint8_t *sha1_result(sha1nfo *s) { + + // Pad to complete the last block + sha1_pad(s); + +#ifndef SHA_BIG_ENDIAN + // Swap byte order back + int i; + for (i = 0; i < 5; i++) { + + s->state[i] = (((s->state[i]) << 24) & 0xff000000) | + (((s->state[i]) << 8) & 0x00ff0000) | + (((s->state[i]) >> 8) & 0x0000ff00) | + (((s->state[i]) >> 24) & 0x000000ff); + + } + +#endif + + // Return pointer to hash (20 characters) + return (uint8_t *)s->state; + +} + +} // namespace + +namespace fuzzer { + +// The rest is added for LibFuzzer +void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out) { + + sha1nfo s; + sha1_init(&s); + sha1_write(&s, (const char *)Data, Len); + memcpy(Out, sha1_result(&s), HASH_LENGTH); + +} + +std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]) { + + std::stringstream SS; + for (int i = 0; i < kSHA1NumBytes; i++) + SS << std::hex << std::setfill('0') << std::setw(2) << (unsigned)Sha1[i]; + return SS.str(); + +} + +std::string Hash(const Unit &U) { + + uint8_t Hash[kSHA1NumBytes]; + ComputeSHA1(U.data(), U.size(), Hash); + return Sha1ToString(Hash); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerSHA1.h b/custom_mutators/libfuzzer/FuzzerSHA1.h new file mode 100644 index 00000000..05cbacda --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerSHA1.h @@ -0,0 +1,32 @@ +//===- FuzzerSHA1.h - Internal header for the SHA1 utils --------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// SHA1 utils. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_SHA1_H +#define LLVM_FUZZER_SHA1_H + +#include "FuzzerDefs.h" +#include +#include + +namespace fuzzer { + +// Private copy of SHA1 implementation. +static const int kSHA1NumBytes = 20; + +// Computes SHA1 hash of 'Len' bytes in 'Data', writes kSHA1NumBytes to 'Out'. +void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out); + +std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]); + +std::string Hash(const Unit &U); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_SHA1_H diff --git a/custom_mutators/libfuzzer/FuzzerTracePC.cpp b/custom_mutators/libfuzzer/FuzzerTracePC.cpp new file mode 100644 index 00000000..1177325e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerTracePC.cpp @@ -0,0 +1,819 @@ +//===- FuzzerTracePC.cpp - PC tracing--------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Trace PCs. +// This module implements __sanitizer_cov_trace_pc_guard[_init], +// the callback required for -fsanitize-coverage=trace-pc-guard instrumentation. +// +//===----------------------------------------------------------------------===// + +#include "FuzzerTracePC.h" +#include "FuzzerBuiltins.h" +#include "FuzzerBuiltinsMsvc.h" +#include "FuzzerCorpus.h" +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerPlatform.h" +#include "FuzzerUtil.h" +#include "FuzzerValueBitMap.h" +#include + +// Used by -fsanitize-coverage=stack-depth to track stack depth +ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC uintptr_t __sancov_lowest_stack; + +namespace fuzzer { + +TracePC TPC; + +size_t TracePC::GetTotalPCCoverage() { + + return ObservedPCs.size(); + +} + +void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) { + + if (Start == Stop) return; + if (NumModules && Modules[NumModules - 1].Start() == Start) return; + assert(NumModules < sizeof(Modules) / sizeof(Modules[0])); + auto & M = Modules[NumModules++]; + uint8_t *AlignedStart = RoundUpByPage(Start); + uint8_t *AlignedStop = RoundDownByPage(Stop); + size_t NumFullPages = AlignedStop > AlignedStart + ? (AlignedStop - AlignedStart) / PageSize() + : 0; + bool NeedFirst = Start < AlignedStart || !NumFullPages; + bool NeedLast = Stop > AlignedStop && AlignedStop >= AlignedStart; + M.NumRegions = NumFullPages + NeedFirst + NeedLast; + ; + assert(M.NumRegions > 0); + M.Regions = new Module::Region[M.NumRegions]; + assert(M.Regions); + size_t R = 0; + if (NeedFirst) + M.Regions[R++] = {Start, std::min(Stop, AlignedStart), true, false}; + for (uint8_t *P = AlignedStart; P < AlignedStop; P += PageSize()) + M.Regions[R++] = {P, P + PageSize(), true, true}; + if (NeedLast) M.Regions[R++] = {AlignedStop, Stop, true, false}; + assert(R == M.NumRegions); + assert(M.Size() == (size_t)(Stop - Start)); + assert(M.Stop() == Stop); + assert(M.Start() == Start); + NumInline8bitCounters += M.Size(); + +} + +void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) { + + const PCTableEntry *B = reinterpret_cast(Start); + const PCTableEntry *E = reinterpret_cast(Stop); + if (NumPCTables && ModulePCTable[NumPCTables - 1].Start == B) return; + assert(NumPCTables < sizeof(ModulePCTable) / sizeof(ModulePCTable[0])); + ModulePCTable[NumPCTables++] = {B, E}; + NumPCsInPCTables += E - B; + +} + +void TracePC::PrintModuleInfo() { + + if (NumModules) { + + Printf("INFO: Loaded %zd modules (%zd inline 8-bit counters): ", + NumModules, NumInline8bitCounters); + for (size_t i = 0; i < NumModules; i++) + Printf("%zd [%p, %p), ", Modules[i].Size(), Modules[i].Start(), + Modules[i].Stop()); + Printf("\n"); + + } + + if (NumPCTables) { + + Printf("INFO: Loaded %zd PC tables (%zd PCs): ", NumPCTables, + NumPCsInPCTables); + for (size_t i = 0; i < NumPCTables; i++) { + + Printf("%zd [%p,%p), ", ModulePCTable[i].Stop - ModulePCTable[i].Start, + ModulePCTable[i].Start, ModulePCTable[i].Stop); + + } + + Printf("\n"); + + if (NumInline8bitCounters && NumInline8bitCounters != NumPCsInPCTables) { + + Printf( + "ERROR: The size of coverage PC tables does not match the\n" + "number of instrumented PCs. This might be a compiler bug,\n" + "please contact the libFuzzer developers.\n" + "Also check https://bugs.llvm.org/show_bug.cgi?id=34636\n" + "for possible workarounds (tl;dr: don't use the old GNU ld)\n"); + _Exit(1); + + } + + } + + if (size_t NumExtraCounters = ExtraCountersEnd() - ExtraCountersBegin()) + Printf("INFO: %zd Extra Counters\n", NumExtraCounters); + +} + +ATTRIBUTE_NO_SANITIZE_ALL +void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) { + + const uintptr_t kBits = 12; + const uintptr_t kMask = (1 << kBits) - 1; + uintptr_t Idx = (Caller & kMask) | ((Callee & kMask) << kBits); + ValueProfileMap.AddValueModPrime(Idx); + +} + +/// \return the address of the previous instruction. +/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.h` +inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) { + +#if defined(__arm__) + // T32 (Thumb) branch instructions might be 16 or 32 bit long, + // so we return (pc-2) in that case in order to be safe. + // For A32 mode we return (pc-4) because all instructions are 32 bit long. + return (PC - 3) & (~1); +#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__aarch64__) + // PCs are always 4 byte aligned. + return PC - 4; +#elif defined(__sparc__) || defined(__mips__) + return PC - 8; +#else + return PC - 1; +#endif + +} + +/// \return the address of the next instruction. +/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.cpp` +ALWAYS_INLINE uintptr_t TracePC::GetNextInstructionPc(uintptr_t PC) { + +#if defined(__mips__) + return PC + 8; +#elif defined(__powerpc__) || defined(__sparc__) || defined(__arm__) || \ + defined(__aarch64__) + return PC + 4; +#else + return PC + 1; +#endif + +} + +void TracePC::UpdateObservedPCs() { + + Vector CoveredFuncs; + auto ObservePC = [&](const PCTableEntry *TE) { + + if (ObservedPCs.insert(TE).second && DoPrintNewPCs) { + + PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", + GetNextInstructionPc(TE->PC)); + Printf("\n"); + + } + + }; + + auto Observe = [&](const PCTableEntry *TE) { + + if (PcIsFuncEntry(TE)) + if (++ObservedFuncs[TE->PC] == 1 && NumPrintNewFuncs) + CoveredFuncs.push_back(TE->PC); + ObservePC(TE); + + }; + + if (NumPCsInPCTables) { + + if (NumInline8bitCounters == NumPCsInPCTables) { + + for (size_t i = 0; i < NumModules; i++) { + + auto &M = Modules[i]; + assert(M.Size() == + (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start)); + for (size_t r = 0; r < M.NumRegions; r++) { + + auto &R = M.Regions[r]; + if (!R.Enabled) continue; + for (uint8_t *P = R.Start; P < R.Stop; P++) + if (*P) Observe(&ModulePCTable[i].Start[M.Idx(P)]); + + } + + } + + } + + } + + for (size_t i = 0, N = Min(CoveredFuncs.size(), NumPrintNewFuncs); i < N; + i++) { + + Printf("\tNEW_FUNC[%zd/%zd]: ", i + 1, CoveredFuncs.size()); + PrintPC("%p %F %L", "%p", GetNextInstructionPc(CoveredFuncs[i])); + Printf("\n"); + + } + +} + +uintptr_t TracePC::PCTableEntryIdx(const PCTableEntry *TE) { + + size_t TotalTEs = 0; + for (size_t i = 0; i < NumPCTables; i++) { + + auto &M = ModulePCTable[i]; + if (TE >= M.Start && TE < M.Stop) return TotalTEs + TE - M.Start; + TotalTEs += M.Stop - M.Start; + + } + + assert(0); + return 0; + +} + +const TracePC::PCTableEntry *TracePC::PCTableEntryByIdx(uintptr_t Idx) { + + for (size_t i = 0; i < NumPCTables; i++) { + + auto & M = ModulePCTable[i]; + size_t Size = M.Stop - M.Start; + if (Idx < Size) return &M.Start[Idx]; + Idx -= Size; + + } + + return nullptr; + +} + +static std::string GetModuleName(uintptr_t PC) { + + char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++? + void *OffsetRaw = nullptr; + if (!EF->__sanitizer_get_module_and_offset_for_pc( + reinterpret_cast(PC), ModulePathRaw, sizeof(ModulePathRaw), + &OffsetRaw)) + return ""; + return ModulePathRaw; + +} + +template +void TracePC::IterateCoveredFunctions(CallBack CB) { + + for (size_t i = 0; i < NumPCTables; i++) { + + auto &M = ModulePCTable[i]; + assert(M.Start < M.Stop); + auto ModuleName = GetModuleName(M.Start->PC); + for (auto NextFE = M.Start; NextFE < M.Stop;) { + + auto FE = NextFE; + assert(PcIsFuncEntry(FE) && "Not a function entry point"); + do { + + NextFE++; + + } while (NextFE < M.Stop && !(PcIsFuncEntry(NextFE))); + + CB(FE, NextFE, ObservedFuncs[FE->PC]); + + } + + } + +} + +void TracePC::SetFocusFunction(const std::string &FuncName) { + + // This function should be called once. + assert(!FocusFunctionCounterPtr); + // "auto" is not a valid function name. If this function is called with "auto" + // that means the auto focus functionality failed. + if (FuncName.empty() || FuncName == "auto") return; + for (size_t M = 0; M < NumModules; M++) { + + auto & PCTE = ModulePCTable[M]; + size_t N = PCTE.Stop - PCTE.Start; + for (size_t I = 0; I < N; I++) { + + if (!(PcIsFuncEntry(&PCTE.Start[I]))) continue; // not a function entry. + auto Name = DescribePC("%F", GetNextInstructionPc(PCTE.Start[I].PC)); + if (Name[0] == 'i' && Name[1] == 'n' && Name[2] == ' ') + Name = Name.substr(3, std::string::npos); + if (FuncName != Name) continue; + Printf("INFO: Focus function is set to '%s'\n", Name.c_str()); + FocusFunctionCounterPtr = Modules[M].Start() + I; + return; + + } + + } + + Printf( + "ERROR: Failed to set focus function. Make sure the function name is " + "valid (%s) and symbolization is enabled.\n", + FuncName.c_str()); + exit(1); + +} + +bool TracePC::ObservedFocusFunction() { + + return FocusFunctionCounterPtr && *FocusFunctionCounterPtr; + +} + +void TracePC::PrintCoverage() { + + if (!EF->__sanitizer_symbolize_pc || + !EF->__sanitizer_get_module_and_offset_for_pc) { + + Printf( + "INFO: __sanitizer_symbolize_pc or " + "__sanitizer_get_module_and_offset_for_pc is not available," + " not printing coverage\n"); + return; + + } + + Printf("COVERAGE:\n"); + auto CoveredFunctionCallback = [&](const PCTableEntry *First, + const PCTableEntry *Last, + uintptr_t Counter) { + + assert(First < Last); + auto VisualizePC = GetNextInstructionPc(First->PC); + std::string FileStr = DescribePC("%s", VisualizePC); + if (!IsInterestingCoverageFile(FileStr)) return; + std::string FunctionStr = DescribePC("%F", VisualizePC); + if (FunctionStr.find("in ") == 0) FunctionStr = FunctionStr.substr(3); + std::string LineStr = DescribePC("%l", VisualizePC); + size_t NumEdges = Last - First; + Vector UncoveredPCs; + for (auto TE = First; TE < Last; TE++) + if (!ObservedPCs.count(TE)) UncoveredPCs.push_back(TE->PC); + Printf("%sCOVERED_FUNC: hits: %zd", Counter ? "" : "UN", Counter); + Printf(" edges: %zd/%zd", NumEdges - UncoveredPCs.size(), NumEdges); + Printf(" %s %s:%s\n", FunctionStr.c_str(), FileStr.c_str(), + LineStr.c_str()); + if (Counter) + for (auto PC : UncoveredPCs) + Printf(" UNCOVERED_PC: %s\n", + DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str()); + + }; + + IterateCoveredFunctions(CoveredFunctionCallback); + +} + +// Value profile. +// We keep track of various values that affect control flow. +// These values are inserted into a bit-set-based hash map. +// Every new bit in the map is treated as a new coverage. +// +// For memcmp/strcmp/etc the interesting value is the length of the common +// prefix of the parameters. +// For cmp instructions the interesting value is a XOR of the parameters. +// The interesting value is mixed up with the PC and is then added to the map. + +ATTRIBUTE_NO_SANITIZE_ALL +void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, + size_t n, bool StopAtZero) { + + if (!n) return; + size_t Len = std::min(n, Word::GetMaxSize()); + const uint8_t *A1 = reinterpret_cast(s1); + const uint8_t *A2 = reinterpret_cast(s2); + uint8_t B1[Word::kMaxSize]; + uint8_t B2[Word::kMaxSize]; + // Copy the data into locals in this non-msan-instrumented function + // to avoid msan complaining further. + size_t Hash = 0; // Compute some simple hash of both strings. + for (size_t i = 0; i < Len; i++) { + + B1[i] = A1[i]; + B2[i] = A2[i]; + size_t T = B1[i]; + Hash ^= (T << 8) | B2[i]; + + } + + size_t I = 0; + uint8_t HammingDistance = 0; + for (; I < Len; I++) { + + if (B1[I] != B2[I] || (StopAtZero && B1[I] == 0)) { + + HammingDistance = Popcountll(B1[I] ^ B2[I]); + break; + + } + + } + + size_t PC = reinterpret_cast(caller_pc); + size_t Idx = (PC & 4095) | (I << 12); + Idx += HammingDistance; + ValueProfileMap.AddValue(Idx); + TORCW.Insert(Idx ^ Hash, Word(B1, Len), Word(B2, Len)); + +} + +template +ATTRIBUTE_TARGET_POPCNT ALWAYS_INLINE ATTRIBUTE_NO_SANITIZE_ALL void +TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) { + + uint64_t ArgXor = Arg1 ^ Arg2; + if (sizeof(T) == 4) + TORC4.Insert(ArgXor, Arg1, Arg2); + else if (sizeof(T) == 8) + TORC8.Insert(ArgXor, Arg1, Arg2); + uint64_t HammingDistance = Popcountll(ArgXor); // [0,64] + uint64_t AbsoluteDistance = (Arg1 == Arg2 ? 0 : Clzll(Arg1 - Arg2) + 1); + ValueProfileMap.AddValue(PC * 128 + HammingDistance); + ValueProfileMap.AddValue(PC * 128 + 64 + AbsoluteDistance); + +} + +static size_t InternalStrnlen(const char *S, size_t MaxLen) { + + size_t Len = 0; + for (; Len < MaxLen && S[Len]; Len++) {} + return Len; + +} + +// Finds min of (strlen(S1), strlen(S2)). +// Needed bacause one of these strings may actually be non-zero terminated. +static size_t InternalStrnlen2(const char *S1, const char *S2) { + + size_t Len = 0; + for (; S1[Len] && S2[Len]; Len++) {} + return Len; + +} + +void TracePC::ClearInlineCounters() { + + IterateCounterRegions([](const Module::Region &R) { + + if (R.Enabled) memset(R.Start, 0, R.Stop - R.Start); + + }); + +} + +ATTRIBUTE_NO_SANITIZE_ALL +void TracePC::RecordInitialStack() { + + int stack; + __sancov_lowest_stack = InitialStack = reinterpret_cast(&stack); + +} + +uintptr_t TracePC::GetMaxStackOffset() const { + + return InitialStack - __sancov_lowest_stack; // Stack grows down + +} + +void WarnAboutDeprecatedInstrumentation(const char *flag) { + + // Use RawPrint because Printf cannot be used on Windows before OutputFile is + // initialized. + RawPrint(flag); + RawPrint( + " is no longer supported by libFuzzer.\n" + "Please either migrate to a compiler that supports -fsanitize=fuzzer\n" + "or use an older version of libFuzzer\n"); + exit(1); + +} + +} // namespace fuzzer + +extern "C" { + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) { + + fuzzer::WarnAboutDeprecatedInstrumentation( + "-fsanitize-coverage=trace-pc-guard"); + +} + +// Best-effort support for -fsanitize-coverage=trace-pc, which is available +// in both Clang and GCC. +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +void __sanitizer_cov_trace_pc() { + + fuzzer::WarnAboutDeprecatedInstrumentation("-fsanitize-coverage=trace-pc"); + +} + +ATTRIBUTE_INTERFACE +void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) { + + fuzzer::WarnAboutDeprecatedInstrumentation( + "-fsanitize-coverage=trace-pc-guard"); + +} + +ATTRIBUTE_INTERFACE +void __sanitizer_cov_8bit_counters_init(uint8_t *Start, uint8_t *Stop) { + + fuzzer::TPC.HandleInline8bitCountersInit(Start, Stop); + +} + +ATTRIBUTE_INTERFACE +void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg, + const uintptr_t *pcs_end) { + + fuzzer::TPC.HandlePCsInit(pcs_beg, pcs_end); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCallerCallee(PC, Callee); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +// Now the __sanitizer_cov_trace_const_cmp[1248] callbacks just mimic +// the behaviour of __sanitizer_cov_trace_cmp[1248] ones. This, however, +// should be changed later to make full use of instrumentation. +void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_const_cmp1(uint8_t Arg1, uint8_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { + + uint64_t N = Cases[0]; + uint64_t ValSizeInBits = Cases[1]; + uint64_t *Vals = Cases + 2; + // Skip the most common and the most boring case: all switch values are small. + // We may want to skip this at compile-time, but it will make the + // instrumentation less general. + if (Vals[N - 1] < 256) return; + // Also skip small inputs values, they won't give good signal. + if (Val < 256) return; + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + size_t i; + uint64_t Smaller = 0; + uint64_t Larger = ~(uint64_t)0; + // Find two switch values such that Smaller < Val < Larger. + // Use 0 and 0xfff..f as the defaults. + for (i = 0; i < N; i++) { + + if (Val < Vals[i]) { + + Larger = Vals[i]; + break; + + } + + if (Val > Vals[i]) Smaller = Vals[i]; + + } + + // Apply HandleCmp to {Val,Smaller} and {Val, Larger}, + // use i as the PC modifier for HandleCmp. + if (ValSizeInBits == 16) { + + fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast(Val), + (uint16_t)(Smaller)); + fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast(Val), + (uint16_t)(Larger)); + + } else if (ValSizeInBits == 32) { + + fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast(Val), + (uint32_t)(Smaller)); + fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast(Val), + (uint32_t)(Larger)); + + } else { + + fuzzer::TPC.HandleCmp(PC + 2 * i, Val, Smaller); + fuzzer::TPC.HandleCmp(PC + 2 * i + 1, Val, Larger); + + } + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_div4(uint32_t Val) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Val, (uint32_t)0); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_div8(uint64_t Val) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Val, (uint64_t)0); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_gep(uintptr_t Idx) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Idx, (uintptr_t)0); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2, + size_t n, int result) { + + if (!fuzzer::RunningUserCallback) return; + if (result == 0) return; // No reason to mutate. + if (n <= 1) return; // Not interesting. + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/ false); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2, + size_t n, int result) { + + if (!fuzzer::RunningUserCallback) return; + if (result == 0) return; // No reason to mutate. + size_t Len1 = fuzzer::InternalStrnlen(s1, n); + size_t Len2 = fuzzer::InternalStrnlen(s2, n); + n = std::min(n, Len1); + n = std::min(n, Len2); + if (n <= 1) return; // Not interesting. + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/ true); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2, + int result) { + + if (!fuzzer::RunningUserCallback) return; + if (result == 0) return; // No reason to mutate. + size_t N = fuzzer::InternalStrnlen2(s1, s2); + if (N <= 1) return; // Not interesting. + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, N, /*StopAtZero*/ true); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1, + const char *s2, size_t n, int result) { + + if (!fuzzer::RunningUserCallback) return; + return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1, + const char *s2, int result) { + + if (!fuzzer::RunningUserCallback) return; + return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strstr(void *called_pc, const char *s1, const char *s2, + char *result) { + + if (!fuzzer::RunningUserCallback) return; + fuzzer::TPC.MMT.Add(reinterpret_cast(s2), strlen(s2)); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1, + const char *s2, char *result) { + + if (!fuzzer::RunningUserCallback) return; + fuzzer::TPC.MMT.Add(reinterpret_cast(s2), strlen(s2)); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1, + const void *s2, size_t len2, void *result) { + + if (!fuzzer::RunningUserCallback) return; + fuzzer::TPC.MMT.Add(reinterpret_cast(s2), len2); + +} + +} // extern "C" + diff --git a/custom_mutators/libfuzzer/FuzzerTracePC.h b/custom_mutators/libfuzzer/FuzzerTracePC.h new file mode 100644 index 00000000..4601300c --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerTracePC.h @@ -0,0 +1,291 @@ +//===- FuzzerTracePC.h - Internal header for the Fuzzer ---------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::TracePC +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_TRACE_PC +#define LLVM_FUZZER_TRACE_PC + +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerValueBitMap.h" + +#include +#include + +namespace fuzzer { + +// TableOfRecentCompares (TORC) remembers the most recently performed +// comparisons of type T. +// We record the arguments of CMP instructions in this table unconditionally +// because it seems cheaper this way than to compute some expensive +// conditions inside __sanitizer_cov_trace_cmp*. +// After the unit has been executed we may decide to use the contents of +// this table to populate a Dictionary. +template +struct TableOfRecentCompares { + static const size_t kSize = kSizeT; + struct Pair { + T A, B; + }; + ATTRIBUTE_NO_SANITIZE_ALL + void Insert(size_t Idx, const T &Arg1, const T &Arg2) { + Idx = Idx % kSize; + Table[Idx].A = Arg1; + Table[Idx].B = Arg2; + } + + Pair Get(size_t I) { return Table[I % kSize]; } + + Pair Table[kSize]; +}; + +template +struct MemMemTable { + static const size_t kSize = kSizeT; + Word MemMemWords[kSize]; + Word EmptyWord; + + void Add(const uint8_t *Data, size_t Size) { + if (Size <= 2) return; + Size = std::min(Size, Word::GetMaxSize()); + size_t Idx = SimpleFastHash(Data, Size) % kSize; + MemMemWords[Idx].Set(Data, Size); + } + const Word &Get(size_t Idx) { + for (size_t i = 0; i < kSize; i++) { + const Word &W = MemMemWords[(Idx + i) % kSize]; + if (W.size()) return W; + } + EmptyWord.Set(nullptr, 0); + return EmptyWord; + } +}; + +class TracePC { + public: + void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop); + void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop); + void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee); + template void HandleCmp(uintptr_t PC, T Arg1, T Arg2); + size_t GetTotalPCCoverage(); + void SetUseCounters(bool UC) { UseCounters = UC; } + void SetUseValueProfileMask(uint32_t VPMask) { UseValueProfileMask = VPMask; } + void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; } + void SetPrintNewFuncs(size_t P) { NumPrintNewFuncs = P; } + void UpdateObservedPCs(); + template void CollectFeatures(Callback CB) const; + + void ResetMaps() { + ValueProfileMap.Reset(); + ClearExtraCounters(); + ClearInlineCounters(); + } + + void ClearInlineCounters(); + + void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize); + void PrintFeatureSet(); + + void PrintModuleInfo(); + + void PrintCoverage(); + + template + void IterateCoveredFunctions(CallBack CB); + + void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, + size_t n, bool StopAtZero); + + TableOfRecentCompares TORC4; + TableOfRecentCompares TORC8; + TableOfRecentCompares TORCW; + MemMemTable<1024> MMT; + + void RecordInitialStack(); + uintptr_t GetMaxStackOffset() const; + + template + void ForEachObservedPC(CallBack CB) { + for (auto PC : ObservedPCs) + CB(PC); + } + + void SetFocusFunction(const std::string &FuncName); + bool ObservedFocusFunction(); + + struct PCTableEntry { + uintptr_t PC, PCFlags; + }; + + uintptr_t PCTableEntryIdx(const PCTableEntry *TE); + const PCTableEntry *PCTableEntryByIdx(uintptr_t Idx); + static uintptr_t GetNextInstructionPc(uintptr_t PC); + bool PcIsFuncEntry(const PCTableEntry *TE) { return TE->PCFlags & 1; } + +private: + bool UseCounters = false; + uint32_t UseValueProfileMask = false; + bool DoPrintNewPCs = false; + size_t NumPrintNewFuncs = 0; + + // Module represents the array of 8-bit counters split into regions + // such that every region, except maybe the first and the last one, is one + // full page. + struct Module { + struct Region { + uint8_t *Start, *Stop; + bool Enabled; + bool OneFullPage; + }; + Region *Regions; + size_t NumRegions; + uint8_t *Start() { return Regions[0].Start; } + uint8_t *Stop() { return Regions[NumRegions - 1].Stop; } + size_t Size() { return Stop() - Start(); } + size_t Idx(uint8_t *P) { + assert(P >= Start() && P < Stop()); + return P - Start(); + } + }; + + Module Modules[4096]; + size_t NumModules; // linker-initialized. + size_t NumInline8bitCounters; + + template + void IterateCounterRegions(Callback CB) { + for (size_t m = 0; m < NumModules; m++) + for (size_t r = 0; r < Modules[m].NumRegions; r++) + CB(Modules[m].Regions[r]); + } + + struct { const PCTableEntry *Start, *Stop; } ModulePCTable[4096]; + size_t NumPCTables; + size_t NumPCsInPCTables; + + Set ObservedPCs; + std::unordered_map ObservedFuncs; // PC => Counter. + + uint8_t *FocusFunctionCounterPtr = nullptr; + + ValueBitMap ValueProfileMap; + uintptr_t InitialStack; +}; + +template +// void Callback(size_t FirstFeature, size_t Idx, uint8_t Value); +ATTRIBUTE_NO_SANITIZE_ALL +size_t ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End, + size_t FirstFeature, Callback Handle8bitCounter) { + typedef uintptr_t LargeType; + const size_t Step = sizeof(LargeType) / sizeof(uint8_t); + const size_t StepMask = Step - 1; + auto P = Begin; + // Iterate by 1 byte until either the alignment boundary or the end. + for (; reinterpret_cast(P) & StepMask && P < End; P++) + if (uint8_t V = *P) + Handle8bitCounter(FirstFeature, P - Begin, V); + + // Iterate by Step bytes at a time. + for (; P < End; P += Step) + if (LargeType Bundle = *reinterpret_cast(P)) { + Bundle = HostToLE(Bundle); + for (size_t I = 0; I < Step; I++, Bundle >>= 8) + if (uint8_t V = Bundle & 0xff) + Handle8bitCounter(FirstFeature, P - Begin + I, V); + } + + // Iterate by 1 byte until the end. + for (; P < End; P++) + if (uint8_t V = *P) + Handle8bitCounter(FirstFeature, P - Begin, V); + return End - Begin; +} + +// Given a non-zero Counter returns a number in the range [0,7]. +template +unsigned CounterToFeature(T Counter) { + // Returns a feature number by placing Counters into buckets as illustrated + // below. + // + // Counter bucket: [1] [2] [3] [4-7] [8-15] [16-31] [32-127] [128+] + // Feature number: 0 1 2 3 4 5 6 7 + // + // This is a heuristic taken from AFL (see + // http://lcamtuf.coredump.cx/afl/technical_details.txt). + // + // This implementation may change in the future so clients should + // not rely on it. + assert(Counter); + unsigned Bit = 0; + /**/ if (Counter >= 128) Bit = 7; + else if (Counter >= 32) Bit = 6; + else if (Counter >= 16) Bit = 5; + else if (Counter >= 8) Bit = 4; + else if (Counter >= 4) Bit = 3; + else if (Counter >= 3) Bit = 2; + else if (Counter >= 2) Bit = 1; + return Bit; +} + +template // void Callback(size_t Feature) +ATTRIBUTE_NO_SANITIZE_ADDRESS +ATTRIBUTE_NOINLINE +void TracePC::CollectFeatures(Callback HandleFeature) const { + auto Handle8bitCounter = [&](size_t FirstFeature, + size_t Idx, uint8_t Counter) { + if (UseCounters) + HandleFeature(FirstFeature + Idx * 8 + CounterToFeature(Counter)); + else + HandleFeature(FirstFeature + Idx); + }; + + size_t FirstFeature = 0; + + for (size_t i = 0; i < NumModules; i++) { + for (size_t r = 0; r < Modules[i].NumRegions; r++) { + if (!Modules[i].Regions[r].Enabled) continue; + FirstFeature += 8 * ForEachNonZeroByte(Modules[i].Regions[r].Start, + Modules[i].Regions[r].Stop, + FirstFeature, Handle8bitCounter); + } + } + + FirstFeature += + 8 * ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), + FirstFeature, Handle8bitCounter); + + if (UseValueProfileMask) { + ValueProfileMap.ForEach([&](size_t Idx) { + HandleFeature(FirstFeature + Idx); + }); + FirstFeature += ValueProfileMap.SizeInBits(); + } + + // Step function, grows similar to 8 * Log_2(A). + auto StackDepthStepFunction = [](uint32_t A) -> uint32_t { + if (!A) return A; + uint32_t Log2 = Log(A); + if (Log2 < 3) return A; + Log2 -= 3; + return (Log2 + 1) * 8 + ((A >> Log2) & 7); + }; + assert(StackDepthStepFunction(1024) == 64); + assert(StackDepthStepFunction(1024 * 4) == 80); + assert(StackDepthStepFunction(1024 * 1024) == 144); + + if (auto MaxStackOffset = GetMaxStackOffset()) + HandleFeature(FirstFeature + StackDepthStepFunction(MaxStackOffset / 8)); +} + +extern TracePC TPC; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_TRACE_PC diff --git a/custom_mutators/libfuzzer/FuzzerUtil.cpp b/custom_mutators/libfuzzer/FuzzerUtil.cpp new file mode 100644 index 00000000..7c395f7d --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtil.cpp @@ -0,0 +1,314 @@ +//===- FuzzerUtil.cpp - Misc utils ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils. +//===----------------------------------------------------------------------===// + +#include "FuzzerUtil.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +void PrintHexArray(const uint8_t *Data, size_t Size, const char *PrintAfter) { + + for (size_t i = 0; i < Size; i++) + Printf("0x%x,", (unsigned)Data[i]); + Printf("%s", PrintAfter); + +} + +void Print(const Unit &v, const char *PrintAfter) { + + PrintHexArray(v.data(), v.size(), PrintAfter); + +} + +void PrintASCIIByte(uint8_t Byte) { + + if (Byte == '\\') + Printf("\\\\"); + else if (Byte == '"') + Printf("\\\""); + else if (Byte >= 32 && Byte < 127) + Printf("%c", Byte); + else + Printf("\\x%02x", Byte); + +} + +void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter) { + + for (size_t i = 0; i < Size; i++) + PrintASCIIByte(Data[i]); + Printf("%s", PrintAfter); + +} + +void PrintASCII(const Unit &U, const char *PrintAfter) { + + PrintASCII(U.data(), U.size(), PrintAfter); + +} + +bool ToASCII(uint8_t *Data, size_t Size) { + + bool Changed = false; + for (size_t i = 0; i < Size; i++) { + + uint8_t &X = Data[i]; + auto NewX = X; + NewX &= 127; + if (!isspace(NewX) && !isprint(NewX)) NewX = ' '; + Changed |= NewX != X; + X = NewX; + + } + + return Changed; + +} + +bool IsASCII(const Unit &U) { + + return IsASCII(U.data(), U.size()); + +} + +bool IsASCII(const uint8_t *Data, size_t Size) { + + for (size_t i = 0; i < Size; i++) + if (!(isprint(Data[i]) || isspace(Data[i]))) return false; + return true; + +} + +bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) { + + U->clear(); + if (Str.empty()) return false; + size_t L = 0, R = Str.size() - 1; // We are parsing the range [L,R]. + // Skip spaces from both sides. + while (L < R && isspace(Str[L])) + L++; + while (R > L && isspace(Str[R])) + R--; + if (R - L < 2) return false; + // Check the closing " + if (Str[R] != '"') return false; + R--; + // Find the opening " + while (L < R && Str[L] != '"') + L++; + if (L >= R) return false; + assert(Str[L] == '\"'); + L++; + assert(L <= R); + for (size_t Pos = L; Pos <= R; Pos++) { + + uint8_t V = (uint8_t)Str[Pos]; + if (!isprint(V) && !isspace(V)) return false; + if (V == '\\') { + + // Handle '\\' + if (Pos + 1 <= R && (Str[Pos + 1] == '\\' || Str[Pos + 1] == '"')) { + + U->push_back(Str[Pos + 1]); + Pos++; + continue; + + } + + // Handle '\xAB' + if (Pos + 3 <= R && Str[Pos + 1] == 'x' && isxdigit(Str[Pos + 2]) && + isxdigit(Str[Pos + 3])) { + + char Hex[] = "0xAA"; + Hex[2] = Str[Pos + 2]; + Hex[3] = Str[Pos + 3]; + U->push_back(strtol(Hex, nullptr, 16)); + Pos += 3; + continue; + + } + + return false; // Invalid escape. + + } else { + + // Any other character. + U->push_back(V); + + } + + } + + return true; + +} + +bool ParseDictionaryFile(const std::string &Text, Vector *Units) { + + if (Text.empty()) { + + Printf("ParseDictionaryFile: file does not exist or is empty\n"); + return false; + + } + + std::istringstream ISS(Text); + Units->clear(); + Unit U; + int LineNo = 0; + std::string S; + while (std::getline(ISS, S, '\n')) { + + LineNo++; + size_t Pos = 0; + while (Pos < S.size() && isspace(S[Pos])) + Pos++; // Skip spaces. + if (Pos == S.size()) continue; // Empty line. + if (S[Pos] == '#') continue; // Comment line. + if (ParseOneDictionaryEntry(S, &U)) { + + Units->push_back(U); + + } else { + + Printf("ParseDictionaryFile: error in line %d\n\t\t%s\n", LineNo, + S.c_str()); + return false; + + } + + } + + return true; + +} + +// Code duplicated (and tested) in llvm/include/llvm/Support/Base64.h +std::string Base64(const Unit &U) { + + static const char Table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + std::string Buffer; + Buffer.resize(((U.size() + 2) / 3) * 4); + + size_t i = 0, j = 0; + for (size_t n = U.size() / 3 * 3; i < n; i += 3, j += 4) { + + uint32_t x = ((unsigned char)U[i] << 16) | ((unsigned char)U[i + 1] << 8) | + (unsigned char)U[i + 2]; + Buffer[j + 0] = Table[(x >> 18) & 63]; + Buffer[j + 1] = Table[(x >> 12) & 63]; + Buffer[j + 2] = Table[(x >> 6) & 63]; + Buffer[j + 3] = Table[x & 63]; + + } + + if (i + 1 == U.size()) { + + uint32_t x = ((unsigned char)U[i] << 16); + Buffer[j + 0] = Table[(x >> 18) & 63]; + Buffer[j + 1] = Table[(x >> 12) & 63]; + Buffer[j + 2] = '='; + Buffer[j + 3] = '='; + + } else if (i + 2 == U.size()) { + + uint32_t x = ((unsigned char)U[i] << 16) | ((unsigned char)U[i + 1] << 8); + Buffer[j + 0] = Table[(x >> 18) & 63]; + Buffer[j + 1] = Table[(x >> 12) & 63]; + Buffer[j + 2] = Table[(x >> 6) & 63]; + Buffer[j + 3] = '='; + + } + + return Buffer; + +} + +static std::mutex SymbolizeMutex; + +std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) { + + std::unique_lock l(SymbolizeMutex, std::try_to_lock); + if (!EF->__sanitizer_symbolize_pc || !l.owns_lock()) + return ""; + char PcDescr[1024] = {}; + EF->__sanitizer_symbolize_pc(reinterpret_cast(PC), SymbolizedFMT, + PcDescr, sizeof(PcDescr)); + PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case. + return PcDescr; + +} + +void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) { + + if (EF->__sanitizer_symbolize_pc) + Printf("%s", DescribePC(SymbolizedFMT, PC).c_str()); + else + Printf(FallbackFMT, PC); + +} + +void PrintStackTrace() { + + std::unique_lock l(SymbolizeMutex, std::try_to_lock); + if (EF->__sanitizer_print_stack_trace && l.owns_lock()) + EF->__sanitizer_print_stack_trace(); + +} + +void PrintMemoryProfile() { + + std::unique_lock l(SymbolizeMutex, std::try_to_lock); + if (EF->__sanitizer_print_memory_profile && l.owns_lock()) + EF->__sanitizer_print_memory_profile(95, 8); + +} + +unsigned NumberOfCpuCores() { + + unsigned N = std::thread::hardware_concurrency(); + if (!N) { + + Printf( + "WARNING: std::thread::hardware_concurrency not well defined for " + "your platform. Assuming CPU count of 1.\n"); + N = 1; + + } + + return N; + +} + +size_t SimpleFastHash(const uint8_t *Data, size_t Size) { + + size_t Res = 0; + for (size_t i = 0; i < Size; i++) + Res = Res * 11 + Data[i]; + return Res; + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerUtil.h b/custom_mutators/libfuzzer/FuzzerUtil.h new file mode 100644 index 00000000..e90be085 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtil.h @@ -0,0 +1,117 @@ +//===- FuzzerUtil.h - Internal header for the Fuzzer Utils ------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Util functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_UTIL_H +#define LLVM_FUZZER_UTIL_H + +#include "FuzzerBuiltins.h" +#include "FuzzerBuiltinsMsvc.h" +#include "FuzzerCommand.h" +#include "FuzzerDefs.h" + +namespace fuzzer { + +void PrintHexArray(const Unit &U, const char *PrintAfter = ""); + +void PrintHexArray(const uint8_t *Data, size_t Size, + const char *PrintAfter = ""); + +void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter = ""); + +void PrintASCII(const Unit &U, const char *PrintAfter = ""); + +// Changes U to contain only ASCII (isprint+isspace) characters. +// Returns true iff U has been changed. +bool ToASCII(uint8_t *Data, size_t Size); + +bool IsASCII(const Unit &U); + +bool IsASCII(const uint8_t *Data, size_t Size); + +std::string Base64(const Unit &U); + +void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC); + +std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC); + +void PrintStackTrace(); + +void PrintMemoryProfile(); + +unsigned NumberOfCpuCores(); + +// Platform specific functions. +void SetSignalHandler(const FuzzingOptions& Options); + +void SleepSeconds(int Seconds); + +unsigned long GetPid(); + +size_t GetPeakRSSMb(); + +int ExecuteCommand(const Command &Cmd); +bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput); + +// Fuchsia does not have popen/pclose. +FILE *OpenProcessPipe(const char *Command, const char *Mode); +int CloseProcessPipe(FILE *F); + +const void *SearchMemory(const void *haystack, size_t haystacklen, + const void *needle, size_t needlelen); + +std::string CloneArgsWithoutX(const Vector &Args, + const char *X1, const char *X2); + +inline std::string CloneArgsWithoutX(const Vector &Args, + const char *X) { + return CloneArgsWithoutX(Args, X, X); +} + +inline std::pair SplitBefore(std::string X, + std::string S) { + auto Pos = S.find(X); + if (Pos == std::string::npos) + return std::make_pair(S, ""); + return std::make_pair(S.substr(0, Pos), S.substr(Pos)); +} + +void DiscardOutput(int Fd); + +std::string DisassembleCmd(const std::string &FileName); + +std::string SearchRegexCmd(const std::string &Regex); + +size_t SimpleFastHash(const uint8_t *Data, size_t Size); + +inline uint32_t Log(uint32_t X) { return 32 - Clz(X) - 1; } + +inline size_t PageSize() { return 4096; } +inline uint8_t *RoundUpByPage(uint8_t *P) { + uintptr_t X = reinterpret_cast(P); + size_t Mask = PageSize() - 1; + X = (X + Mask) & ~Mask; + return reinterpret_cast(X); +} +inline uint8_t *RoundDownByPage(uint8_t *P) { + uintptr_t X = reinterpret_cast(P); + size_t Mask = PageSize() - 1; + X = X & ~Mask; + return reinterpret_cast(X); +} + +#if __BYTE_ORDER == __LITTLE_ENDIAN +template T HostToLE(T X) { return X; } +#else +template T HostToLE(T X) { return Bswap(X); } +#endif + +} // namespace fuzzer + +#endif // LLVM_FUZZER_UTIL_H diff --git a/custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp b/custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp new file mode 100644 index 00000000..420d8c23 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp @@ -0,0 +1,205 @@ +//===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils for Darwin. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_APPLE + #include "FuzzerCommand.h" + #include "FuzzerIO.h" + #include + #include + #include + #include + #include + #include + #include + +// There is no header for this on macOS so declare here +extern "C" char **environ; + +namespace fuzzer { + +static std::mutex SignalMutex; +// Global variables used to keep track of how signal handling should be +// restored. They should **not** be accessed without holding `SignalMutex`. +static int ActiveThreadCount = 0; +static struct sigaction OldSigIntAction; +static struct sigaction OldSigQuitAction; +static sigset_t OldBlockedSignalsSet; + +// This is a reimplementation of Libc's `system()`. On Darwin the Libc +// implementation contains a mutex which prevents it from being used +// concurrently. This implementation **can** be used concurrently. It sets the +// signal handlers when the first thread enters and restores them when the last +// thread finishes execution of the function and ensures this is not racey by +// using a mutex. +int ExecuteCommand(const Command &Cmd) { + + std::string CmdLine = Cmd.toString(); + posix_spawnattr_t SpawnAttributes; + if (posix_spawnattr_init(&SpawnAttributes)) return -1; + // Block and ignore signals of the current process when the first thread + // enters. + { + + std::lock_guard Lock(SignalMutex); + if (ActiveThreadCount == 0) { + + static struct sigaction IgnoreSignalAction; + sigset_t BlockedSignalsSet; + memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction)); + IgnoreSignalAction.sa_handler = SIG_IGN; + + if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) { + + Printf("Failed to ignore SIGINT\n"); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + + } + + if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) { + + Printf("Failed to ignore SIGQUIT\n"); + // Try our best to restore the signal handlers. + (void)sigaction(SIGINT, &OldSigIntAction, NULL); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + + } + + (void)sigemptyset(&BlockedSignalsSet); + (void)sigaddset(&BlockedSignalsSet, SIGCHLD); + if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) == + -1) { + + Printf("Failed to block SIGCHLD\n"); + // Try our best to restore the signal handlers. + (void)sigaction(SIGQUIT, &OldSigQuitAction, NULL); + (void)sigaction(SIGINT, &OldSigIntAction, NULL); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + + } + + } + + ++ActiveThreadCount; + + } + + // NOTE: Do not introduce any new `return` statements past this + // point. It is important that `ActiveThreadCount` always be decremented + // when leaving this function. + + // Make sure the child process uses the default handlers for the + // following signals rather than inheriting what the parent has. + sigset_t DefaultSigSet; + (void)sigemptyset(&DefaultSigSet); + (void)sigaddset(&DefaultSigSet, SIGQUIT); + (void)sigaddset(&DefaultSigSet, SIGINT); + (void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet); + // Make sure the child process doesn't block SIGCHLD + (void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet); + short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + (void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags); + + pid_t Pid; + char ** Environ = environ; // Read from global + const char *CommandCStr = CmdLine.c_str(); + char *const Argv[] = {strdup("sh"), strdup("-c"), strdup(CommandCStr), NULL}; + int ErrorCode = 0, ProcessStatus = 0; + // FIXME: We probably shouldn't hardcode the shell path. + ErrorCode = + posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes, Argv, Environ); + (void)posix_spawnattr_destroy(&SpawnAttributes); + if (!ErrorCode) { + + pid_t SavedPid = Pid; + do { + + // Repeat until call completes uninterrupted. + Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0); + + } while (Pid == -1 && errno == EINTR); + + if (Pid == -1) { + + // Fail for some other reason. + ProcessStatus = -1; + + } + + } else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) { + + // Fork failure. + ProcessStatus = -1; + + } else { + + // Shell execution failure. + ProcessStatus = W_EXITCODE(127, 0); + + } + + for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i) + free(Argv[i]); + + // Restore the signal handlers of the current process when the last thread + // using this function finishes. + { + + std::lock_guard Lock(SignalMutex); + --ActiveThreadCount; + if (ActiveThreadCount == 0) { + + bool FailedRestore = false; + if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) { + + Printf("Failed to restore SIGINT handling\n"); + FailedRestore = true; + + } + + if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) { + + Printf("Failed to restore SIGQUIT handling\n"); + FailedRestore = true; + + } + + if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) { + + Printf("Failed to unblock SIGCHLD\n"); + FailedRestore = true; + + } + + if (FailedRestore) ProcessStatus = -1; + + } + + } + + return ProcessStatus; + +} + +void DiscardOutput(int Fd) { + + FILE *Temp = fopen("/dev/null", "w"); + if (!Temp) return; + dup2(fileno(Temp), Fd); + fclose(Temp); + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_APPLE + diff --git a/custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp b/custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp new file mode 100644 index 00000000..45ecbca8 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp @@ -0,0 +1,658 @@ +//===- FuzzerUtilFuchsia.cpp - Misc utils for Fuchsia. --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils implementation using Fuchsia/Zircon APIs. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" + +#if LIBFUZZER_FUCHSIA + + #include "FuzzerInternal.h" + #include "FuzzerUtil.h" + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + +namespace fuzzer { + +// Given that Fuchsia doesn't have the POSIX signals that libFuzzer was written +// around, the general approach is to spin up dedicated threads to watch for +// each requested condition (alarm, interrupt, crash). Of these, the crash +// handler is the most involved, as it requires resuming the crashed thread in +// order to invoke the sanitizers to get the needed state. + +// Forward declaration of assembly trampoline needed to resume crashed threads. +// This appears to have external linkage to C++, which is why it's not in the +// anonymous namespace. The assembly definition inside MakeTrampoline() +// actually defines the symbol with internal linkage only. +void CrashTrampolineAsm() __asm__("CrashTrampolineAsm"); + +namespace { + +// Helper function to handle Zircon syscall failures. +void ExitOnErr(zx_status_t Status, const char *Syscall) { + + if (Status != ZX_OK) { + + Printf("libFuzzer: %s failed: %s\n", Syscall, + _zx_status_get_string(Status)); + exit(1); + + } + +} + +void AlarmHandler(int Seconds) { + + while (true) { + + SleepSeconds(Seconds); + Fuzzer::StaticAlarmCallback(); + + } + +} + +void InterruptHandler() { + + fd_set readfds; + // Ctrl-C sends ETX in Zircon. + do { + + FD_ZERO(&readfds); + FD_SET(STDIN_FILENO, &readfds); + select(STDIN_FILENO + 1, &readfds, nullptr, nullptr, nullptr); + + } while (!FD_ISSET(STDIN_FILENO, &readfds) || getchar() != 0x03); + + Fuzzer::StaticInterruptCallback(); + +} + + // CFAOffset is used to reference the stack pointer before entering the + // trampoline (Stack Pointer + CFAOffset = prev Stack Pointer). Before jumping + // to the trampoline we copy all the registers onto the stack. We need to make + // sure that the new stack has enough space to store all the registers. + // + // The trampoline holds CFI information regarding the registers stored in the + // stack, which is then used by the unwinder to restore them. + #if defined(__x86_64__) +// In x86_64 the crashing function might also be using the red zone (128 bytes +// on top of their rsp). +constexpr size_t CFAOffset = 128 + sizeof(zx_thread_state_general_regs_t); + #elif defined(__aarch64__) +// In aarch64 we need to always have the stack pointer aligned to 16 bytes, so +// we make sure that we are keeping that same alignment. +constexpr size_t CFAOffset = + (sizeof(zx_thread_state_general_regs_t) + 15) & -(uintptr_t)16; + #endif + + // For the crash handler, we need to call Fuzzer::StaticCrashSignalCallback + // without POSIX signal handlers. To achieve this, we use an assembly + // function to add the necessary CFI unwinding information and a C function to + // bridge from that back into C++. + + // FIXME: This works as a short-term solution, but this code really shouldn't + // be architecture dependent. A better long term solution is to implement + // remote unwinding and expose the necessary APIs through sanitizer_common + // and/or ASAN to allow the exception handling thread to gather the crash + // state directly. + // + // Alternatively, Fuchsia may in future actually implement basic signal + // handling for the machine trap signals. + #if defined(__x86_64__) + #define FOREACH_REGISTER(OP_REG, OP_NUM) \ + OP_REG(rax) \ + OP_REG(rbx) \ + OP_REG(rcx) \ + OP_REG(rdx) \ + OP_REG(rsi) \ + OP_REG(rdi) \ + OP_REG(rbp) \ + OP_REG(rsp) \ + OP_REG(r8) \ + OP_REG(r9) \ + OP_REG(r10) \ + OP_REG(r11) \ + OP_REG(r12) \ + OP_REG(r13) \ + OP_REG(r14) \ + OP_REG(r15) \ + OP_REG(rip) + + #elif defined(__aarch64__) + #define FOREACH_REGISTER(OP_REG, OP_NUM) \ + OP_NUM(0) \ + OP_NUM(1) \ + OP_NUM(2) \ + OP_NUM(3) \ + OP_NUM(4) \ + OP_NUM(5) \ + OP_NUM(6) \ + OP_NUM(7) \ + OP_NUM(8) \ + OP_NUM(9) \ + OP_NUM(10) \ + OP_NUM(11) \ + OP_NUM(12) \ + OP_NUM(13) \ + OP_NUM(14) \ + OP_NUM(15) \ + OP_NUM(16) \ + OP_NUM(17) \ + OP_NUM(18) \ + OP_NUM(19) \ + OP_NUM(20) \ + OP_NUM(21) \ + OP_NUM(22) \ + OP_NUM(23) \ + OP_NUM(24) \ + OP_NUM(25) \ + OP_NUM(26) \ + OP_NUM(27) \ + OP_NUM(28) \ + OP_NUM(29) \ + OP_REG(sp) + + #else + #error "Unsupported architecture for fuzzing on Fuchsia" + #endif + + // Produces a CFI directive for the named or numbered register. + // The value used refers to an assembler immediate operand with the same name + // as the register (see ASM_OPERAND_REG). + #define CFI_OFFSET_REG(reg) ".cfi_offset " #reg ", %c[" #reg "]\n" + #define CFI_OFFSET_NUM(num) CFI_OFFSET_REG(x##num) + + // Produces an assembler immediate operand for the named or numbered register. + // This operand contains the offset of the register relative to the CFA. + #define ASM_OPERAND_REG(reg) \ + [reg] "i"(offsetof(zx_thread_state_general_regs_t, reg) - CFAOffset), + #define ASM_OPERAND_NUM(num) \ + [x##num] "i"(offsetof(zx_thread_state_general_regs_t, r[num]) - CFAOffset), + +// Trampoline to bridge from the assembly below to the static C++ crash +// callback. +__attribute__((noreturn)) static void StaticCrashHandler() { + + Fuzzer::StaticCrashSignalCallback(); + for (;;) { + + _Exit(1); + + } + +} + +// Creates the trampoline with the necessary CFI information to unwind through +// to the crashing call stack: +// * Defining the CFA so that it points to the stack pointer at the point +// of crash. +// * Storing all registers at the point of crash in the stack and refer to them +// via CFI information (relative to the CFA). +// * Setting the return column so the unwinder knows how to continue unwinding. +// * (x86_64) making sure rsp is aligned before calling StaticCrashHandler. +// * Calling StaticCrashHandler that will trigger the unwinder. +// +// The __attribute__((used)) is necessary because the function +// is never called; it's just a container around the assembly to allow it to +// use operands for compile-time computed constants. +__attribute__((used)) void MakeTrampoline() { + + __asm__(".cfi_endproc\n" + ".pushsection .text.CrashTrampolineAsm\n" + ".type CrashTrampolineAsm,STT_FUNC\n" +"CrashTrampolineAsm:\n" + ".cfi_startproc simple\n" + ".cfi_signal_frame\n" + #if defined(__x86_64__) + ".cfi_return_column rip\n" + ".cfi_def_cfa rsp, %c[CFAOffset]\n" + FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) + "mov %%rsp, %%rbp\n" + ".cfi_def_cfa_register rbp\n" + "andq $-16, %%rsp\n" + "call %c[StaticCrashHandler]\n" + "ud2\n" + #elif defined(__aarch64__) + ".cfi_return_column 33\n" + ".cfi_def_cfa sp, %c[CFAOffset]\n" + FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) + ".cfi_offset 33, %c[pc]\n" + ".cfi_offset 30, %c[lr]\n" + "bl %c[StaticCrashHandler]\n" + "brk 1\n" + #else + #error "Unsupported architecture for fuzzing on Fuchsia" + #endif + ".cfi_endproc\n" + ".size CrashTrampolineAsm, . - CrashTrampolineAsm\n" + ".popsection\n" + ".cfi_startproc\n" + : // No outputs + : FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM) + #if defined(__aarch64__) + ASM_OPERAND_REG(pc) + ASM_OPERAND_REG(lr) + #endif + [StaticCrashHandler] "i" (StaticCrashHandler), + [CFAOffset] "i" (CFAOffset)); + +} + +void CrashHandler(zx_handle_t *Event) { + + // This structure is used to ensure we close handles to objects we create in + // this handler. + struct ScopedHandle { + + ~ScopedHandle() { + + _zx_handle_close(Handle); + + } + + zx_handle_t Handle = ZX_HANDLE_INVALID; + + }; + + // Create the exception channel. We need to claim to be a "debugger" so the + // kernel will allow us to modify and resume dying threads (see below). Once + // the channel is set, we can signal the main thread to continue and wait + // for the exception to arrive. + ScopedHandle Channel; + zx_handle_t Self = _zx_process_self(); + ExitOnErr(_zx_task_create_exception_channel( + Self, ZX_EXCEPTION_CHANNEL_DEBUGGER, &Channel.Handle), + "_zx_task_create_exception_channel"); + + ExitOnErr(_zx_object_signal(*Event, 0, ZX_USER_SIGNAL_0), + "_zx_object_signal"); + + // This thread lives as long as the process in order to keep handling + // crashes. In practice, the first crashed thread to reach the end of the + // StaticCrashHandler will end the process. + while (true) { + + ExitOnErr(_zx_object_wait_one(Channel.Handle, ZX_CHANNEL_READABLE, + ZX_TIME_INFINITE, nullptr), + "_zx_object_wait_one"); + + zx_exception_info_t ExceptionInfo; + ScopedHandle Exception; + ExitOnErr( + _zx_channel_read(Channel.Handle, 0, &ExceptionInfo, &Exception.Handle, + sizeof(ExceptionInfo), 1, nullptr, nullptr), + "_zx_channel_read"); + + // Ignore informational synthetic exceptions. + if (ZX_EXCP_THREAD_STARTING == ExceptionInfo.type || + ZX_EXCP_THREAD_EXITING == ExceptionInfo.type || + ZX_EXCP_PROCESS_STARTING == ExceptionInfo.type) { + + continue; + + } + + // At this point, we want to get the state of the crashing thread, but + // libFuzzer and the sanitizers assume this will happen from that same + // thread via a POSIX signal handler. "Resurrecting" the thread in the + // middle of the appropriate callback is as simple as forcibly setting the + // instruction pointer/program counter, provided we NEVER EVER return from + // that function (since otherwise our stack will not be valid). + ScopedHandle Thread; + ExitOnErr(_zx_exception_get_thread(Exception.Handle, &Thread.Handle), + "_zx_exception_get_thread"); + + zx_thread_state_general_regs_t GeneralRegisters; + ExitOnErr( + _zx_thread_read_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS, + &GeneralRegisters, sizeof(GeneralRegisters)), + "_zx_thread_read_state"); + + // To unwind properly, we need to push the crashing thread's register state + // onto the stack and jump into a trampoline with CFI instructions on how + // to restore it. + #if defined(__x86_64__) + uintptr_t StackPtr = GeneralRegisters.rsp - CFAOffset; + __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, + sizeof(GeneralRegisters)); + GeneralRegisters.rsp = StackPtr; + GeneralRegisters.rip = reinterpret_cast(CrashTrampolineAsm); + + #elif defined(__aarch64__) + uintptr_t StackPtr = GeneralRegisters.sp - CFAOffset; + __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, + sizeof(GeneralRegisters)); + GeneralRegisters.sp = StackPtr; + GeneralRegisters.pc = reinterpret_cast(CrashTrampolineAsm); + + #else + #error "Unsupported architecture for fuzzing on Fuchsia" + #endif + + // Now force the crashing thread's state. + ExitOnErr( + _zx_thread_write_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS, + &GeneralRegisters, sizeof(GeneralRegisters)), + "_zx_thread_write_state"); + + // Set the exception to HANDLED so it resumes the thread on close. + uint32_t ExceptionState = ZX_EXCEPTION_STATE_HANDLED; + ExitOnErr(_zx_object_set_property(Exception.Handle, ZX_PROP_EXCEPTION_STATE, + &ExceptionState, sizeof(ExceptionState)), + "zx_object_set_property"); + + } + +} + +} // namespace + +// Platform specific functions. +void SetSignalHandler(const FuzzingOptions &Options) { + + // Make sure information from libFuzzer and the sanitizers are easy to + // reassemble. `__sanitizer_log_write` has the added benefit of ensuring the + // DSO map is always available for the symbolizer. + // A uint64_t fits in 20 chars, so 64 is plenty. + char Buf[64]; + memset(Buf, 0, sizeof(Buf)); + snprintf(Buf, sizeof(Buf), "==%lu== INFO: libFuzzer starting.\n", GetPid()); + if (EF->__sanitizer_log_write) __sanitizer_log_write(Buf, sizeof(Buf)); + Printf("%s", Buf); + + // Set up alarm handler if needed. + if (Options.HandleAlrm && Options.UnitTimeoutSec > 0) { + + std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1); + T.detach(); + + } + + // Set up interrupt handler if needed. + if (Options.HandleInt || Options.HandleTerm) { + + std::thread T(InterruptHandler); + T.detach(); + + } + + // Early exit if no crash handler needed. + if (!Options.HandleSegv && !Options.HandleBus && !Options.HandleIll && + !Options.HandleFpe && !Options.HandleAbrt) + return; + + // Set up the crash handler and wait until it is ready before proceeding. + zx_handle_t Event; + ExitOnErr(_zx_event_create(0, &Event), "_zx_event_create"); + + std::thread T(CrashHandler, &Event); + zx_status_t Status = + _zx_object_wait_one(Event, ZX_USER_SIGNAL_0, ZX_TIME_INFINITE, nullptr); + _zx_handle_close(Event); + ExitOnErr(Status, "_zx_object_wait_one"); + + T.detach(); + +} + +void SleepSeconds(int Seconds) { + + _zx_nanosleep(_zx_deadline_after(ZX_SEC(Seconds))); + +} + +unsigned long GetPid() { + + zx_status_t rc; + zx_info_handle_basic_t Info; + if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info, + sizeof(Info), NULL, NULL)) != ZX_OK) { + + Printf("libFuzzer: unable to get info about self: %s\n", + _zx_status_get_string(rc)); + exit(1); + + } + + return Info.koid; + +} + +size_t GetPeakRSSMb() { + + zx_status_t rc; + zx_info_task_stats_t Info; + if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_TASK_STATS, &Info, + sizeof(Info), NULL, NULL)) != ZX_OK) { + + Printf("libFuzzer: unable to get info about self: %s\n", + _zx_status_get_string(rc)); + exit(1); + + } + + return (Info.mem_private_bytes + Info.mem_shared_bytes) >> 20; + +} + +template +class RunOnDestruction { + + public: + explicit RunOnDestruction(Fn fn) : fn_(fn) { + + } + + ~RunOnDestruction() { + + fn_(); + + } + + private: + Fn fn_; + +}; + +template +RunOnDestruction at_scope_exit(Fn fn) { + + return RunOnDestruction(fn); + +} + +static fdio_spawn_action_t clone_fd_action(int localFd, int targetFd) { + + return { + + .action = FDIO_SPAWN_ACTION_CLONE_FD, + .fd = + { + + .local_fd = localFd, + .target_fd = targetFd, + + }, + + }; + +} + +int ExecuteCommand(const Command &Cmd) { + + zx_status_t rc; + + // Convert arguments to C array + auto Args = Cmd.getArguments(); + size_t Argc = Args.size(); + assert(Argc != 0); + std::unique_ptr Argv(new const char *[Argc + 1]); + for (size_t i = 0; i < Argc; ++i) + Argv[i] = Args[i].c_str(); + Argv[Argc] = nullptr; + + // Determine output. On Fuchsia, the fuzzer is typically run as a component + // that lacks a mutable working directory. Fortunately, when this is the case + // a mutable output directory must be specified using "-artifact_prefix=...", + // so write the log file(s) there. + // However, we don't want to apply this logic for absolute paths. + int FdOut = STDOUT_FILENO; + bool discardStdout = false; + bool discardStderr = false; + + if (Cmd.hasOutputFile()) { + + std::string Path = Cmd.getOutputFile(); + if (Path == getDevNull()) { + + // On Fuchsia, there's no "/dev/null" like-file, so we + // just don't copy the FDs into the spawned process. + discardStdout = true; + + } else { + + bool IsAbsolutePath = Path.length() > 1 && Path[0] == '/'; + if (!IsAbsolutePath && Cmd.hasFlag("artifact_prefix")) + Path = Cmd.getFlagValue("artifact_prefix") + "/" + Path; + + FdOut = open(Path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0); + if (FdOut == -1) { + + Printf("libFuzzer: failed to open %s: %s\n", Path.c_str(), + strerror(errno)); + return ZX_ERR_IO; + + } + + } + + } + + auto CloseFdOut = at_scope_exit([FdOut]() { + + if (FdOut != STDOUT_FILENO) close(FdOut); + + }); + + // Determine stderr + int FdErr = STDERR_FILENO; + if (Cmd.isOutAndErrCombined()) { + + FdErr = FdOut; + if (discardStdout) discardStderr = true; + + } + + // Clone the file descriptors into the new process + std::vector SpawnActions; + SpawnActions.push_back(clone_fd_action(STDIN_FILENO, STDIN_FILENO)); + + if (!discardStdout) + SpawnActions.push_back(clone_fd_action(FdOut, STDOUT_FILENO)); + if (!discardStderr) + SpawnActions.push_back(clone_fd_action(FdErr, STDERR_FILENO)); + + // Start the process. + char ErrorMsg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; + zx_handle_t ProcessHandle = ZX_HANDLE_INVALID; + rc = fdio_spawn_etc(ZX_HANDLE_INVALID, + FDIO_SPAWN_CLONE_ALL & (~FDIO_SPAWN_CLONE_STDIO), Argv[0], + Argv.get(), nullptr, SpawnActions.size(), + SpawnActions.data(), &ProcessHandle, ErrorMsg); + + if (rc != ZX_OK) { + + Printf("libFuzzer: failed to launch '%s': %s, %s\n", Argv[0], ErrorMsg, + _zx_status_get_string(rc)); + return rc; + + } + + auto CloseHandle = at_scope_exit([&]() { _zx_handle_close(ProcessHandle); }); + + // Now join the process and return the exit status. + if ((rc = _zx_object_wait_one(ProcessHandle, ZX_PROCESS_TERMINATED, + ZX_TIME_INFINITE, nullptr)) != ZX_OK) { + + Printf("libFuzzer: failed to join '%s': %s\n", Argv[0], + _zx_status_get_string(rc)); + return rc; + + } + + zx_info_process_t Info; + if ((rc = _zx_object_get_info(ProcessHandle, ZX_INFO_PROCESS, &Info, + sizeof(Info), nullptr, nullptr)) != ZX_OK) { + + Printf("libFuzzer: unable to get return code from '%s': %s\n", Argv[0], + _zx_status_get_string(rc)); + return rc; + + } + + return Info.return_code; + +} + +bool ExecuteCommand(const Command &BaseCmd, std::string *CmdOutput) { + + auto LogFilePath = TempPath("SimPopenOut", ".txt"); + Command Cmd(BaseCmd); + Cmd.setOutputFile(LogFilePath); + int Ret = ExecuteCommand(Cmd); + *CmdOutput = FileToString(LogFilePath); + RemoveFile(LogFilePath); + return Ret == 0; + +} + +const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, + size_t PattLen) { + + return memmem(Data, DataLen, Patt, PattLen); + +} + +// In fuchsia, accessing /dev/null is not supported. There's nothing +// similar to a file that discards everything that is written to it. +// The way of doing something similar in fuchsia is by using +// fdio_null_create and binding that to a file descriptor. +void DiscardOutput(int Fd) { + + fdio_t *fdio_null = fdio_null_create(); + if (fdio_null == nullptr) return; + int nullfd = fdio_bind_to_fd(fdio_null, -1, 0); + if (nullfd < 0) return; + dup2(nullfd, Fd); + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_FUCHSIA + diff --git a/custom_mutators/libfuzzer/FuzzerUtilLinux.cpp b/custom_mutators/libfuzzer/FuzzerUtilLinux.cpp new file mode 100644 index 00000000..f2531bee --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilLinux.cpp @@ -0,0 +1,43 @@ +//===- FuzzerUtilLinux.cpp - Misc utils for Linux. ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils for Linux. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \ + LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN + #include "FuzzerCommand.h" + + #include + #include + #include + #include + +namespace fuzzer { + +int ExecuteCommand(const Command &Cmd) { + + std::string CmdLine = Cmd.toString(); + int exit_code = system(CmdLine.c_str()); + if (WIFEXITED(exit_code)) return WEXITSTATUS(exit_code); + return exit_code; + +} + +void DiscardOutput(int Fd) { + + FILE *Temp = fopen("/dev/null", "w"); + if (!Temp) return; + dup2(fileno(Temp), Fd); + fclose(Temp); + +} + +} // namespace fuzzer + +#endif + diff --git a/custom_mutators/libfuzzer/FuzzerUtilPosix.cpp b/custom_mutators/libfuzzer/FuzzerUtilPosix.cpp new file mode 100644 index 00000000..372bfa5e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilPosix.cpp @@ -0,0 +1,239 @@ +//===- FuzzerUtilPosix.cpp - Misc utils for Posix. ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils implementation using Posix API. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_POSIX + #include "FuzzerIO.h" + #include "FuzzerInternal.h" + #include "FuzzerTracePC.h" + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + +namespace fuzzer { + +static void AlarmHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticAlarmCallback(); + +} + +static void (*upstream_segv_handler)(int, siginfo_t *, void *); + +static void SegvHandler(int sig, siginfo_t *si, void *ucontext) { + + assert(si->si_signo == SIGSEGV); + if (upstream_segv_handler) return upstream_segv_handler(sig, si, ucontext); + Fuzzer::StaticCrashSignalCallback(); + +} + +static void CrashHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticCrashSignalCallback(); + +} + +static void InterruptHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticInterruptCallback(); + +} + +static void GracefulExitHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticGracefulExitCallback(); + +} + +static void FileSizeExceedHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticFileSizeExceedCallback(); + +} + +static void SetSigaction(int signum, + void (*callback)(int, siginfo_t *, void *)) { + + struct sigaction sigact = {}; + if (sigaction(signum, nullptr, &sigact)) { + + Printf("libFuzzer: sigaction failed with %d\n", errno); + exit(1); + + } + + if (sigact.sa_flags & SA_SIGINFO) { + + if (sigact.sa_sigaction) { + + if (signum != SIGSEGV) return; + upstream_segv_handler = sigact.sa_sigaction; + + } + + } else { + + if (sigact.sa_handler != SIG_DFL && sigact.sa_handler != SIG_IGN && + sigact.sa_handler != SIG_ERR) + return; + + } + + sigact = {}; + sigact.sa_flags = SA_SIGINFO; + sigact.sa_sigaction = callback; + if (sigaction(signum, &sigact, 0)) { + + Printf("libFuzzer: sigaction failed with %d\n", errno); + exit(1); + + } + +} + +// Return true on success, false otherwise. +bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput) { + + FILE *Pipe = popen(Cmd.toString().c_str(), "r"); + if (!Pipe) return false; + + if (CmdOutput) { + + char TmpBuffer[128]; + while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe)) + CmdOutput->append(TmpBuffer); + + } + + return pclose(Pipe) == 0; + +} + +void SetTimer(int Seconds) { + + struct itimerval T { + + {Seconds, 0}, { + + Seconds, 0 + + } + + }; + + if (setitimer(ITIMER_REAL, &T, nullptr)) { + + Printf("libFuzzer: setitimer failed with %d\n", errno); + exit(1); + + } + + SetSigaction(SIGALRM, AlarmHandler); + +} + +void SetSignalHandler(const FuzzingOptions &Options) { + + // setitimer is not implemented in emscripten. + if (Options.HandleAlrm && Options.UnitTimeoutSec > 0 && !LIBFUZZER_EMSCRIPTEN) + SetTimer(Options.UnitTimeoutSec / 2 + 1); + if (Options.HandleInt) SetSigaction(SIGINT, InterruptHandler); + if (Options.HandleTerm) SetSigaction(SIGTERM, InterruptHandler); + if (Options.HandleSegv) SetSigaction(SIGSEGV, SegvHandler); + if (Options.HandleBus) SetSigaction(SIGBUS, CrashHandler); + if (Options.HandleAbrt) SetSigaction(SIGABRT, CrashHandler); + if (Options.HandleIll) SetSigaction(SIGILL, CrashHandler); + if (Options.HandleFpe) SetSigaction(SIGFPE, CrashHandler); + if (Options.HandleXfsz) SetSigaction(SIGXFSZ, FileSizeExceedHandler); + if (Options.HandleUsr1) SetSigaction(SIGUSR1, GracefulExitHandler); + if (Options.HandleUsr2) SetSigaction(SIGUSR2, GracefulExitHandler); + +} + +void SleepSeconds(int Seconds) { + + sleep(Seconds); // Use C API to avoid coverage from instrumented libc++. + +} + +unsigned long GetPid() { + + return (unsigned long)getpid(); + +} + +size_t GetPeakRSSMb() { + + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage)) return 0; + if (LIBFUZZER_LINUX || LIBFUZZER_FREEBSD || LIBFUZZER_NETBSD || + LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN) { + + // ru_maxrss is in KiB + return usage.ru_maxrss >> 10; + + } else if (LIBFUZZER_APPLE) { + + // ru_maxrss is in bytes + return usage.ru_maxrss >> 20; + + } + + assert(0 && "GetPeakRSSMb() is not implemented for your platform"); + return 0; + +} + +FILE *OpenProcessPipe(const char *Command, const char *Mode) { + + return popen(Command, Mode); + +} + +int CloseProcessPipe(FILE *F) { + + return pclose(F); + +} + +const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, + size_t PattLen) { + + return memmem(Data, DataLen, Patt, PattLen); + +} + +std::string DisassembleCmd(const std::string &FileName) { + + return "objdump -d " + FileName; + +} + +std::string SearchRegexCmd(const std::string &Regex) { + + return "grep '" + Regex + "'"; + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_POSIX + diff --git a/custom_mutators/libfuzzer/FuzzerUtilWindows.cpp b/custom_mutators/libfuzzer/FuzzerUtilWindows.cpp new file mode 100644 index 00000000..dca5630f --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilWindows.cpp @@ -0,0 +1,279 @@ +//===- FuzzerUtilWindows.cpp - Misc utils for Windows. --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils implementation for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_WINDOWS + #include "FuzzerCommand.h" + #include "FuzzerIO.h" + #include "FuzzerInternal.h" + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + // This must be included after windows.h. + #include + +namespace fuzzer { + +static const FuzzingOptions *HandlerOpt = nullptr; + +static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { + + switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { + + case EXCEPTION_ACCESS_VIOLATION: + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + case EXCEPTION_STACK_OVERFLOW: + if (HandlerOpt->HandleSegv) Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_DATATYPE_MISALIGNMENT: + case EXCEPTION_IN_PAGE_ERROR: + if (HandlerOpt->HandleBus) Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + case EXCEPTION_PRIV_INSTRUCTION: + if (HandlerOpt->HandleIll) Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_INVALID_OPERATION: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_STACK_CHECK: + case EXCEPTION_FLT_UNDERFLOW: + case EXCEPTION_INT_DIVIDE_BY_ZERO: + case EXCEPTION_INT_OVERFLOW: + if (HandlerOpt->HandleFpe) Fuzzer::StaticCrashSignalCallback(); + break; + // TODO: handle (Options.HandleXfsz) + + } + + return EXCEPTION_CONTINUE_SEARCH; + +} + +BOOL WINAPI CtrlHandler(DWORD dwCtrlType) { + + switch (dwCtrlType) { + + case CTRL_C_EVENT: + if (HandlerOpt->HandleInt) Fuzzer::StaticInterruptCallback(); + return TRUE; + case CTRL_BREAK_EVENT: + if (HandlerOpt->HandleTerm) Fuzzer::StaticInterruptCallback(); + return TRUE; + + } + + return FALSE; + +} + +void CALLBACK AlarmHandler(PVOID, BOOLEAN) { + + Fuzzer::StaticAlarmCallback(); + +} + +class TimerQ { + + HANDLE TimerQueue; + + public: + TimerQ() : TimerQueue(NULL) { + + } + + ~TimerQ() { + + if (TimerQueue) DeleteTimerQueueEx(TimerQueue, NULL); + + } + + void SetTimer(int Seconds) { + + if (!TimerQueue) { + + TimerQueue = CreateTimerQueue(); + if (!TimerQueue) { + + Printf("libFuzzer: CreateTimerQueue failed.\n"); + exit(1); + + } + + } + + HANDLE Timer; + if (!CreateTimerQueueTimer(&Timer, TimerQueue, AlarmHandler, NULL, + Seconds * 1000, Seconds * 1000, 0)) { + + Printf("libFuzzer: CreateTimerQueueTimer failed.\n"); + exit(1); + + } + + } + +}; + +static TimerQ Timer; + +static void CrashHandler(int) { + + Fuzzer::StaticCrashSignalCallback(); + +} + +void SetSignalHandler(const FuzzingOptions &Options) { + + HandlerOpt = &Options; + + if (Options.HandleAlrm && Options.UnitTimeoutSec > 0) + Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1); + + if (Options.HandleInt || Options.HandleTerm) + if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) { + + DWORD LastError = GetLastError(); + Printf("libFuzzer: SetConsoleCtrlHandler failed (Error code: %lu).\n", + LastError); + exit(1); + + } + + if (Options.HandleSegv || Options.HandleBus || Options.HandleIll || + Options.HandleFpe) + SetUnhandledExceptionFilter(ExceptionHandler); + + if (Options.HandleAbrt) + if (SIG_ERR == signal(SIGABRT, CrashHandler)) { + + Printf("libFuzzer: signal failed with %d\n", errno); + exit(1); + + } + +} + +void SleepSeconds(int Seconds) { + + Sleep(Seconds * 1000); + +} + +unsigned long GetPid() { + + return GetCurrentProcessId(); + +} + +size_t GetPeakRSSMb() { + + PROCESS_MEMORY_COUNTERS info; + if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info))) return 0; + return info.PeakWorkingSetSize >> 20; + +} + +FILE *OpenProcessPipe(const char *Command, const char *Mode) { + + return _popen(Command, Mode); + +} + +int CloseProcessPipe(FILE *F) { + + return _pclose(F); + +} + +int ExecuteCommand(const Command &Cmd) { + + std::string CmdLine = Cmd.toString(); + return system(CmdLine.c_str()); + +} + +bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput) { + + FILE *Pipe = _popen(Cmd.toString().c_str(), "r"); + if (!Pipe) return false; + + if (CmdOutput) { + + char TmpBuffer[128]; + while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe)) + CmdOutput->append(TmpBuffer); + + } + + return _pclose(Pipe) == 0; + +} + +const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, + size_t PattLen) { + + // TODO: make this implementation more efficient. + const char *Cdata = (const char *)Data; + const char *Cpatt = (const char *)Patt; + + if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen) + return NULL; + + if (PattLen == 1) return memchr(Data, *Cpatt, DataLen); + + const char *End = Cdata + DataLen - PattLen + 1; + + for (const char *It = Cdata; It < End; ++It) + if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0) return It; + + return NULL; + +} + +std::string DisassembleCmd(const std::string &FileName) { + + Vector command_vector; + command_vector.push_back("dumpbin /summary > nul"); + if (ExecuteCommand(Command(command_vector)) == 0) + return "dumpbin /disasm " + FileName; + Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n"); + exit(1); + +} + +std::string SearchRegexCmd(const std::string &Regex) { + + return "findstr /r \"" + Regex + "\""; + +} + +void DiscardOutput(int Fd) { + + FILE *Temp = fopen("nul", "w"); + if (!Temp) return; + _dup2(_fileno(Temp), Fd); + fclose(Temp); + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS + diff --git a/custom_mutators/libfuzzer/FuzzerValueBitMap.h b/custom_mutators/libfuzzer/FuzzerValueBitMap.h new file mode 100644 index 00000000..ddbfe200 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerValueBitMap.h @@ -0,0 +1,73 @@ +//===- FuzzerValueBitMap.h - INTERNAL - Bit map -----------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// ValueBitMap. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_VALUE_BIT_MAP_H +#define LLVM_FUZZER_VALUE_BIT_MAP_H + +#include "FuzzerPlatform.h" +#include + +namespace fuzzer { + +// A bit map containing kMapSizeInWords bits. +struct ValueBitMap { + static const size_t kMapSizeInBits = 1 << 16; + static const size_t kMapPrimeMod = 65371; // Largest Prime < kMapSizeInBits; + static const size_t kBitsInWord = (sizeof(uintptr_t) * 8); + static const size_t kMapSizeInWords = kMapSizeInBits / kBitsInWord; + public: + + // Clears all bits. + void Reset() { memset(Map, 0, sizeof(Map)); } + + // Computes a hash function of Value and sets the corresponding bit. + // Returns true if the bit was changed from 0 to 1. + ATTRIBUTE_NO_SANITIZE_ALL + inline bool AddValue(uintptr_t Value) { + uintptr_t Idx = Value % kMapSizeInBits; + uintptr_t WordIdx = Idx / kBitsInWord; + uintptr_t BitIdx = Idx % kBitsInWord; + uintptr_t Old = Map[WordIdx]; + uintptr_t New = Old | (1ULL << BitIdx); + Map[WordIdx] = New; + return New != Old; + } + + ATTRIBUTE_NO_SANITIZE_ALL + inline bool AddValueModPrime(uintptr_t Value) { + return AddValue(Value % kMapPrimeMod); + } + + inline bool Get(uintptr_t Idx) { + assert(Idx < kMapSizeInBits); + uintptr_t WordIdx = Idx / kBitsInWord; + uintptr_t BitIdx = Idx % kBitsInWord; + return Map[WordIdx] & (1ULL << BitIdx); + } + + size_t SizeInBits() const { return kMapSizeInBits; } + + template + ATTRIBUTE_NO_SANITIZE_ALL + void ForEach(Callback CB) const { + for (size_t i = 0; i < kMapSizeInWords; i++) + if (uintptr_t M = Map[i]) + for (size_t j = 0; j < sizeof(M) * 8; j++) + if (M & ((uintptr_t)1 << j)) + CB(i * sizeof(M) * 8 + j); + } + + private: + ATTRIBUTE_ALIGNED(512) uintptr_t Map[kMapSizeInWords]; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_VALUE_BIT_MAP_H diff --git a/custom_mutators/libfuzzer/Makefile b/custom_mutators/libfuzzer/Makefile new file mode 100644 index 00000000..f0c80392 --- /dev/null +++ b/custom_mutators/libfuzzer/Makefile @@ -0,0 +1,81 @@ + +#CFLAGS = -O3 -funroll-loops -fPIC -fpermissive -std=c++11 +CFLAGS = -g -O0 -fPIC -fpermissive -std=c++11 +CC := clang++ + +all: libfuzzer-mutator.so + +FuzzerCrossOver.o: FuzzerCrossOver.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerDataFlowTrace.o: FuzzerDataFlowTrace.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerDriver.o: FuzzerDriver.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerExtFunctionsDlsym.o: FuzzerExtFunctionsDlsym.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerExtFunctionsWeak.o: FuzzerExtFunctionsWeak.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerExtFunctionsWindows.o: FuzzerExtFunctionsWindows.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerExtraCounters.o: FuzzerExtraCounters.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerFork.o: FuzzerFork.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerIO.o: FuzzerIO.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerIOPosix.o: FuzzerIOPosix.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerIOWindows.o: FuzzerIOWindows.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerLoop.o: FuzzerLoop.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerMerge.o: FuzzerMerge.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerMutate.o: FuzzerMutate.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerSHA1.o: FuzzerSHA1.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerTracePC.o: FuzzerTracePC.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtil.o: FuzzerUtil.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilDarwin.o: FuzzerUtilDarwin.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilFuchsia.o: FuzzerUtilFuchsia.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilLinux.o: FuzzerUtilLinux.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilPosix.o: FuzzerUtilPosix.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilWindows.o: FuzzerUtilWindows.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +libfuzzer.o: libfuzzer.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +libfuzzer-mutator.so: FuzzerCrossOver.o FuzzerDataFlowTrace.o FuzzerDriver.o FuzzerExtFunctionsDlsym.o FuzzerExtFunctionsWeak.o FuzzerExtFunctionsWindows.o FuzzerExtraCounters.o FuzzerFork.o FuzzerIO.o FuzzerIOPosix.o FuzzerIOWindows.o FuzzerLoop.o FuzzerMerge.o FuzzerMutate.o FuzzerSHA1.o FuzzerTracePC.o FuzzerUtil.o FuzzerUtilDarwin.o FuzzerUtilFuchsia.o FuzzerUtilLinux.o FuzzerUtilPosix.o FuzzerUtilWindows.o libfuzzer.o + $(CC) $(CFLAGS) -I../../include -I. -shared -o libfuzzer-mutator.so *.o + +clean: + rm -f *.o *~ *.so core diff --git a/custom_mutators/libfuzzer/README.md b/custom_mutators/libfuzzer/README.md new file mode 100644 index 00000000..a773da02 --- /dev/null +++ b/custom_mutators/libfuzzer/README.md @@ -0,0 +1,24 @@ +# custum mutator: libfuzzer LLVMFuzzerMutate() + +This uses the libfuzzer LLVMFuzzerMutate() function in llvm 12. + +just type `make` to build + +```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/libfuzzer/libfuzzer-mutator.so afl-fuzz ...``` + +Note that is is currently simple and is missing two features: + * Splicing ("Crossover") + * Dictionary support + +To update the source, all that is needed is that FuzzerDriver.cpp has to receive +``` +#include "libfuzzer.inc" +``` +before the closing namespace bracket. + +It is also libfuzzer.inc where the configuration of the libfuzzer mutations +are done. + +> Original repository: https://github.com/llvm/llvm-project +> Path: compiler-rt/lib/fuzzer/*.{h|cpp} +> Source commit: d4b88ac1658d681e143482336cac27c6a74b8b24 diff --git a/custom_mutators/libfuzzer/libfuzzer.cpp b/custom_mutators/libfuzzer/libfuzzer.cpp new file mode 100644 index 00000000..cf41af2d --- /dev/null +++ b/custom_mutators/libfuzzer/libfuzzer.cpp @@ -0,0 +1,147 @@ +#include +#include +#include +#include +//#include "config.h" +//#include "debug.h" +#include "afl-fuzz.h" + +afl_state_t *afl_struct; + +extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +extern "C" int LLVMFuzzerRunDriver(int *argc, char ***argv, + int (*UserCb)(const uint8_t *Data, + size_t Size)); +extern "C" void LLVMFuzzerMyInit(int (*UserCb)(const uint8_t *Data, + size_t Size), + unsigned int Seed); + +typedef struct my_mutator { + + afl_state_t *afl; + u8 * mutator_buf; + unsigned int seed; + unsigned int extras_cnt, a_extras_cnt; + +} my_mutator_t; + +extern "C" int dummy(const uint8_t *Data, size_t Size) { + + (void)(Data); + (void)(Size); + fprintf(stderr, "dummy() called\n"); + return 0; + +} + +extern "C" my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) { + + my_mutator_t *data = (my_mutator_t *)calloc(1, sizeof(my_mutator_t)); + if (!data) { + + perror("afl_custom_init alloc"); + return NULL; + + } + + if ((data->mutator_buf = (u8 *)malloc(MAX_FILE)) == NULL) { + + perror("mutator_buf alloc"); + return NULL; + + } + + data->afl = afl; + data->seed = seed; + afl_struct = afl; + + /* + char **argv; + argv = (char**)malloc(sizeof(size_t) * 2); + argv[0] = (char*)"foo"; + argv[1] = NULL; + int eins = 1; + LLVMFuzzerRunDriver(&eins, &argv, dummy); + */ + + LLVMFuzzerMyInit(dummy, seed); + + return data; + +} + +/* When a new queue entry is added we check if there are new dictionary + entries to add to honggfuzz structure */ +#if ß +extern "C" void afl_custom_queue_new_entry(my_mutator_t * data, + const uint8_t *filename_new_queue, + const uint8_t *filename_orig_queue) { + + while (data->extras_cnt < afl_struct->extras_cnt) { + + /* + memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val, + afl_struct->extras[data->extras_cnt].data, + afl_struct->extras[data->extras_cnt].len); + run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len = + afl_struct->extras[data->extras_cnt].len; + run.global->mutate.dictionaryCnt++; + */ + data->extras_cnt++; + + } + + while (data->a_extras_cnt < afl_struct->a_extras_cnt) { + + /* + memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val, + afl_struct->a_extras[data->a_extras_cnt].data, + afl_struct->a_extras[data->a_extras_cnt].len); + run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len = + afl_struct->a_extras[data->a_extras_cnt].len; + run.global->mutate.dictionaryCnt++; + data->a_extras_cnt++; + */ + + } + +} + +#endif +/* we could set only_printable if is_ascii is set ... let's see +uint8_t afl_custom_queue_get(void *data, const uint8_t *filename) { + + //run.global->cfg.only_printable = ... + +} + +*/ + +/* here we run the honggfuzz mutator, which is really good */ + +extern "C" size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, + size_t buf_size, u8 **out_buf, + uint8_t *add_buf, size_t add_buf_size, + size_t max_size) { + + memcpy(data->mutator_buf, buf, buf_size); + size_t ret = LLVMFuzzerMutate(data->mutator_buf, buf_size, max_size); + + /* return size of mutated data */ + *out_buf = data->mutator_buf; + return ret; + +} + +/** + * Deinitialize everything + * + * @param data The data ptr from afl_custom_init + */ +extern "C" void afl_custom_deinit(my_mutator_t *data) { + + free(data->mutator_buf); + free(data); + +} + diff --git a/custom_mutators/libfuzzer/libfuzzer.inc b/custom_mutators/libfuzzer/libfuzzer.inc new file mode 100644 index 00000000..01f21dbe --- /dev/null +++ b/custom_mutators/libfuzzer/libfuzzer.inc @@ -0,0 +1,36 @@ + + +extern "C" ATTRIBUTE_INTERFACE void +LLVMFuzzerMyInit(int (*Callback)(const uint8_t *Data, size_t Size), unsigned int Seed) { + Random Rand(Seed); + FuzzingOptions Options; + Options.Verbosity = 3; + Options.MaxLen = 1024000; + Options.LenControl = true; + Options.DoCrossOver = false; + Options.MutateDepth = 6; + Options.UseCounters = false; + Options.UseMemmem = false; + Options.UseCmp = false; + Options.UseValueProfile = false; + Options.Shrink = false; + Options.ReduceInputs = false; + Options.PreferSmall = false; + Options.ReloadIntervalSec = 0; + Options.OnlyASCII = false; + Options.DetectLeaks = false; + Options.PurgeAllocatorIntervalSec = 0; + Options.TraceMalloc = false; + Options.RssLimitMb = 100; + Options.MallocLimitMb = 100; + Options.MaxNumberOfRuns = 0; + Options.ReportSlowUnits = false; + Options.Entropic = false; + + struct EntropicOptions Entropic; + Entropic.Enabled = Options.Entropic; + EF = new ExternalFunctions(); + auto *MD = new MutationDispatcher(Rand, Options); + auto *Corpus = new InputCorpus(Options.OutputCorpus, Entropic); + auto *F = new Fuzzer(Callback, *Corpus, *MD, Options); +} diff --git a/custom_mutators/symcc/symcc.c b/custom_mutators/symcc/symcc.c index d0572f4e..6f14052f 100644 --- a/custom_mutators/symcc/symcc.c +++ b/custom_mutators/symcc/symcc.c @@ -12,7 +12,8 @@ afl_state_t *afl_struct; #ifdef DEBUG #define DBG(x...) fprintf(stderr, x) #else - #define DBG(x...) {} + #define DBG(x...) \ + {} #endif typedef struct my_mutator { @@ -177,8 +178,8 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, size_t max_size) { struct dirent **nl; - int32_t i, done = 0, items = scandir(data->out_dir, &nl, NULL, NULL); - size_t size = 0; + int32_t i, done = 0, items = scandir(data->out_dir, &nl, NULL, NULL); + size_t size = 0; if (items <= 0) return 0; diff --git a/docs/Changelog.md b/docs/Changelog.md index 1e5c1b95..af52b955 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -33,6 +33,8 @@ sending a mail to . - LTO autodict now also collects interesting cmp comparisons, std::string compare + find + ==, bcmp - added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/ + - added a new custom mutator: libfuzzer that integrates libfuzzer mutations + - Our afl++ Grammar-Mutator is now better integrated into custom_mutators/ ### Version ++2.68c (release) diff --git a/include/afl-prealloc.h b/include/afl-prealloc.h index edf69a67..fa6c9b70 100644 --- a/include/afl-prealloc.h +++ b/include/afl-prealloc.h @@ -60,7 +60,7 @@ typedef enum prealloc_status { \ if ((prealloc_counter) >= (prealloc_size)) { \ \ - el_ptr = (void *)malloc(sizeof(*el_ptr)); \ + el_ptr = (element_t *)malloc(sizeof(*el_ptr)); \ if (!el_ptr) { FATAL("error in list.h -> out of memory for element!"); } \ el_ptr->pre_status = PRE_STATUS_MALLOC; \ \ diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 90701d18..36e47810 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -668,7 +668,7 @@ static inline void *afl_realloc(void **buf, size_t size_needed) { if (likely(*buf)) { /* the size is always stored at buf - 1*size_t */ - new_buf = afl_alloc_bufptr(*buf); + new_buf = (struct afl_alloc_buf *)afl_alloc_bufptr(*buf); current_size = new_buf->complete_size; } @@ -694,7 +694,7 @@ static inline void *afl_realloc(void **buf, size_t size_needed) { } /* alloc */ - new_buf = realloc(new_buf, next_size); + new_buf = (struct afl_alloc_buf *)realloc(new_buf, next_size); if (unlikely(!new_buf)) { *buf = NULL; diff --git a/include/list.h b/include/list.h index 88cbe062..7ec81cbe 100644 --- a/include/list.h +++ b/include/list.h @@ -81,6 +81,7 @@ static inline void list_append(list_t *list, void *el) { } element_t *el_box = NULL; + PRE_ALLOC(el_box, list->element_prealloc_buf, LIST_PREALLOC_SIZE, list->element_prealloc_count); if (!el_box) { FATAL("failed to allocate list element"); } diff --git a/src/afl-cc.c b/src/afl-cc.c index 6bee8b38..a00b240d 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1508,9 +1508,9 @@ int main(int argc, char **argv, char **envp) { if (debug) { - SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + SAYF(cMGN "[D]" cRST " cd '%s';", getthecwd()); for (i = 0; i < argc; i++) - SAYF(" \"%s\"", argv[i]); + SAYF(" '%s'", argv[i]); SAYF("\n"); } @@ -1536,9 +1536,9 @@ int main(int argc, char **argv, char **envp) { if (debug) { - SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + SAYF(cMGN "[D]" cRST " cd '%s';", getthecwd()); for (i = 0; i < cc_par_cnt; i++) - SAYF(" \"%s\"", cc_params[i]); + SAYF(" '%s'", cc_params[i]); SAYF("\n"); } -- cgit 1.4.1 From a9ba907676a63777f82c4029f732670c9389e15e Mon Sep 17 00:00:00 2001 From: Choongwoo Han Date: Sun, 13 Sep 2020 21:25:02 +0900 Subject: Fix qemu argv construction (#555) --- src/afl-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-common.c b/src/afl-common.c index d66440aa..30c67909 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -146,7 +146,7 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { u8 * tmp, *cp = NULL, *rsl, *own_copy; memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1)); - new_argv[argc - 1] = NULL; + new_argv[argc + 2] = NULL; new_argv[2] = *target_path_p; new_argv[1] = "--"; -- cgit 1.4.1 From 060dbe12391b1e4957c09ef8b135f99f2a3bb121 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 13 Sep 2020 14:26:24 +0200 Subject: wine argv fix --- src/afl-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-common.c b/src/afl-common.c index 30c67909..ddae2ac1 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -228,7 +228,7 @@ char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { u8 * tmp, *cp = NULL, *rsl, *own_copy; memcpy(&new_argv[2], &argv[1], (int)(sizeof(char *)) * (argc - 1)); - new_argv[argc - 1] = NULL; + new_argv[argc + 2] = NULL; new_argv[1] = *target_path_p; -- cgit 1.4.1 From 7cdbe3173ef773562e5f117d03a823cd2786a457 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 13 Sep 2020 15:00:10 +0200 Subject: fixed ngram size unset --- src/afl-cc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index a00b240d..c3b8959d 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1108,7 +1108,7 @@ int main(int argc, char **argv, char **envp) { if (!*ptr) { - if ((ptr = getenv("AFL_LLVM_NGRAM_SIZE")) != NULL) + if ((ptr = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL) FATAL( "you must set the NGRAM size with (e.g. for value 2) " "AFL_LLVM_INSTRUMENT=ngram-2"); -- cgit 1.4.1 From 4561a9590fc9a8c9ef3676b119f04c2e6d0794c0 Mon Sep 17 00:00:00 2001 From: Edznux Date: Thu, 17 Sep 2020 01:29:09 +0200 Subject: WIP. basic state working: submitting statsd metrics (path, crashes, hangs) --- include/afl-fuzz.h | 9 ++++++ src/afl-fuzz-run.c | 5 ++- src/afl-fuzz-statsd.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/afl-fuzz-statsd.c (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 1a05f4f4..5fff7feb 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -65,6 +65,8 @@ #include #include +#include + #include #include #ifndef USEMMAP @@ -76,6 +78,7 @@ #include #include #include +#include #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ defined(__NetBSD__) || defined(__DragonFly__) @@ -951,6 +954,12 @@ void maybe_update_plot_file(afl_state_t *, double, double); void show_stats(afl_state_t *); void show_init_stats(afl_state_t *); +/* StatsD */ + +int statsd_init(char *host, int port); +int send_statsd_metric(afl_state_t *afl); +void statsd_format_metric(afl_state_t *afl, char *buff, int bufflen); + /* Run */ fsrv_run_result_t fuzz_run_target(afl_state_t *, afl_forkserver_t *fsrv, u32); diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index d71ec339..8dc0b334 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -901,7 +901,10 @@ common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { afl->stage_cur + 1 == afl->stage_max) { show_stats(afl); - + if(send_statsd_metric(afl)){ + //Change me to something realistic; don't fail on connection / lookup fail for metrics... + exit(1); + } } return 0; diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c new file mode 100644 index 00000000..aa12ca9a --- /dev/null +++ b/src/afl-fuzz-statsd.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "afl-fuzz.h" + + +int sock = 0; +struct sockaddr_in server; +int error = 0; + +int statsd_init(char *host, int port){ + if((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1){ + perror("socket"); + exit(1); + } + + memset(&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_port = htons(port); + + struct addrinfo *result; + struct addrinfo hints; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + if ( (error = getaddrinfo(host, NULL, &hints, &result)) ) { + perror("getaddrinfo"); + exit(1); + } + + memcpy(&(server.sin_addr), &((struct sockaddr_in*)result->ai_addr)->sin_addr, sizeof(struct in_addr)); + freeaddrinfo(result); + + return 0; +} + +int send_statsd_metric(afl_state_t *afl){ + u64 cur_ms = get_cur_time(); + if (cur_ms - afl->stats_last_plot_ms < 1000) { + return 0; + } + + error = statsd_init("127.0.0.1", 12345); + if (error){ + perror("Failed to init statsd client. Aborting"); + return -1; + } + + if(!sock){ + perror("sock"); + return -1; + } + char buff[512]; + statsd_format_metric(afl, buff, 512); + + if (sendto(sock, buff, strlen(buff), 0, (struct sockaddr *) &server, sizeof(server)) == -1) { + perror("sendto"); + return -1; + } + close(sock); + sock=0; + + return 0; +} + + +void statsd_format_metric(afl_state_t *afl, char *buff, int bufflen){ + char *format = "fuzzing.afl.cycle_done:%llu|c\n" + "fuzzing.afl.total_path:%lu|c\n" + "fuzzing.afl.unique_crashes:%llu|c\n" + "fuzzing.afl.total_crashes:%llu|c\n" + "fuzzing.afl.unique_hangs:%llu|c\n"; + snprintf(buff, bufflen, format, + afl->queue_cycle, + afl->queued_paths, + afl->unique_crashes, + afl->total_crashes, + afl->unique_hangs + ); +} \ No newline at end of file -- cgit 1.4.1 From 9eed8fe58895fd4a20aa7b5f180b1bfaebf42cd7 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Fri, 18 Sep 2020 09:02:43 +0200 Subject: portability: type -> command -v, compiler warnings --- GNUmakefile.gcc_plugin | 2 +- GNUmakefile.llvm | 10 +++++----- src/afl-fuzz-init.c | 4 +++- 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/GNUmakefile.gcc_plugin b/GNUmakefile.gcc_plugin index fe2dc266..55b79182 100644 --- a/GNUmakefile.gcc_plugin +++ b/GNUmakefile.gcc_plugin @@ -103,7 +103,7 @@ endif .PHONY: test_deps test_deps: @echo "[*] Checking for working '$(CC)'..." - @type $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 ) + @command -v $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 ) # @echo "[*] Checking for gcc for plugin support..." # @$(CC) -v 2>&1 | grep -q -- --enable-plugin || ( echo "[-] Oops, this gcc has not been configured with plugin support."; exit 1 ) @echo "[*] Checking for gcc plugin development header files..." diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 39ddba3c..11ed0bd6 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -116,9 +116,9 @@ ifeq "$(shell test -e $(CC) || echo 1 )" "1" endif # llvm-config --bindir may not providing a valid path, so ... ifeq "$(shell test -e $(CXX) || echo 1 )" "1" - # however we must ensure that this is not a "CC=gcc make" + # however we must ensure that this is not a "CXX=g++ make" ifeq "$(shell command -v $(CXX) 2> /dev/null)" "" - # we do not have a valid CC variable so we try alternatives + # we do not have a valid CXX variable so we try alternatives ifeq "$(shell test -e '$(BIN_DIR)/clang++' && echo 1)" "1" # we found one in the local install directory, lets use these CXX = $(BIN_DIR)/clang++ @@ -175,10 +175,10 @@ endif # Now it can happen that CC points to clang - but there is no clang on the # system. Then we fall back to cc # -ifeq "$(shell type $(CC))" "" +ifeq "$(shell command -v $(CC) 2>/dev/null)" "" CC = cc endif -ifeq "$(shell type $(CXX))" "" +ifeq "$(shell command -v $(CXX) 2>/dev/null)" "" CXX = c++ endif @@ -476,7 +476,7 @@ vpath % .. %.8: % @echo .TH $* 8 $(BUILD_DATE) "afl++" > ./$@ @echo .SH NAME >> ./$@ - @printf ".B $* \- " >> ../$@ + @printf "%s" ".B $* \- " >> ../$@ @./$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> ../$@ @echo .B $* >> ./$@ @echo >> ./$@ diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 29c8c6fa..cbac3822 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -311,7 +311,7 @@ void bind_to_free_cpu(afl_state_t *afl) { } - for (i = 0; i < proccount; i++) { + for (i = 0; i < (s32)proccount; i++) { if (procs[i].p_cpuid < sizeof(cpu_used) && procs[i].p_pctcpu > 0) cpu_used[procs[i].p_cpuid] = 1; @@ -2182,6 +2182,8 @@ void check_cpu_governor(afl_state_t *afl) { "drop.\n", min / 1024, max / 1024); FATAL("Suboptimal CPU scaling governor"); +#else + (void)afl; #endif } -- cgit 1.4.1 From 44c0dc6d961853806a07fa05b948686392ea93fc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 18 Sep 2020 12:19:27 +0200 Subject: fix expand havoc --- instrumentation/afl-compiler-rt.o.c | 2 +- src/afl-fuzz.c | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 2fbefd70..f38af668 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -35,7 +35,7 @@ #include #ifndef __HAIKU__ -#include + #include #endif #include #include diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 9196d78b..ea24011e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -242,11 +242,11 @@ static int stricmp(char const *a, char const *b) { int main(int argc, char **argv_orig, char **envp) { - s32 opt, i; - u64 prev_queued = 0; - u32 sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE; - u8 * extras_dir[4]; - u8 mem_limit_given = 0, exit_1 = 0, debug = 0, extras_dir_cnt = 0; + s32 opt, i; + u64 prev_queued = 0; + u32 sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE; + u8 *extras_dir[4]; + u8 mem_limit_given = 0, exit_1 = 0, debug = 0, extras_dir_cnt = 0, have_p = 0; char **use_argv; struct timeval tv; @@ -364,6 +364,8 @@ int main(int argc, char **argv_orig, char **envp) { } + have_p = 1; + break; case 'e': @@ -1364,7 +1366,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->expand_havoc = 2; break; case 2: - // afl->cycle_schedules = 1; + if (!have_p) afl->schedule = EXPLOIT; afl->expand_havoc = 3; break; case 3: -- cgit 1.4.1 From 7ff9800804f6f4f88b70d8aaf882b66e0ce40c8f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 19 Sep 2020 15:13:15 +0200 Subject: fix exploit, mmopt and rare schedule --- include/afl-fuzz.h | 14 +++++++------- src/afl-fuzz-queue.c | 2 +- src/afl-fuzz.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 9e469864..9404c417 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -243,13 +243,13 @@ enum { enum { /* 00 */ EXPLORE, /* AFL default, Exploration-based constant schedule */ - /* 01 */ EXPLOIT, /* AFL's exploitation-based const. */ - /* 02 */ FAST, /* Exponential schedule */ - /* 03 */ COE, /* Cut-Off Exponential schedule */ - /* 04 */ LIN, /* Linear schedule */ - /* 05 */ QUAD, /* Quadratic schedule */ - /* 06 */ RARE, /* Rare edges */ - /* 07 */ MMOPT, /* Modified MOPT schedule */ + /* 01 */ MMOPT, /* Modified MOPT schedule */ + /* 02 */ EXPLOIT, /* AFL's exploitation-based const. */ + /* 03 */ FAST, /* Exponential schedule */ + /* 04 */ COE, /* Cut-Off Exponential schedule */ + /* 05 */ LIN, /* Linear schedule */ + /* 06 */ QUAD, /* Quadratic schedule */ + /* 07 */ RARE, /* Rare edges */ /* 08 */ SEEK, /* EXPLORE that ignores timings */ POWER_SCHEDULES_NUM diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 336b7f4f..ddd08f1c 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -710,7 +710,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { } - if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) { + if (unlikely(afl->schedule >= EXPLOIT && afl->schedule <= QUAD)) { if (factor > MAX_FACTOR) { factor = MAX_FACTOR; } perf_score *= factor / POWER_BETA; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index ea24011e..11037f73 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1366,7 +1366,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->expand_havoc = 2; break; case 2: - if (!have_p) afl->schedule = EXPLOIT; + //if (!have_p) afl->schedule = EXPLOIT; afl->expand_havoc = 3; break; case 3: -- cgit 1.4.1 From a18523f0186548c27026f85151e354a085763ce1 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 19 Sep 2020 15:13:58 +0200 Subject: fix exploit, mmopt and rare schedule --- src/afl-fuzz.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 11037f73..8cfd79e2 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -246,7 +246,7 @@ int main(int argc, char **argv_orig, char **envp) { u64 prev_queued = 0; u32 sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE; u8 *extras_dir[4]; - u8 mem_limit_given = 0, exit_1 = 0, debug = 0, extras_dir_cnt = 0, have_p = 0; + u8 mem_limit_given = 0, exit_1 = 0, debug = 0, extras_dir_cnt = 0/*, have_p = 0*/; char **use_argv; struct timeval tv; @@ -364,7 +364,7 @@ int main(int argc, char **argv_orig, char **envp) { } - have_p = 1; + //have_p = 1; break; -- cgit 1.4.1 From 5f52f72761fd6bcb2cd9c97fb1cd3a6f05f28ccd Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 20 Sep 2020 14:58:08 +0200 Subject: set explore as default, fix schedule display --- custom_mutators/libfuzzer/Makefile | 2 +- src/afl-fuzz-state.c | 8 ++++---- src/afl-fuzz.c | 9 +++++---- 3 files changed, 10 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/custom_mutators/libfuzzer/Makefile b/custom_mutators/libfuzzer/Makefile index f0c80392..34a358ac 100644 --- a/custom_mutators/libfuzzer/Makefile +++ b/custom_mutators/libfuzzer/Makefile @@ -1,7 +1,7 @@ #CFLAGS = -O3 -funroll-loops -fPIC -fpermissive -std=c++11 CFLAGS = -g -O0 -fPIC -fpermissive -std=c++11 -CC := clang++ +CC ?= clang++ all: libfuzzer-mutator.so diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 4e817843..ae45d571 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -30,9 +30,9 @@ s8 interesting_8[] = {INTERESTING_8}; s16 interesting_16[] = {INTERESTING_8, INTERESTING_16}; s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32}; -char *power_names[POWER_SCHEDULES_NUM] = {"explore", "exploit", "fast", - "coe", "lin", "quad", - "rare", "mmopt", "seek"}; +char *power_names[POWER_SCHEDULES_NUM] = {"explore", "mmopt", "exploit", + "fast", "coe", "lin", + "quad", "rare", "seek"}; /* Initialize MOpt "globals" for this afl state */ @@ -87,7 +87,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->w_end = 0.3; afl->g_max = 5000; afl->period_pilot_tmp = 5000.0; - afl->schedule = SEEK; /* Power schedule (default: SEEK) */ + afl->schedule = EXPLORE; /* Power schedule (default: EXPLORE) */ afl->havoc_max_mult = HAVOC_MAX_MULT; afl->clear_screen = 1; /* Window resized? */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 8cfd79e2..002be0be 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -90,7 +90,7 @@ static void usage(u8 *argv0, int more_help) { "Execution control settings:\n" " -p schedule - power schedules compute a seed's performance score:\n" - " -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " @@ -246,7 +246,8 @@ int main(int argc, char **argv_orig, char **envp) { u64 prev_queued = 0; u32 sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE; u8 *extras_dir[4]; - u8 mem_limit_given = 0, exit_1 = 0, debug = 0, extras_dir_cnt = 0/*, have_p = 0*/; + u8 mem_limit_given = 0, exit_1 = 0, debug = 0, + extras_dir_cnt = 0 /*, have_p = 0*/; char **use_argv; struct timeval tv; @@ -364,7 +365,7 @@ int main(int argc, char **argv_orig, char **envp) { } - //have_p = 1; + // have_p = 1; break; @@ -1366,7 +1367,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->expand_havoc = 2; break; case 2: - //if (!have_p) afl->schedule = EXPLOIT; + // if (!have_p) afl->schedule = EXPLOIT; afl->expand_havoc = 3; break; case 3: -- cgit 1.4.1 From 52c135e1a2cf88f3b578af54962feb8272d12fd4 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 22 Sep 2020 02:05:31 +0200 Subject: fix warning --- src/afl-cc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index c3b8959d..c516dc4c 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1537,7 +1537,7 @@ int main(int argc, char **argv, char **envp) { if (debug) { SAYF(cMGN "[D]" cRST " cd '%s';", getthecwd()); - for (i = 0; i < cc_par_cnt; i++) + for (i = 0; i < (s32)cc_par_cnt; i++) SAYF(" '%s'", cc_params[i]); SAYF("\n"); -- cgit 1.4.1 From 888d63748a3c6aafd974cb9d96cdb8d3916e82bb Mon Sep 17 00:00:00 2001 From: Vitalii Akolzin Date: Thu, 24 Sep 2020 18:25:32 +0300 Subject: Fix potential endless loop in custom_mutator_stage Co-authored-by: Ivan Gulakov --- include/afl-fuzz.h | 3 +++ src/afl-fuzz-one.c | 64 ++++++++++++++++++++++++++++++++-------------------- src/afl-fuzz-queue.c | 2 ++ src/afl-fuzz-state.c | 2 ++ 4 files changed, 46 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 9404c417..0efd48ec 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -657,6 +657,9 @@ typedef struct afl_state { * they do not call another function */ u8 *map_tmp_buf; + /* queue entries ready for splicing count (len > 1) */ + u32 ready_for_splicing_count; + } afl_state_t; struct custom_mutator { diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 5737c1f5..edae2a88 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1696,50 +1696,58 @@ custom_mutator_stage: struct queue_entry *target; u32 tid; - u8 * new_buf; + u8 * new_buf = NULL; + u32 target_len = 0; - retry_external_pick: - /* Pick a random other queue entry for passing to external API */ + if (afl->ready_for_splicing_count > 1 || + (afl->ready_for_splicing_count == 1 && + afl->queue_cur->len == 1)) { - do { + retry_external_pick: + /* Pick a random other queue entry for passing to external API */ - tid = rand_below(afl, afl->queued_paths); + do { - } while (tid == afl->current_entry && afl->queued_paths > 1); + tid = rand_below(afl, afl->queued_paths); - afl->splicing_with = tid; - target = afl->queue_buf[tid]; + } while (tid == afl->current_entry && afl->queued_paths > 1); - /* Make sure that the target has a reasonable length. */ + afl->splicing_with = tid; + target = afl->queue_buf[tid]; - while (target && (target->len < 2 || target == afl->queue_cur) && - afl->queued_paths > 3) { + /* Make sure that the target has a reasonable length. */ - target = target->next; - ++afl->splicing_with; + while (target && (target->len < 2 || target == afl->queue_cur) && + afl->queued_paths > 2) { - } + target = target->next; + ++afl->splicing_with; - if (!target) { goto retry_external_pick; } + } - /* Read the additional testcase into a new buffer. */ - fd = open(target->fname, O_RDONLY); - if (unlikely(fd < 0)) { + if (!target) { goto retry_external_pick; } - PFATAL("Unable to open '%s'", target->fname); + /* Read the additional testcase into a new buffer. */ + fd = open(target->fname, O_RDONLY); + if (unlikely(fd < 0)) { - } + PFATAL("Unable to open '%s'", target->fname); - new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), target->len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - ck_read(fd, new_buf, target->len, target->fname); - close(fd); + } + + new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), target->len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } + ck_read(fd, new_buf, target->len, target->fname); + close(fd); + target_len = target->len; + + } u8 *mutated_buf = NULL; size_t mutated_size = el->afl_custom_fuzz(el->data, out_buf, len, &mutated_buf, new_buf, - target->len, max_seed_size); + target_len, max_seed_size); if (unlikely(!mutated_buf)) { @@ -2738,6 +2746,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { if (!afl->non_instrumented_mode && !afl->queue_cur->trim_done) { + u32 old_len = afl->queue_cur->len; + u8 res = trim_case(afl, afl->queue_cur, in_buf); if (res == FSRV_RUN_ERROR) { @@ -2759,6 +2769,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { len = afl->queue_cur->len; + /* maybe current entry stop being ready for splicing */ + if (old_len > 1 && afl->queue_cur->len == 1) + afl->ready_for_splicing_count--; + } memcpy(out_buf, in_buf, len); diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index ddd08f1c..14aa34fc 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -234,6 +234,8 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { } + if (q->len > 1) afl->ready_for_splicing_count++; + ++afl->queued_paths; ++afl->pending_not_fuzzed; diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index ae45d571..9f68bb51 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -155,6 +155,8 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->stats_last_execs = 0; afl->stats_avg_exec = -1; + afl->ready_for_splicing_count = 0; + init_mopt_globals(afl); list_append(&afl_states, afl); -- cgit 1.4.1 From a75e7594f78454a11e3d93b3cb4878a21e4e943f Mon Sep 17 00:00:00 2001 From: Vitalii Akolzin Date: Thu, 24 Sep 2020 18:50:59 +0300 Subject: Add comments Co-authored-by: Ivan Gulakov --- src/afl-fuzz-one.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index edae2a88..8c1aa179 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1699,6 +1699,9 @@ custom_mutator_stage: u8 * new_buf = NULL; u32 target_len = 0; + /* check if splicing is possible (if the only entry has len > 1 + * check it is not current entry) + */ if (afl->ready_for_splicing_count > 1 || (afl->ready_for_splicing_count == 1 && afl->queue_cur->len == 1)) { @@ -2769,7 +2772,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { len = afl->queue_cur->len; - /* maybe current entry stop being ready for splicing */ + /* maybe current entry is not ready for splicing anymore */ if (old_len > 1 && afl->queue_cur->len == 1) afl->ready_for_splicing_count--; -- cgit 1.4.1 From 6b3b1775b6b274bc62f9c79f686fc79fa110d0a8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 25 Sep 2020 12:03:24 +0200 Subject: improving on splice candidate check patch --- include/afl-fuzz.h | 4 ++-- include/config.h | 2 +- src/afl-fuzz-one.c | 33 +++++++++------------------------ src/afl-fuzz-queue.c | 2 +- src/afl-fuzz-state.c | 48 +++++++++++++----------------------------------- 5 files changed, 26 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 0efd48ec..441ecc61 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -483,7 +483,7 @@ typedef struct afl_state { disable_trim, /* Never trim in fuzz_one */ shmem_testcase_mode, /* If sharedmem testcases are used */ expand_havoc, /* perform expensive havoc after no find */ - cycle_schedules; /* cycle power schedules ? */ + cycle_schedules; /* cycle power schedules? */ u8 *virgin_bits, /* Regions yet untouched by fuzzing */ *virgin_tmout, /* Bits we haven't seen in tmouts */ @@ -657,7 +657,7 @@ typedef struct afl_state { * they do not call another function */ u8 *map_tmp_buf; - /* queue entries ready for splicing count (len > 1) */ + /* queue entries ready for splicing count (len > 4) */ u32 ready_for_splicing_count; } afl_state_t; diff --git a/include/config.h b/include/config.h index a01491e7..7c8e0c7d 100644 --- a/include/config.h +++ b/include/config.h @@ -136,7 +136,7 @@ two cycles where smaller blocks are favored: */ #define HAVOC_BLK_SMALL 32 -#define HAVOC_BLK_MEDIUM 128 +#define HAVOC_BLK_MEDIUM 128 #define HAVOC_BLK_LARGE 1500 /* Extra-large blocks, selected very rarely (<5% of the time): */ diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 8c1aa179..e96c4311 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1699,36 +1699,22 @@ custom_mutator_stage: u8 * new_buf = NULL; u32 target_len = 0; - /* check if splicing is possible (if the only entry has len > 1 - * check it is not current entry) - */ - if (afl->ready_for_splicing_count > 1 || - (afl->ready_for_splicing_count == 1 && - afl->queue_cur->len == 1)) { + /* check if splicing makes sense yet (enough entries) */ + if (likely(afl->ready_for_splicing_count > 1)) { - retry_external_pick: - /* Pick a random other queue entry for passing to external API */ + /* Pick a random other queue entry for passing to external API + that has the necessary length */ do { tid = rand_below(afl, afl->queued_paths); - } while (tid == afl->current_entry && afl->queued_paths > 1); - - afl->splicing_with = tid; - target = afl->queue_buf[tid]; - - /* Make sure that the target has a reasonable length. */ - - while (target && (target->len < 2 || target == afl->queue_cur) && - afl->queued_paths > 2) { - - target = target->next; - ++afl->splicing_with; + } while (unlikely(tid == afl->current_entry && - } + afl->queue_buf[tid]->len >= 4)); - if (!target) { goto retry_external_pick; } + target = afl->queue_buf[tid]; + afl->splicing_with = tid; /* Read the additional testcase into a new buffer. */ fd = open(target->fname, O_RDONLY); @@ -2773,8 +2759,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { len = afl->queue_cur->len; /* maybe current entry is not ready for splicing anymore */ - if (old_len > 1 && afl->queue_cur->len == 1) - afl->ready_for_splicing_count--; + if (unlikely(len <= 4 && old_len > 4)) afl->ready_for_splicing_count--; } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 14aa34fc..53c3e984 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -234,7 +234,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { } - if (q->len > 1) afl->ready_for_splicing_count++; + if (likely(q->len > 4)) afl->ready_for_splicing_count++; ++afl->queued_paths; ++afl->pending_not_fuzzed; diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 9f68bb51..5e0995fe 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -95,6 +95,11 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->stage_name = "init"; /* Name of the current fuzz stage */ afl->splicing_with = -1; /* Splicing with which test case? */ afl->cpu_to_bind = -1; + afl->cal_cycles = CAL_CYCLES; + afl->cal_cycles_long = CAL_CYCLES_LONG; + afl->hang_tmout = EXEC_TIMEOUT; + afl->stats_update_freq = 1; + afl->stats_avg_exec = -1; #ifdef HAVE_AFFINITY afl->cpu_aff = -1; /* Selected CPU core */ @@ -115,48 +120,13 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { // afl_state_t is not available in forkserver.c afl->fsrv.afl_ptr = (void *)afl; afl->fsrv.add_extra_func = (void (*)(void *, u8 *, u32)) & add_extra; - - afl->cal_cycles = CAL_CYCLES; - afl->cal_cycles_long = CAL_CYCLES_LONG; - afl->fsrv.exec_tmout = EXEC_TIMEOUT; - afl->hang_tmout = EXEC_TIMEOUT; - afl->fsrv.mem_limit = MEM_LIMIT; - - afl->stats_update_freq = 1; - afl->fsrv.dev_urandom_fd = -1; afl->fsrv.dev_null_fd = -1; - afl->fsrv.child_pid = -1; afl->fsrv.out_dir_fd = -1; - afl->cmplog_prev_timed_out = 0; - - /* statis file */ - afl->last_bitmap_cvg = 0; - afl->last_stability = 0; - afl->last_eps = 0; - - /* plot file saves from last run */ - afl->plot_prev_qp = 0; - afl->plot_prev_pf = 0; - afl->plot_prev_pnf = 0; - afl->plot_prev_ce = 0; - afl->plot_prev_md = 0; - afl->plot_prev_qc = 0; - afl->plot_prev_uc = 0; - afl->plot_prev_uh = 0; - - afl->stats_last_stats_ms = 0; - afl->stats_last_plot_ms = 0; - afl->stats_last_ms = 0; - afl->stats_last_execs = 0; - afl->stats_avg_exec = -1; - - afl->ready_for_splicing_count = 0; - init_mopt_globals(afl); list_append(&afl_states, afl); @@ -177,6 +147,14 @@ void read_afl_environment(afl_state_t *afl, char **envp) { WARNF("Potentially mistyped AFL environment variable: %s", env); issue_detected = 1; + } else if (strncmp(env, "USE_", 4) == 0) { + + WARNF( + "Potentially mistyped AFL environment variable: %s, did you mean " + "AFL_%s?", + env, env); + issue_detected = 1; + } else if (strncmp(env, "AFL_", 4) == 0) { int i = 0, match = 0; -- cgit 1.4.1 From a55e0d11891f0cc19a4ec6cc67c4e5e0d1c7f465 Mon Sep 17 00:00:00 2001 From: Edznux Date: Fri, 25 Sep 2020 23:28:15 +0200 Subject: WIP envs --- include/afl-fuzz.h | 2 +- include/envs.h | 2 ++ src/afl-fuzz-statsd.c | 70 ++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 58 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 5fff7feb..65327d93 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -958,7 +958,7 @@ void show_init_stats(afl_state_t *); int statsd_init(char *host, int port); int send_statsd_metric(afl_state_t *afl); -void statsd_format_metric(afl_state_t *afl, char *buff, int bufflen); +int statsd_format_metric(afl_state_t *afl, char *formatted[], size_t *num_of_tags); /* Run */ diff --git a/include/envs.h b/include/envs.h index 2dc1dbbf..6776a7cd 100644 --- a/include/envs.h +++ b/include/envs.h @@ -129,6 +129,8 @@ static char *afl_environment_variables[] = { "AFL_SKIP_BIN_CHECK", "AFL_SKIP_CPUFREQ", "AFL_SKIP_CRASHES", + "AFL_STATSD_HOST", + "AFL_STATSD_PORT", "AFL_TMIN_EXACT", "AFL_TMPDIR", "AFL_TOKEN_FILE", diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index aa12ca9a..d09d1f6e 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -9,6 +9,9 @@ #include "afl-fuzz.h" +#define MAX_STATSD_PACKET_SIZE 1024 +#define MAX_TAG_LEN 200 + int sock = 0; struct sockaddr_in server; int error = 0; @@ -46,8 +49,21 @@ int send_statsd_metric(afl_state_t *afl){ if (cur_ms - afl->stats_last_plot_ms < 1000) { return 0; } + + u16 port = 8125; + char* host = "127.0.0.1"; - error = statsd_init("127.0.0.1", 12345); + char* port_env; + char* host_env; + if ((port_env = getenv("AFL_STATSD_PORT")) != NULL) { + port = atoi(port_env); + } + if ((host_env = getenv("AFL_STATSD_HOST")) != NULL) { + // sanitization check ? + host = host_env; + } + + error = statsd_init(host, port); if (error){ perror("Failed to init statsd client. Aborting"); return -1; @@ -57,31 +73,55 @@ int send_statsd_metric(afl_state_t *afl){ perror("sock"); return -1; } - char buff[512]; - statsd_format_metric(afl, buff, 512); - - if (sendto(sock, buff, strlen(buff), 0, (struct sockaddr *) &server, sizeof(server)) == -1) { - perror("sendto"); - return -1; + char *formatted[] = {}; + size_t *num_of_tags = 0; + statsd_format_metric(afl, &formatted, num_of_tags); + for (size_t i = 0; i < &num_of_tags; i++){ + printf("%ld\n", i); + printf("%s\n", formatted[i]); + if (sendto(sock, formatted[i], strlen(formatted[i]), 0, (struct sockaddr *) &server, sizeof(server)) == -1) { + perror("sendto"); + return -1; + } } + close(sock); sock=0; return 0; } +int statsd_format_metric(afl_state_t *afl, char *formatted[], size_t *num_of_tags){ + + char *tags = "key:value"; + + *num_of_tags = 0; // reset + + const char *metrics[] = { + "fuzzing.afl.cycle_done:%llu|g|#%s\n", + "fuzzing.afl.total_path:%lu|g|#%s\n", + "fuzzing.afl.unique_crashes:%llu|g|#%s\n", + "fuzzing.afl.total_crashes:%llu|g|#%s\n", + "fuzzing.afl.unique_hangs:%llu|g|#%s\n" + }; -void statsd_format_metric(afl_state_t *afl, char *buff, int bufflen){ - char *format = "fuzzing.afl.cycle_done:%llu|c\n" - "fuzzing.afl.total_path:%lu|c\n" - "fuzzing.afl.unique_crashes:%llu|c\n" - "fuzzing.afl.total_crashes:%llu|c\n" - "fuzzing.afl.unique_hangs:%llu|c\n"; - snprintf(buff, bufflen, format, + const int metricValues[] = { afl->queue_cycle, afl->queued_paths, afl->unique_crashes, afl->total_crashes, afl->unique_hangs - ); + }; + + *num_of_tags = sizeof(metrics)/sizeof(metrics[0]); + + for (size_t i = 0; i < &num_of_tags; i++){ + char *tmp = malloc(MAX_STATSD_PACKET_SIZE); + if(tmp == NULL){ + return -1; + } + snprintf(tmp, MAX_STATSD_PACKET_SIZE, metrics[i], metricValues[i], tags); + formatted[i] = tmp; + } + return 0; } \ No newline at end of file -- cgit 1.4.1 From e69b25e34be8028921389bbb114135c3028d0a3d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 28 Sep 2020 10:13:00 +0200 Subject: increase havoc_stack_pow2 on no finds --- include/afl-fuzz.h | 1 + src/afl-fuzz-one.c | 4 ++-- src/afl-fuzz-state.c | 1 + src/afl-fuzz.c | 5 +++++ 4 files changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 441ecc61..aa278820 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -443,6 +443,7 @@ typedef struct afl_state { u8 cal_cycles, /* Calibration cycles defaults */ cal_cycles_long, /* Calibration cycles defaults */ + havoc_stack_pow2, /* HAVOC_STACK_POW2 */ no_unlink, /* do not unlink cur_input */ debug, /* Debug mode */ custom_only, /* Custom mutator only mode */ diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index e96c4311..c04b492b 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1884,7 +1884,7 @@ havoc_stage: for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { - u32 use_stacking = 1 << (1 + rand_below(afl, HAVOC_STACK_POW2)); + u32 use_stacking = 1 << (1 + rand_below(afl, afl->havoc_stack_pow2)); afl->stage_cur_val = use_stacking; @@ -3970,7 +3970,7 @@ pacemaker_fuzzing: for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { - u32 use_stacking = 1 << (1 + rand_below(afl, HAVOC_STACK_POW2)); + u32 use_stacking = 1 << (1 + rand_below(afl, afl->havoc_stack_pow2)); afl->stage_cur_val = use_stacking; diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 5e0995fe..a8e56e60 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -95,6 +95,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->stage_name = "init"; /* Name of the current fuzz stage */ afl->splicing_with = -1; /* Splicing with which test case? */ afl->cpu_to_bind = -1; + afl->havoc_stack_pow2 = HAVOC_STACK_POW2; afl->cal_cycles = CAL_CYCLES; afl->cal_cycles_long = CAL_CYCLES_LONG; afl->hang_tmout = EXEC_TIMEOUT; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 002be0be..28507857 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1368,9 +1368,14 @@ int main(int argc, char **argv_orig, char **envp) { break; case 2: // if (!have_p) afl->schedule = EXPLOIT; + afl->havoc_stack_pow2++; afl->expand_havoc = 3; break; case 3: + afl->havoc_stack_pow2++; + afl->expand_havoc = 4; + break; + case 4: // nothing else currently break; -- cgit 1.4.1 From e87eca7fe8ec3ed0ba79e7722350ad502b67218b Mon Sep 17 00:00:00 2001 From: Marcel Boehme Date: Tue, 29 Sep 2020 11:53:27 +0000 Subject: Patching and improving AFLFast schedules. --- GNUmakefile | 2 +- include/afl-fuzz.h | 4 ++- src/afl-fuzz-bitmap.c | 18 +++-------- src/afl-fuzz-init.c | 8 +++++ src/afl-fuzz-queue.c | 84 +++++++++++++++++++++++++++++++++------------------ src/afl-fuzz.c | 7 +++++ 6 files changed, 78 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index 889c0e7d..c885a935 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -205,7 +205,7 @@ endif ifneq "$(filter Linux GNU%,$(shell uname))" "" override CFLAGS += -D_FORTIFY_SOURCE=2 - LDFLAGS += -ldl -lrt + LDFLAGS += -ldl -lrt -lm endif ifneq "$(findstring FreeBSD, $(shell uname))" "" diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index aa278820..f65fc40f 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -155,7 +155,6 @@ struct queue_entry { u64 exec_us, /* Execution time (us) */ handicap, /* Number of queue cycles behind */ - n_fuzz, /* Number of fuzz, does not overflow*/ depth, /* Path depth */ exec_cksum; /* Checksum of the execution trace */ @@ -492,6 +491,9 @@ typedef struct afl_state { u8 *var_bytes; /* Bytes that appear to be variable */ + #define n_fuzz_size (1 << 21) + u32 *n_fuzz; + volatile u8 stop_soon, /* Ctrl-C pressed? */ clear_screen; /* Window resized? */ diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 1b9df624..64de86a2 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -555,19 +555,9 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); - struct queue_entry *q = afl->queue; - while (q) { - - if (q->exec_cksum == cksum) { - - ++q->n_fuzz; - break; - - } - - q = q->next; - - } + /* Saturated increment */ + if (afl->n_fuzz[cksum % n_fuzz_size] < 0xFFFFFFFF) + afl->n_fuzz[cksum % n_fuzz_size]++; } @@ -610,6 +600,8 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { afl->queue_top->exec_cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); + afl->n_fuzz[cksum % n_fuzz_size] = 1; + /* Try to calibrate inline; this also calls update_bitmap_score() when successful. */ diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index cbac3822..b825837f 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -729,6 +729,14 @@ void read_testcases(afl_state_t *afl, u8 *directory) { add_to_queue(afl, fn2, st.st_size >= MAX_FILE ? MAX_FILE : st.st_size, passed_det); + if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) { + + u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); + + afl->n_fuzz[cksum % n_fuzz_size] = 1; + + } + } free(nl); /* not tracked */ diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 53c3e984..dfabba7b 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -25,6 +25,7 @@ #include "afl-fuzz.h" #include #include +#include /* Mark deterministic checks as done for a particular queue entry. We use the .state file to avoid repeating deterministic fuzzing when resuming aborted @@ -218,7 +219,6 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { q->len = len; q->depth = afl->cur_depth + 1; q->passed_det = passed_det; - q->n_fuzz = 1; q->trace_mini = NULL; if (q->depth > afl->max_depth) { afl->max_depth = q->depth; } @@ -307,8 +307,10 @@ void update_bitmap_score(afl_state_t *afl, struct queue_entry *q) { u64 fav_factor; u64 fuzz_p2; - if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) - fuzz_p2 = next_pow2(q->n_fuzz); + if (unlikely(afl->schedule >= FAST && afl->schedule < RARE)) + fuzz_p2 = 0; // Skip the fuzz_p2 comparison + else if (unlikely(afl->schedule == RARE)) + fuzz_p2 = next_pow2(afl->n_fuzz[q->exec_cksum % n_fuzz_size]); else fuzz_p2 = q->fuzz_level; @@ -334,7 +336,7 @@ void update_bitmap_score(afl_state_t *afl, struct queue_entry *q) { u64 top_rated_fav_factor; u64 top_rated_fuzz_p2; if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) - top_rated_fuzz_p2 = next_pow2(afl->top_rated[i]->n_fuzz); + top_rated_fuzz_p2 = next_pow2(afl->n_fuzz[afl->top_rated[i]->exec_cksum % n_fuzz_size]); else top_rated_fuzz_p2 = afl->top_rated[i]->fuzz_level; @@ -605,11 +607,10 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { } - u64 fuzz = q->n_fuzz; - u64 fuzz_total; + u32 n_paths; + double factor = 1.0; + long double fuzz_mu; - u32 n_paths, fuzz_mu; - u32 factor = 1; switch (afl->schedule) { @@ -624,60 +625,83 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { break; case COE: - fuzz_total = 0; + fuzz_mu = 0.0; n_paths = 0; + // Don't modify perf_score for unfuzzed seeds + if (q->fuzz_level == 0) break; + struct queue_entry *queue_it = afl->queue; while (queue_it) { - fuzz_total += queue_it->n_fuzz; + fuzz_mu += log2(afl->n_fuzz[q->exec_cksum % n_fuzz_size]); n_paths++; + queue_it = queue_it->next; } if (unlikely(!n_paths)) { FATAL("Queue state corrupt"); } - fuzz_mu = fuzz_total / n_paths; - if (fuzz <= fuzz_mu) { + fuzz_mu = fuzz_mu / n_paths; - if (q->fuzz_level < 16) { + if (log2(afl->n_fuzz[q->exec_cksum % n_fuzz_size]) > fuzz_mu) { - factor = ((u32)(1 << q->fuzz_level)); + /* Never skip favourites */ + if (!q->favored) factor = 0; - } else { + break; - factor = MAX_FACTOR; + } - } + // Fall through + case FAST: - } else { + // Don't modify unfuzzed seeds + if (q->fuzz_level == 0) break; - factor = 0; + switch ((u32)log2(afl->n_fuzz[q->exec_cksum % n_fuzz_size])) { - } + case 0 ... 1: + factor = 4; + break; - break; + case 2 ... 3: + factor = 3; + break; - case FAST: - if (q->fuzz_level < 16) { + case 4: + factor = 2; + break; - factor = ((u32)(1 << q->fuzz_level)) / (fuzz == 0 ? 1 : fuzz); + case 5: + break; - } else { + case 6: + if (!q->favored) factor = 0.8; + break; - factor = MAX_FACTOR / (fuzz == 0 ? 1 : next_pow2(fuzz)); + case 7: + if (!q->favored) factor = 0.6; + break; + + default: + if (!q->favored) factor = 0.4; + break; } + if (q->favored) + factor *= 1.15; + break; case LIN: - factor = q->fuzz_level / (fuzz == 0 ? 1 : fuzz); + factor = q->fuzz_level / (afl->n_fuzz[q->exec_cksum % n_fuzz_size] + 1); break; case QUAD: - factor = q->fuzz_level * q->fuzz_level / (fuzz == 0 ? 1 : fuzz); + factor = q->fuzz_level * q->fuzz_level / (afl->n_fuzz[q->exec_cksum % n_fuzz_size] + 1); break; case MMOPT: @@ -703,7 +727,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { // the more often fuzz result paths are equal to this queue entry, // reduce its value perf_score *= - (1 - (double)((double)q->n_fuzz / (double)afl->fsrv.total_execs)); + (1 - (double)((double)afl->n_fuzz[q->exec_cksum % n_fuzz_size] / (double)afl->fsrv.total_execs)); break; @@ -724,7 +748,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { perf_score *= 2; - } else if (perf_score < 1) { + } else if (afl->schedule != COE && perf_score < 1) { // Add a lower bound to AFLFast's energy assignment strategies perf_score = 1; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 28507857..889f753d 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -936,6 +936,13 @@ int main(int argc, char **argv_orig, char **envp) { } + /* Dynamically allocate memory for AFLFast schedules */ + if (afl->schedule >= FAST && afl->schedule <= RARE) { + + afl->n_fuzz = ck_alloc(n_fuzz_size * sizeof(u32)); + + } + if (get_afl_env("AFL_NO_FORKSRV")) { afl->no_forkserver = 1; } if (get_afl_env("AFL_NO_CPU_RED")) { afl->no_cpu_meter_red = 1; } if (get_afl_env("AFL_NO_ARITH")) { afl->no_arith = 1; } -- cgit 1.4.1 From 383cd487a2c28012c80341f8517e473120af4d19 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 29 Sep 2020 15:02:57 +0200 Subject: small improvements to Marcel's patch, fix laf-intel + redqueen crashes --- docs/Changelog.md | 1 + include/afl-fuzz.h | 5 +-- instrumentation/afl-llvm-dict2file.so.cc | 14 ++++++--- instrumentation/cmplog-instructions-pass.cc | 6 ++-- instrumentation/cmplog-routines-pass.cc | 11 ++++--- instrumentation/compare-transform-pass.so.cc | 24 ++++++++------ instrumentation/split-compares-pass.so.cc | 47 +++++++++++++++++----------- instrumentation/split-switches-pass.so.cc | 21 +++++++------ src/afl-fuzz-bitmap.c | 13 +++++--- src/afl-fuzz-init.c | 4 +-- src/afl-fuzz-queue.c | 30 +++++++++--------- src/afl-fuzz.c | 2 +- 12 files changed, 104 insertions(+), 74 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 789b1f74..0f923423 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,7 @@ sending a mail to . - all compilers combined to afl-cc which emulates the previous ones - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o - afl-fuzz + - Marcel Boehme submitted a patch that improves all AFFast schedules :) - reading testcases from -i now descends into subdirectories - allow up to 4 -x command line options - loaded extras now have a duplicate protection diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index f65fc40f..fb661ce5 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -151,7 +151,8 @@ struct queue_entry { is_ascii; /* Is the input just ascii text? */ u32 bitmap_size, /* Number of bits set in bitmap */ - fuzz_level; /* Number of fuzzing iterations */ + fuzz_level, /* Number of fuzzing iterations */ + n_fuzz_entry; /* offset in n_fuzz */ u64 exec_us, /* Execution time (us) */ handicap, /* Number of queue cycles behind */ @@ -491,7 +492,7 @@ typedef struct afl_state { u8 *var_bytes; /* Bytes that appear to be variable */ - #define n_fuzz_size (1 << 21) +#define N_FUZZ_SIZE (1 << 21) u32 *n_fuzz; volatile u8 stop_soon, /* Ctrl-C pressed? */ diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc index e04ebda8..bd8eb27a 100644 --- a/instrumentation/afl-llvm-dict2file.so.cc +++ b/instrumentation/afl-llvm-dict2file.so.cc @@ -381,8 +381,9 @@ bool AFLdict2filePass::runOnModule(Module &M) { if (debug) fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n", - FuncName.c_str(), (void*)Str1P, Str1P->getName().str().c_str(), - Str1.c_str(), HasStr1 == true ? "true" : "false", (void*)Str2P, + FuncName.c_str(), (void *)Str1P, + Str1P->getName().str().c_str(), Str1.c_str(), + HasStr1 == true ? "true" : "false", (void *)Str2P, Str2P->getName().str().c_str(), Str2.c_str(), HasStr2 == true ? "true" : "false"); @@ -436,7 +437,8 @@ bool AFLdict2filePass::runOnModule(Module &M) { valueMap[Str1P] = new std::string(Str2); if (debug) - fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), (void*)Str1P); + fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), + (void *)Str1P); continue; } @@ -455,7 +457,8 @@ bool AFLdict2filePass::runOnModule(Module &M) { Str2 = *strng; HasStr2 = true; if (debug) - fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(), (void*)Str2P); + fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(), + (void *)Str2P); } @@ -497,7 +500,8 @@ bool AFLdict2filePass::runOnModule(Module &M) { Str1 = *strng; HasStr1 = true; if (debug) - fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(), (void*)Str1P); + fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(), + (void *)Str1P); } diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index d5de3dbb..9921de0c 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -210,7 +210,8 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } if (!icomps.size()) return false; - if (!be_quiet) errs() << "Hooking " << icomps.size() << " cmp instructions\n"; + // if (!be_quiet) errs() << "Hooking " << icomps.size() << " cmp + // instructions\n"; for (auto &selectcmpInst : icomps) { @@ -259,8 +260,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { bool CmpLogInstructions::runOnModule(Module &M) { if (getenv("AFL_QUIET") == NULL) - llvm::errs() - << "Running cmplog-instructions-pass by andreafioraldi@gmail.com\n"; + printf("Running cmplog-instructions-pass by andreafioraldi@gmail.com\n"); else be_quiet = 1; hookInstrs(M); diff --git a/instrumentation/cmplog-routines-pass.cc b/instrumentation/cmplog-routines-pass.cc index c44f38c4..e92883ae 100644 --- a/instrumentation/cmplog-routines-pass.cc +++ b/instrumentation/cmplog-routines-pass.cc @@ -149,9 +149,11 @@ bool CmpLogRoutines::hookRtns(Module &M) { } if (!calls.size()) return false; - if (!be_quiet) - errs() << "Hooking " << calls.size() - << " calls with pointers as arguments\n"; + /* + if (!be_quiet) + errs() << "Hooking " << calls.size() + << " calls with pointers as arguments\n"; + */ for (auto &callInst : calls) { @@ -179,8 +181,7 @@ bool CmpLogRoutines::hookRtns(Module &M) { bool CmpLogRoutines::runOnModule(Module &M) { if (getenv("AFL_QUIET") == NULL) - llvm::errs() - << "Running cmplog-routines-pass by andreafioraldi@gmail.com\n"; + printf("Running cmplog-routines-pass by andreafioraldi@gmail.com\n"); else be_quiet = 1; hookRtns(M); diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index 9d2f4a92..3a4abd6e 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -339,8 +339,9 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, if (!calls.size()) return false; if (!be_quiet) - errs() << "Replacing " << calls.size() - << " calls to strcmp/memcmp/strncmp/strcasecmp/strncasecmp\n"; + printf( + "Replacing %lu calls to strcmp/memcmp/strncmp/strcasecmp/strncasecmp\n", + calls.size()); for (auto &callInst : calls) { @@ -426,11 +427,14 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, else unrollLen = constStrLen; - if (!be_quiet) - errs() << callInst->getCalledFunction()->getName() << ": unroll len " - << unrollLen - << ((isSizedcmp && !isConstSized) ? ", variable n" : "") << ": " - << ConstStr << "\n"; + /* + if (!be_quiet) + errs() << callInst->getCalledFunction()->getName() << ": unroll len " + << unrollLen + << ((isSizedcmp && !isConstSized) ? ", variable n" : "") << ": + " + << ConstStr << "\n"; + */ /* split before the call instruction */ BasicBlock *bb = callInst->getParent(); @@ -556,10 +560,12 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, bool CompareTransform::runOnModule(Module &M) { if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) - llvm::errs() << "Running compare-transform-pass by laf.intel@gmail.com, " - "extended by heiko@hexco.de\n"; + printf( + "Running compare-transform-pass by laf.intel@gmail.com, extended by " + "heiko@hexco.de\n"); else be_quiet = 1; + transformCmps(M, true, true, true, true, true); verifyModule(M); diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc index 2fb90e5e..6d0c52a4 100644 --- a/instrumentation/split-compares-pass.so.cc +++ b/instrumentation/split-compares-pass.so.cc @@ -1262,8 +1262,9 @@ bool SplitComparesTransform::runOnModule(Module &M) { if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) { - errs() << "Split-compare-pass by laf.intel@gmail.com, extended by " - "heiko@hexco.de\n"; + printf( + "Split-compare-pass by laf.intel@gmail.com, extended by " + "heiko@hexco.de\n"); } else { @@ -1275,13 +1276,15 @@ bool SplitComparesTransform::runOnModule(Module &M) { count = splitFPCompares(M); - if (!be_quiet) { + /* + if (!be_quiet) { - errs() << "Split-floatingpoint-compare-pass: " << count - << " FP comparisons split\n"; + errs() << "Split-floatingpoint-compare-pass: " << count + << " FP comparisons split\n"; - } + } + */ simplifyFPCompares(M); } @@ -1294,10 +1297,12 @@ bool SplitComparesTransform::runOnModule(Module &M) { case 64: count = splitIntCompares(M, bitw); - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " split\n"; - + /* + if (!be_quiet) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << + count + << " split\n"; + */ bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) @@ -1305,10 +1310,12 @@ bool SplitComparesTransform::runOnModule(Module &M) { #endif case 32: count = splitIntCompares(M, bitw); - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " split\n"; - + /* + if (!be_quiet) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << + count + << " split\n"; + */ bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) @@ -1316,15 +1323,17 @@ bool SplitComparesTransform::runOnModule(Module &M) { #endif case 16: count = splitIntCompares(M, bitw); - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " split\n"; - + /* + if (!be_quiet) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << + count + << " split\n"; + */ bitw >>= 1; break; default: - if (!be_quiet) errs() << "NOT Running split-compare-pass \n"; + // if (!be_quiet) errs() << "NOT Running split-compare-pass \n"; return false; break; diff --git a/instrumentation/split-switches-pass.so.cc b/instrumentation/split-switches-pass.so.cc index a79d4114..97ab04a4 100644 --- a/instrumentation/split-switches-pass.so.cc +++ b/instrumentation/split-switches-pass.so.cc @@ -327,10 +327,11 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) { } if (!switches.size()) return false; - if (!be_quiet) - errs() << "Rewriting " << switches.size() << " switch statements " - << "\n"; - + /* + if (!be_quiet) + errs() << "Rewriting " << switches.size() << " switch statements " + << "\n"; + */ for (auto &SI : switches) { BasicBlock *CurBlock = SI->getParent(); @@ -341,15 +342,17 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) { BasicBlock *Default = SI->getDefaultDest(); unsigned bitw = Val->getType()->getIntegerBitWidth(); - if (!be_quiet) - errs() << "switch: " << SI->getNumCases() << " cases " << bitw - << " bit\n"; + /* + if (!be_quiet) + errs() << "switch: " << SI->getNumCases() << " cases " << bitw + << " bit\n"; + */ /* If there is only the default destination or the condition checks 8 bit or * less, don't bother with the code below. */ if (!SI->getNumCases() || bitw <= 8) { - if (!be_quiet) errs() << "skip trivial switch..\n"; + // if (!be_quiet) errs() << "skip trivial switch..\n"; continue; } @@ -415,7 +418,7 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) { bool SplitSwitchesTransform::runOnModule(Module &M) { if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) - llvm::errs() << "Running split-switches-pass by laf.intel@gmail.com\n"; + printf("Running split-switches-pass by laf.intel@gmail.com\n"); else be_quiet = 1; splitSwitches(M); diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 64de86a2..a22223b9 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -556,8 +556,8 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); /* Saturated increment */ - if (afl->n_fuzz[cksum % n_fuzz_size] < 0xFFFFFFFF) - afl->n_fuzz[cksum % n_fuzz_size]++; + if (afl->n_fuzz[cksum % N_FUZZ_SIZE] < 0xFFFFFFFF) + afl->n_fuzz[cksum % N_FUZZ_SIZE]++; } @@ -597,10 +597,15 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (cksum) afl->queue_top->exec_cksum = cksum; else - afl->queue_top->exec_cksum = + cksum = afl->queue_top->exec_cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); - afl->n_fuzz[cksum % n_fuzz_size] = 1; + if (afl->schedule >= FAST && afl->schedule <= RARE) { + + afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE; + afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1; + + } /* Try to calibrate inline; this also calls update_bitmap_score() when successful. */ diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index b825837f..65478a78 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -732,8 +732,8 @@ void read_testcases(afl_state_t *afl, u8 *directory) { if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) { u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); - - afl->n_fuzz[cksum % n_fuzz_size] = 1; + afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE; + afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1; } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index dfabba7b..0d7d0314 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -308,9 +308,9 @@ void update_bitmap_score(afl_state_t *afl, struct queue_entry *q) { u64 fuzz_p2; if (unlikely(afl->schedule >= FAST && afl->schedule < RARE)) - fuzz_p2 = 0; // Skip the fuzz_p2 comparison + fuzz_p2 = 0; // Skip the fuzz_p2 comparison else if (unlikely(afl->schedule == RARE)) - fuzz_p2 = next_pow2(afl->n_fuzz[q->exec_cksum % n_fuzz_size]); + fuzz_p2 = next_pow2(afl->n_fuzz[q->n_fuzz_entry]); else fuzz_p2 = q->fuzz_level; @@ -336,7 +336,8 @@ void update_bitmap_score(afl_state_t *afl, struct queue_entry *q) { u64 top_rated_fav_factor; u64 top_rated_fuzz_p2; if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) - top_rated_fuzz_p2 = next_pow2(afl->n_fuzz[afl->top_rated[i]->exec_cksum % n_fuzz_size]); + top_rated_fuzz_p2 = + next_pow2(afl->n_fuzz[afl->top_rated[i]->n_fuzz_entry]); else top_rated_fuzz_p2 = afl->top_rated[i]->fuzz_level; @@ -607,11 +608,10 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { } - u32 n_paths; - double factor = 1.0; + u32 n_paths; + double factor = 1.0; long double fuzz_mu; - switch (afl->schedule) { case EXPLORE: @@ -634,7 +634,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { struct queue_entry *queue_it = afl->queue; while (queue_it) { - fuzz_mu += log2(afl->n_fuzz[q->exec_cksum % n_fuzz_size]); + fuzz_mu += log2(afl->n_fuzz[q->n_fuzz_entry]); n_paths++; queue_it = queue_it->next; @@ -645,7 +645,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { fuzz_mu = fuzz_mu / n_paths; - if (log2(afl->n_fuzz[q->exec_cksum % n_fuzz_size]) > fuzz_mu) { + if (log2(afl->n_fuzz[q->n_fuzz_entry]) > fuzz_mu) { /* Never skip favourites */ if (!q->favored) factor = 0; @@ -660,7 +660,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { // Don't modify unfuzzed seeds if (q->fuzz_level == 0) break; - switch ((u32)log2(afl->n_fuzz[q->exec_cksum % n_fuzz_size])) { + switch ((u32)log2(afl->n_fuzz[q->n_fuzz_entry])) { case 0 ... 1: factor = 4; @@ -691,17 +691,17 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { } - if (q->favored) - factor *= 1.15; + if (q->favored) factor *= 1.15; break; case LIN: - factor = q->fuzz_level / (afl->n_fuzz[q->exec_cksum % n_fuzz_size] + 1); + factor = q->fuzz_level / (afl->n_fuzz[q->n_fuzz_entry] + 1); break; case QUAD: - factor = q->fuzz_level * q->fuzz_level / (afl->n_fuzz[q->exec_cksum % n_fuzz_size] + 1); + factor = + q->fuzz_level * q->fuzz_level / (afl->n_fuzz[q->n_fuzz_entry] + 1); break; case MMOPT: @@ -726,8 +726,8 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { perf_score += (q->tc_ref * 10); // the more often fuzz result paths are equal to this queue entry, // reduce its value - perf_score *= - (1 - (double)((double)afl->n_fuzz[q->exec_cksum % n_fuzz_size] / (double)afl->fsrv.total_execs)); + perf_score *= (1 - (double)((double)afl->n_fuzz[q->n_fuzz_entry] / + (double)afl->fsrv.total_execs)); break; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 889f753d..273d1c14 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -939,7 +939,7 @@ int main(int argc, char **argv_orig, char **envp) { /* Dynamically allocate memory for AFLFast schedules */ if (afl->schedule >= FAST && afl->schedule <= RARE) { - afl->n_fuzz = ck_alloc(n_fuzz_size * sizeof(u32)); + afl->n_fuzz = ck_alloc(N_FUZZ_SIZE * sizeof(u32)); } -- cgit 1.4.1 From 223974336196a8f3617548e8ca88ee084794ed60 Mon Sep 17 00:00:00 2001 From: Edznux Date: Thu, 1 Oct 2020 00:11:01 +0200 Subject: Rewrote format metric to be simpler/more static --- include/afl-fuzz.h | 2 +- src/afl-fuzz-statsd.c | 78 ++++++++++++++++++++++----------------------------- 2 files changed, 35 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 65327d93..61672169 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -958,7 +958,7 @@ void show_init_stats(afl_state_t *); int statsd_init(char *host, int port); int send_statsd_metric(afl_state_t *afl); -int statsd_format_metric(afl_state_t *afl, char *formatted[], size_t *num_of_tags); +int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen); /* Run */ diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index d09d1f6e..a53569c3 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -9,7 +9,7 @@ #include "afl-fuzz.h" -#define MAX_STATSD_PACKET_SIZE 1024 +#define MAX_STATSD_PACKET_SIZE 1400 #define MAX_TAG_LEN 200 int sock = 0; @@ -56,6 +56,7 @@ int send_statsd_metric(afl_state_t *afl){ char* port_env; char* host_env; if ((port_env = getenv("AFL_STATSD_PORT")) != NULL) { + // sanitization check ? port = atoi(port_env); } if ((host_env = getenv("AFL_STATSD_HOST")) != NULL) { @@ -73,55 +74,44 @@ int send_statsd_metric(afl_state_t *afl){ perror("sock"); return -1; } - char *formatted[] = {}; - size_t *num_of_tags = 0; - statsd_format_metric(afl, &formatted, num_of_tags); - for (size_t i = 0; i < &num_of_tags; i++){ - printf("%ld\n", i); - printf("%s\n", formatted[i]); - if (sendto(sock, formatted[i], strlen(formatted[i]), 0, (struct sockaddr *) &server, sizeof(server)) == -1) { - perror("sendto"); - return -1; - } + + char buff[MAX_STATSD_PACKET_SIZE] = {0}; + + statsd_format_metric(afl, buff, MAX_STATSD_PACKET_SIZE); + if (sendto(sock, buff, MAX_STATSD_PACKET_SIZE, 0, + (struct sockaddr *)&server, sizeof(server)) == -1) { + perror("sendto"); + return -1; } - + close(sock); sock=0; return 0; } -int statsd_format_metric(afl_state_t *afl, char *formatted[], size_t *num_of_tags){ +int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen){ + /* + metric format: + :|| + tags format: + #key:value,key:value,key + */ + char tags[MAX_TAG_LEN * 2] = {0}; + snprintf(tags, MAX_TAG_LEN * 2, + "banner:%s,afl_version:%s", + afl->use_banner, + VERSION); + // Sends multiple metrics with one UDP Packet. + // bufflen will limit to the max safe size. + snprintf(buff, bufflen, + "fuzzing.afl.cycle_done:%llu|g|#%s\n" + "fuzzing.afl.total_path:%u|g|#%s\n" + "fuzzing.afl.unique_crashes:%llu|g|#%s\n" + "fuzzing.afl.total_crashes:%llu|g|#%s\n" + "fuzzing.afl.unique_hangs:%llu|g|#%s\n", + afl->queue_cycle, tags, afl->queued_paths, tags, afl->unique_crashes, + tags, afl->total_crashes, tags, afl->unique_hangs, tags); - char *tags = "key:value"; - - *num_of_tags = 0; // reset - - const char *metrics[] = { - "fuzzing.afl.cycle_done:%llu|g|#%s\n", - "fuzzing.afl.total_path:%lu|g|#%s\n", - "fuzzing.afl.unique_crashes:%llu|g|#%s\n", - "fuzzing.afl.total_crashes:%llu|g|#%s\n", - "fuzzing.afl.unique_hangs:%llu|g|#%s\n" - }; - - const int metricValues[] = { - afl->queue_cycle, - afl->queued_paths, - afl->unique_crashes, - afl->total_crashes, - afl->unique_hangs - }; - - *num_of_tags = sizeof(metrics)/sizeof(metrics[0]); - - for (size_t i = 0; i < &num_of_tags; i++){ - char *tmp = malloc(MAX_STATSD_PACKET_SIZE); - if(tmp == NULL){ - return -1; - } - snprintf(tmp, MAX_STATSD_PACKET_SIZE, metrics[i], metricValues[i], tags); - formatted[i] = tmp; - } - return 0; + return 0; } \ No newline at end of file -- cgit 1.4.1 From 26dcddab0cdb3ca662746b1fd02cd58de3266269 Mon Sep 17 00:00:00 2001 From: Edznux Date: Sat, 3 Oct 2020 23:56:55 +0200 Subject: Add config ifdef --- include/config.h | 6 ++++++ src/afl-fuzz-run.c | 2 ++ 2 files changed, 8 insertions(+) (limited to 'src') diff --git a/include/config.h b/include/config.h index 77407d50..104276e3 100644 --- a/include/config.h +++ b/include/config.h @@ -41,6 +41,12 @@ #define USE_COLOR +/* Enable sending statistics over a StatsD daemon. +Server config can be adjusted with AFL_STATSD_HOST and AFL_STATSD_PORT env var. +*/ + +#define USE_STATSD + /* If you want to have the original afl internal memory corruption checks. Disabled by default for speed. it is better to use "make ASAN_BUILD=1". */ diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 8dc0b334..b7c4ae05 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -901,10 +901,12 @@ common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { afl->stage_cur + 1 == afl->stage_max) { show_stats(afl); + #ifdef USE_STATSD if(send_statsd_metric(afl)){ //Change me to something realistic; don't fail on connection / lookup fail for metrics... exit(1); } + #endif } return 0; -- cgit 1.4.1 From ff8c6d24156811333c855755fea8fafc43e133bc Mon Sep 17 00:00:00 2001 From: Edznux Date: Sun, 4 Oct 2020 03:22:28 +0200 Subject: Adds other metrics --- include/afl-fuzz.h | 2 +- src/afl-fuzz-statsd.c | 74 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 57 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 61672169..eecfcf75 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -956,7 +956,7 @@ void show_init_stats(afl_state_t *); /* StatsD */ -int statsd_init(char *host, int port); +int statsd_socket_init(char *host, int port); int send_statsd_metric(afl_state_t *afl); int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen); diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index a53569c3..d9cd12d2 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -9,14 +9,15 @@ #include "afl-fuzz.h" -#define MAX_STATSD_PACKET_SIZE 1400 +#define MAX_STATSD_PACKET_SIZE 4096 #define MAX_TAG_LEN 200 +#define METRIC_PREFIX "fuzzing" int sock = 0; struct sockaddr_in server; int error = 0; -int statsd_init(char *host, int port){ +int statsd_socket_init(char *host, int port){ if((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1){ perror("socket"); exit(1); @@ -45,11 +46,9 @@ int statsd_init(char *host, int port){ } int send_statsd_metric(afl_state_t *afl){ - u64 cur_ms = get_cur_time(); - if (cur_ms - afl->stats_last_plot_ms < 1000) { - return 0; - } - + /* default port and host. + Will be overwritten by AFL_STATSD_PORT and AFL_STATSD_HOST environment variable, if they exists. + */ u16 port = 8125; char* host = "127.0.0.1"; @@ -64,7 +63,7 @@ int send_statsd_metric(afl_state_t *afl){ host = host_env; } - error = statsd_init(host, port); + error = statsd_socket_init(host, port); if (error){ perror("Failed to init statsd client. Aborting"); return -1; @@ -78,7 +77,7 @@ int send_statsd_metric(afl_state_t *afl){ char buff[MAX_STATSD_PACKET_SIZE] = {0}; statsd_format_metric(afl, buff, MAX_STATSD_PACKET_SIZE); - if (sendto(sock, buff, MAX_STATSD_PACKET_SIZE, 0, + if (sendto(sock, buff, strlen(buff), 0, (struct sockaddr *)&server, sizeof(server)) == -1) { perror("sendto"); return -1; @@ -95,23 +94,62 @@ int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen){ metric format: :|| tags format: - #key:value,key:value,key + */ + #ifdef USE_STATSD_TAGS + /* #key:value,key:value,key */ char tags[MAX_TAG_LEN * 2] = {0}; snprintf(tags, MAX_TAG_LEN * 2, - "banner:%s,afl_version:%s", + "|#banner:%s,afl_version:%s", afl->use_banner, VERSION); + #else + char *tags = ""; + #endif // Sends multiple metrics with one UDP Packet. // bufflen will limit to the max safe size. snprintf(buff, bufflen, - "fuzzing.afl.cycle_done:%llu|g|#%s\n" - "fuzzing.afl.total_path:%u|g|#%s\n" - "fuzzing.afl.unique_crashes:%llu|g|#%s\n" - "fuzzing.afl.total_crashes:%llu|g|#%s\n" - "fuzzing.afl.unique_hangs:%llu|g|#%s\n", - afl->queue_cycle, tags, afl->queued_paths, tags, afl->unique_crashes, - tags, afl->total_crashes, tags, afl->unique_hangs, tags); + METRIC_PREFIX".cycle_done:%llu|g%s\n" + METRIC_PREFIX".cycles_wo_finds:%llu|g%s\n" + METRIC_PREFIX".execs_done:%llu|g%s\n" + METRIC_PREFIX".execs_per_sec:%0.02f|g%s\n" + METRIC_PREFIX".paths_total:%u|g%s\n" + METRIC_PREFIX".paths_favored:%u|g%s\n" + METRIC_PREFIX".paths_found:%u|g%s\n" + METRIC_PREFIX".paths_imported:%u|g%s\n" + METRIC_PREFIX".max_depth:%u|g%s\n" + METRIC_PREFIX".cur_path:%u|g%s\n" + METRIC_PREFIX".pending_favs:%u|g%s\n" + METRIC_PREFIX".pending_total:%u|g%s\n" + METRIC_PREFIX".variable_paths:%u|g%s\n" + METRIC_PREFIX".unique_crashes:%llu|g%s\n" + METRIC_PREFIX".unique_hangs:%llu|g%s\n" + METRIC_PREFIX".total_crashes:%llu|g%s\n" + METRIC_PREFIX".slowest_exec_ms:%u|g%s\n" + METRIC_PREFIX".edges_found:%u|g%s\n" + METRIC_PREFIX".var_byte_count:%u|g%s\n" + METRIC_PREFIX".havoc_expansion:%u|g%s\n", + afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, + afl->cycles_wo_finds, tags, + afl->fsrv.total_execs, tags, + afl->fsrv.total_execs / ((double)(get_cur_time() - afl->start_time) / 1000), tags, + afl->queued_paths, tags, + afl->queued_favored, tags, + afl->queued_discovered, tags, + afl->queued_imported, tags, + afl->max_depth, tags, + afl->current_entry, tags, + afl->pending_favored, tags, + afl->pending_not_fuzzed, tags, + afl->queued_variable, tags, + afl->unique_crashes, tags, + afl->unique_hangs, tags, + afl->total_crashes, tags, + afl->slowest_exec_ms, tags, + count_non_255_bytes(afl, afl->virgin_bits), tags, + afl->var_byte_count, tags, + afl->expand_havoc, tags + ); return 0; } \ No newline at end of file -- cgit 1.4.1 From 6006cce0cf012884d1509c00ef0f088aeac66bb7 Mon Sep 17 00:00:00 2001 From: Edznux Date: Sun, 4 Oct 2020 03:24:09 +0200 Subject: Define config, change parent func to show_stats --- include/config.h | 1 + src/afl-fuzz-run.c | 6 ------ src/afl-fuzz-stats.c | 8 ++++++++ 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/include/config.h b/include/config.h index 104276e3..c55dda2b 100644 --- a/include/config.h +++ b/include/config.h @@ -46,6 +46,7 @@ Server config can be adjusted with AFL_STATSD_HOST and AFL_STATSD_PORT env var. */ #define USE_STATSD +#define STATSD_UPDATE_SEC 1 /* If you want to have the original afl internal memory corruption checks. Disabled by default for speed. it is better to use "make ASAN_BUILD=1". */ diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index b7c4ae05..6fa142d2 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -901,12 +901,6 @@ common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { afl->stage_cur + 1 == afl->stage_max) { show_stats(afl); - #ifdef USE_STATSD - if(send_statsd_metric(afl)){ - //Change me to something realistic; don't fail on connection / lookup fail for metrics... - exit(1); - } - #endif } return 0; diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 51eed14b..bbbe6ba6 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -422,6 +422,14 @@ void show_stats(afl_state_t *afl) { } + #ifdef USE_STATSD + if (cur_ms - afl->stats_last_stats_ms > STATSD_UPDATE_SEC * 1000) { + if(send_statsd_metric(afl)){ + WARNF("coundln't send statsd metric."); + } + } + #endif + /* Every now and then, write plot data. */ if (cur_ms - afl->stats_last_plot_ms > PLOT_UPDATE_SEC * 1000) { -- cgit 1.4.1 From ca6106a1dc5b39df9f167b3d30ea4472636564c9 Mon Sep 17 00:00:00 2001 From: Edznux Date: Sun, 4 Oct 2020 14:24:25 +0200 Subject: Refactor --- include/afl-fuzz.h | 4 ++- include/config.h | 2 ++ src/afl-fuzz-stats.c | 6 +++-- src/afl-fuzz-statsd.c | 73 ++++++++++++++++++++++++++------------------------- 4 files changed, 46 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index eecfcf75..f341e300 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -635,6 +635,8 @@ typedef struct afl_state { u64 plot_prev_qc, plot_prev_uc, plot_prev_uh, plot_prev_ed; u64 stats_last_stats_ms, stats_last_plot_ms, stats_last_ms, stats_last_execs; + // StatsD + u64 statsd_last_send_ms; double stats_avg_exec; u8 *clean_trace; @@ -957,7 +959,7 @@ void show_init_stats(afl_state_t *); /* StatsD */ int statsd_socket_init(char *host, int port); -int send_statsd_metric(afl_state_t *afl); +int statsd_send_metric(afl_state_t *afl); int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen); /* Run */ diff --git a/include/config.h b/include/config.h index c55dda2b..8cdf0633 100644 --- a/include/config.h +++ b/include/config.h @@ -47,6 +47,8 @@ Server config can be adjusted with AFL_STATSD_HOST and AFL_STATSD_PORT env var. #define USE_STATSD #define STATSD_UPDATE_SEC 1 +#define STATSD_DEFAULT_PORT 8125 +#define STATSD_DEFAULT_HOST "127.0.0.1" /* If you want to have the original afl internal memory corruption checks. Disabled by default for speed. it is better to use "make ASAN_BUILD=1". */ diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index bbbe6ba6..ffef7647 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -423,8 +423,10 @@ void show_stats(afl_state_t *afl) { } #ifdef USE_STATSD - if (cur_ms - afl->stats_last_stats_ms > STATSD_UPDATE_SEC * 1000) { - if(send_statsd_metric(afl)){ + if (cur_ms - afl->statsd_last_send_ms > STATSD_UPDATE_SEC * 1000) { + /* reset counter, even if send failed. */ + afl->statsd_last_send_ms = cur_ms; + if(statsd_send_metric(afl)){ WARNF("coundln't send statsd metric."); } } diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index d9cd12d2..5bd1b537 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -13,11 +13,12 @@ #define MAX_TAG_LEN 200 #define METRIC_PREFIX "fuzzing" -int sock = 0; struct sockaddr_in server; int error = 0; +int statds_sock = 0; int statsd_socket_init(char *host, int port){ + int sock; if((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1){ perror("socket"); exit(1); @@ -42,72 +43,72 @@ int statsd_socket_init(char *host, int port){ memcpy(&(server.sin_addr), &((struct sockaddr_in*)result->ai_addr)->sin_addr, sizeof(struct in_addr)); freeaddrinfo(result); - return 0; + return sock; } -int send_statsd_metric(afl_state_t *afl){ - /* default port and host. +int statsd_send_metric(afl_state_t *afl){ + + char buff[MAX_STATSD_PACKET_SIZE] = {0}; + /* Default port and host. Will be overwritten by AFL_STATSD_PORT and AFL_STATSD_HOST environment variable, if they exists. - */ - u16 port = 8125; - char* host = "127.0.0.1"; + */ + u16 port = STATSD_DEFAULT_PORT; + char* host = STATSD_DEFAULT_HOST; char* port_env; char* host_env; if ((port_env = getenv("AFL_STATSD_PORT")) != NULL) { - // sanitization check ? port = atoi(port_env); } if ((host_env = getenv("AFL_STATSD_HOST")) != NULL) { - // sanitization check ? host = host_env; } - - error = statsd_socket_init(host, port); - if (error){ - perror("Failed to init statsd client. Aborting"); - return -1; - } - - if(!sock){ - perror("sock"); - return -1; - } - char buff[MAX_STATSD_PACKET_SIZE] = {0}; + /* statds_sock is a global variable. We set it once in the beginning and reuse the socket. + If the sendto later fail, we reset it to 0 to be able to recreate it. + */ + if(!statds_sock){ + statds_sock = statsd_socket_init(host, port); + if(!statds_sock){ + perror("Cannot create socket"); + return -1; + } + } statsd_format_metric(afl, buff, MAX_STATSD_PACKET_SIZE); - if (sendto(sock, buff, strlen(buff), 0, - (struct sockaddr *)&server, sizeof(server)) == -1) { - perror("sendto"); - return -1; + if (sendto(statds_sock, buff, strlen(buff), 0, (struct sockaddr *)&server, sizeof(server)) == -1) { + if(!close(statds_sock)){ + perror("Cannot close socket"); + } + statds_sock = 0; + perror("Cannot sendto"); + return -1; } - close(sock); - sock=0; - return 0; } int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen){ - /* - metric format: - :|| - tags format: + /* Metric format: + :| */ #ifdef USE_STATSD_TAGS - /* #key:value,key:value,key + /* Tags format: DogStatsD + :||#key:value,key:value,key */ char tags[MAX_TAG_LEN * 2] = {0}; snprintf(tags, MAX_TAG_LEN * 2, "|#banner:%s,afl_version:%s", afl->use_banner, VERSION); - #else + #else + /* No tags. + */ char *tags = ""; #endif - // Sends multiple metrics with one UDP Packet. - // bufflen will limit to the max safe size. + /* Sends multiple metrics with one UDP Packet. + bufflen will limit to the max safe size. + */ snprintf(buff, bufflen, METRIC_PREFIX".cycle_done:%llu|g%s\n" METRIC_PREFIX".cycles_wo_finds:%llu|g%s\n" -- cgit 1.4.1 From b0de6fed11d4a8de8f016f1d8db0cb19a6b96eb2 Mon Sep 17 00:00:00 2001 From: Edznux Date: Sun, 4 Oct 2020 14:29:50 +0200 Subject: Mention tags format in macro's name --- include/config.h | 3 +++ src/afl-fuzz-statsd.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/include/config.h b/include/config.h index 8cdf0633..33113318 100644 --- a/include/config.h +++ b/include/config.h @@ -50,6 +50,9 @@ Server config can be adjusted with AFL_STATSD_HOST and AFL_STATSD_PORT env var. #define STATSD_DEFAULT_PORT 8125 #define STATSD_DEFAULT_HOST "127.0.0.1" +/* comment out to disable tags. */ +#define USE_DOGSTATSD_TAGS + /* If you want to have the original afl internal memory corruption checks. Disabled by default for speed. it is better to use "make ASAN_BUILD=1". */ diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index 5bd1b537..298138be 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -92,7 +92,7 @@ int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen){ /* Metric format: :| */ - #ifdef USE_STATSD_TAGS + #ifdef USE_DOGSTATSD_TAGS /* Tags format: DogStatsD :||#key:value,key:value,key */ -- cgit 1.4.1 From 1a12db1b59e792c7a46e606fba60bdf855d00c29 Mon Sep 17 00:00:00 2001 From: Edznux Date: Sun, 4 Oct 2020 16:11:05 +0200 Subject: Code format --- include/afl-fuzz.h | 1 + src/afl-fuzz-run.c | 1 + src/afl-fuzz-stats.c | 11 ++- src/afl-fuzz-statsd.c | 256 +++++++++++++++++++++++++------------------------- 4 files changed, 136 insertions(+), 133 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 427e1aec..75dfc4e5 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -635,6 +635,7 @@ typedef struct afl_state { u64 stats_last_stats_ms, stats_last_plot_ms, stats_last_ms, stats_last_execs; // StatsD u64 statsd_last_send_ms; + double stats_avg_exec; u8 *clean_trace; diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 5e74dff3..ee22b0f6 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -903,6 +903,7 @@ common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { afl->stage_cur + 1 == afl->stage_max) { show_stats(afl); + } return 0; diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 2f1e3367..942670b4 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -423,15 +423,16 @@ void show_stats(afl_state_t *afl) { } - #ifdef USE_STATSD +#ifdef USE_STATSD if (cur_ms - afl->statsd_last_send_ms > STATSD_UPDATE_SEC * 1000) { + /* reset counter, even if send failed. */ afl->statsd_last_send_ms = cur_ms; - if(statsd_send_metric(afl)){ - WARNF("coundln't send statsd metric."); - } + if (statsd_send_metric(afl)) { WARNF("coundln't send statsd metric."); } + } - #endif + +#endif /* Every now and then, write plot data. */ diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index 298138be..e94f090c 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -8,149 +8,149 @@ #include #include "afl-fuzz.h" - #define MAX_STATSD_PACKET_SIZE 4096 #define MAX_TAG_LEN 200 #define METRIC_PREFIX "fuzzing" struct sockaddr_in server; -int error = 0; -int statds_sock = 0; - -int statsd_socket_init(char *host, int port){ - int sock; - if((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1){ - perror("socket"); - exit(1); - } +int error = 0; +int statds_sock = 0; - memset(&server, 0, sizeof(server)); - server.sin_family = AF_INET; - server.sin_port = htons(port); - - struct addrinfo *result; - struct addrinfo hints; +int statsd_socket_init(char *host, int port) { - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_DGRAM; + int sock; + if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { - if ( (error = getaddrinfo(host, NULL, &hints, &result)) ) { - perror("getaddrinfo"); - exit(1); - } + FATAL("Failed to create socket"); + + } + + memset(&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_port = htons(port); + + struct addrinfo *result; + struct addrinfo hints; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; - memcpy(&(server.sin_addr), &((struct sockaddr_in*)result->ai_addr)->sin_addr, sizeof(struct in_addr)); - freeaddrinfo(result); + if ((error = getaddrinfo(host, NULL, &hints, &result))) { + + FATAL("Fail to getaddrinfo"); + + } + + memcpy(&(server.sin_addr), &((struct sockaddr_in *)result->ai_addr)->sin_addr, + sizeof(struct in_addr)); + freeaddrinfo(result); + + return sock; - return sock; } -int statsd_send_metric(afl_state_t *afl){ - - char buff[MAX_STATSD_PACKET_SIZE] = {0}; - /* Default port and host. - Will be overwritten by AFL_STATSD_PORT and AFL_STATSD_HOST environment variable, if they exists. - */ - u16 port = STATSD_DEFAULT_PORT; - char* host = STATSD_DEFAULT_HOST; - - char* port_env; - char* host_env; - if ((port_env = getenv("AFL_STATSD_PORT")) != NULL) { - port = atoi(port_env); - } - if ((host_env = getenv("AFL_STATSD_HOST")) != NULL) { - host = host_env; - } +int statsd_send_metric(afl_state_t *afl) { - /* statds_sock is a global variable. We set it once in the beginning and reuse the socket. - If the sendto later fail, we reset it to 0 to be able to recreate it. - */ - if(!statds_sock){ - statds_sock = statsd_socket_init(host, port); - if(!statds_sock){ - perror("Cannot create socket"); - return -1; - } - } + char buff[MAX_STATSD_PACKET_SIZE] = {0}; + /* Default port and host. + Will be overwritten by AFL_STATSD_PORT and AFL_STATSD_HOST environment + variable, if they exists. + */ + u16 port = STATSD_DEFAULT_PORT; + char *host = STATSD_DEFAULT_HOST; + + char *port_env; + char *host_env; + if ((port_env = getenv("AFL_STATSD_PORT")) != NULL) { port = atoi(port_env); } + if ((host_env = getenv("AFL_STATSD_HOST")) != NULL) { host = host_env; } + + /* statds_sock is a global variable. We set it once in the beginning and reuse + the socket. If the sendto later fail, we reset it to 0 to be able to recreate + it. + */ + if (!statds_sock) { + + statds_sock = statsd_socket_init(host, port); + if (!statds_sock) { + + WARNF("Cannot create socket"); + return -1; - statsd_format_metric(afl, buff, MAX_STATSD_PACKET_SIZE); - if (sendto(statds_sock, buff, strlen(buff), 0, (struct sockaddr *)&server, sizeof(server)) == -1) { - if(!close(statds_sock)){ - perror("Cannot close socket"); - } - statds_sock = 0; - perror("Cannot sendto"); - return -1; } - return 0; + } + + statsd_format_metric(afl, buff, MAX_STATSD_PACKET_SIZE); + if (sendto(statds_sock, buff, strlen(buff), 0, (struct sockaddr *)&server, + sizeof(server)) == -1) { + + if (!close(statds_sock)) { perror("Cannot close socket"); } + statds_sock = 0; + WARNF("Cannot sendto"); + return -1; + + } + + return 0; + +} + +int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen) { + +/* Metric format: +:| +*/ +#ifdef USE_DOGSTATSD_TAGS + /* Tags format: DogStatsD + :||#key:value,key:value,key + */ + char tags[MAX_TAG_LEN * 2] = {0}; + snprintf(tags, MAX_TAG_LEN * 2, "|#banner:%s,afl_version:%s", afl->use_banner, + VERSION); +#else + /* No tags. + */ + char *tags = ""; +#endif + /* Sends multiple metrics with one UDP Packet. + bufflen will limit to the max safe size. + */ + snprintf(buff, bufflen, + METRIC_PREFIX ".cycle_done:%llu|g%s\n" METRIC_PREFIX + ".cycles_wo_finds:%llu|g%s\n" METRIC_PREFIX + ".execs_done:%llu|g%s\n" METRIC_PREFIX + ".execs_per_sec:%0.02f|g%s\n" METRIC_PREFIX + ".paths_total:%u|g%s\n" METRIC_PREFIX + ".paths_favored:%u|g%s\n" METRIC_PREFIX + ".paths_found:%u|g%s\n" METRIC_PREFIX + ".paths_imported:%u|g%s\n" METRIC_PREFIX + ".max_depth:%u|g%s\n" METRIC_PREFIX + ".cur_path:%u|g%s\n" METRIC_PREFIX + ".pending_favs:%u|g%s\n" METRIC_PREFIX + ".pending_total:%u|g%s\n" METRIC_PREFIX + ".variable_paths:%u|g%s\n" METRIC_PREFIX + ".unique_crashes:%llu|g%s\n" METRIC_PREFIX + ".unique_hangs:%llu|g%s\n" METRIC_PREFIX + ".total_crashes:%llu|g%s\n" METRIC_PREFIX + ".slowest_exec_ms:%u|g%s\n" METRIC_PREFIX + ".edges_found:%u|g%s\n" METRIC_PREFIX + ".var_byte_count:%u|g%s\n" METRIC_PREFIX + ".havoc_expansion:%u|g%s\n", + afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, + afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, + afl->fsrv.total_execs / + ((double)(get_cur_time() - afl->start_time) / 1000), + tags, afl->queued_paths, tags, afl->queued_favored, tags, + afl->queued_discovered, tags, afl->queued_imported, tags, + afl->max_depth, tags, afl->current_entry, tags, afl->pending_favored, + tags, afl->pending_not_fuzzed, tags, afl->queued_variable, tags, + afl->unique_crashes, tags, afl->unique_hangs, tags, + afl->total_crashes, tags, afl->slowest_exec_ms, tags, + count_non_255_bytes(afl, afl->virgin_bits), tags, + afl->var_byte_count, tags, afl->expand_havoc, tags); + + return 0; + } -int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen){ - /* Metric format: - :| - */ - #ifdef USE_DOGSTATSD_TAGS - /* Tags format: DogStatsD - :||#key:value,key:value,key - */ - char tags[MAX_TAG_LEN * 2] = {0}; - snprintf(tags, MAX_TAG_LEN * 2, - "|#banner:%s,afl_version:%s", - afl->use_banner, - VERSION); - #else - /* No tags. - */ - char *tags = ""; - #endif - /* Sends multiple metrics with one UDP Packet. - bufflen will limit to the max safe size. - */ - snprintf(buff, bufflen, - METRIC_PREFIX".cycle_done:%llu|g%s\n" - METRIC_PREFIX".cycles_wo_finds:%llu|g%s\n" - METRIC_PREFIX".execs_done:%llu|g%s\n" - METRIC_PREFIX".execs_per_sec:%0.02f|g%s\n" - METRIC_PREFIX".paths_total:%u|g%s\n" - METRIC_PREFIX".paths_favored:%u|g%s\n" - METRIC_PREFIX".paths_found:%u|g%s\n" - METRIC_PREFIX".paths_imported:%u|g%s\n" - METRIC_PREFIX".max_depth:%u|g%s\n" - METRIC_PREFIX".cur_path:%u|g%s\n" - METRIC_PREFIX".pending_favs:%u|g%s\n" - METRIC_PREFIX".pending_total:%u|g%s\n" - METRIC_PREFIX".variable_paths:%u|g%s\n" - METRIC_PREFIX".unique_crashes:%llu|g%s\n" - METRIC_PREFIX".unique_hangs:%llu|g%s\n" - METRIC_PREFIX".total_crashes:%llu|g%s\n" - METRIC_PREFIX".slowest_exec_ms:%u|g%s\n" - METRIC_PREFIX".edges_found:%u|g%s\n" - METRIC_PREFIX".var_byte_count:%u|g%s\n" - METRIC_PREFIX".havoc_expansion:%u|g%s\n", - afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, - afl->cycles_wo_finds, tags, - afl->fsrv.total_execs, tags, - afl->fsrv.total_execs / ((double)(get_cur_time() - afl->start_time) / 1000), tags, - afl->queued_paths, tags, - afl->queued_favored, tags, - afl->queued_discovered, tags, - afl->queued_imported, tags, - afl->max_depth, tags, - afl->current_entry, tags, - afl->pending_favored, tags, - afl->pending_not_fuzzed, tags, - afl->queued_variable, tags, - afl->unique_crashes, tags, - afl->unique_hangs, tags, - afl->total_crashes, tags, - afl->slowest_exec_ms, tags, - count_non_255_bytes(afl, afl->virgin_bits), tags, - afl->var_byte_count, tags, - afl->expand_havoc, tags - ); - - return 0; -} \ No newline at end of file -- cgit 1.4.1 From bab60b68d968492d689bc5963bd775b10c6292e8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 4 Oct 2020 20:45:59 +0200 Subject: changed the default schedule to coe --- src/afl-fuzz-state.c | 2 +- src/afl-fuzz.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index a8e56e60..4a1e739f 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -87,7 +87,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->w_end = 0.3; afl->g_max = 5000; afl->period_pilot_tmp = 5000.0; - afl->schedule = EXPLORE; /* Power schedule (default: EXPLORE) */ + afl->schedule = COE; /* Power schedule (default: COE) */ afl->havoc_max_mult = HAVOC_MAX_MULT; afl->clear_screen = 1; /* Window resized? */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 273d1c14..2f8aa3fd 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -90,7 +90,7 @@ static void usage(u8 *argv0, int more_help) { "Execution control settings:\n" " -p schedule - power schedules compute a seed's performance score:\n" - " -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " @@ -928,7 +928,7 @@ int main(int argc, char **argv_orig, char **envp) { OKF("Using seek power schedule (SEEK)"); break; case EXPLORE: - OKF("Using exploration-based constant power schedule (EXPLORE, default)"); + OKF("Using exploration-based constant power schedule (EXPLORE)"); break; default: FATAL("Unknown power schedule"); -- cgit 1.4.1 From 9b112fde1a7d776b07f0cb656d11c6151902d2c3 Mon Sep 17 00:00:00 2001 From: Edznux Date: Mon, 5 Oct 2020 19:29:37 +0200 Subject: Add help for AFL_STATSD_HOST & AFL_STATSD_PORT env var --- src/afl-fuzz.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index ea24011e..0b08f426 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -194,6 +194,8 @@ static void usage(u8 *argv0, int more_help) { "AFL_SKIP_BIN_CHECK: skip the check, if the target is an executable\n" "AFL_SKIP_CPUFREQ: do not warn about variable cpu clocking\n" "AFL_SKIP_CRASHES: during initial dry run do not terminate for crashing inputs\n" + "AFL_STATSD_HOST: change default statsd host. (default 127.0.0.1)" + "AFL_STATSD_PORT: change default statsd port (default: 8125)" "AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)\n" //"AFL_PERSISTENT: not supported anymore -> no effect, just a warning\n" //"AFL_DEFER_FORKSRV: not supported anymore -> no effect, just a warning\n" -- cgit 1.4.1 From 2bf3a70e2b5cd0961cb5b1d525f3fd58c4408ba5 Mon Sep 17 00:00:00 2001 From: Edznux Date: Mon, 5 Oct 2020 22:01:50 +0200 Subject: Correctly handle env var. --- include/afl-fuzz.h | 2 +- src/afl-fuzz-state.c | 13 +++++++++++++ src/afl-fuzz-statsd.c | 6 ++---- 3 files changed, 16 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 75dfc4e5..d79920bd 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -359,7 +359,7 @@ typedef struct afl_env_vars { u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload, - *afl_max_det_extras; + *afl_max_det_extras, *afl_statsd_host, *afl_statsd_port; } afl_env_vars_t; diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 4e817843..aefa226d 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -363,6 +363,19 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_forksrv_init_tmout = (u8 *)get_afl_env(afl_environment_variables[i]); + } else if (!strncmp(env, "AFL_STATSD_HOST", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd_host = + (u8 *)get_afl_env(afl_environment_variables[i]); + } else if (!strncmp(env, "AFL_STATSD_PORT", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd_port = + (u8 *)get_afl_env(afl_environment_variables[i]); + } } else { diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index e94f090c..7ae2efca 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -60,10 +60,8 @@ int statsd_send_metric(afl_state_t *afl) { u16 port = STATSD_DEFAULT_PORT; char *host = STATSD_DEFAULT_HOST; - char *port_env; - char *host_env; - if ((port_env = getenv("AFL_STATSD_PORT")) != NULL) { port = atoi(port_env); } - if ((host_env = getenv("AFL_STATSD_HOST")) != NULL) { host = host_env; } + if (afl->afl_env.afl_statsd_port) { port = atoi(afl->afl_env.afl_statsd_port); } + if (afl->afl_env.afl_statsd_host) { host = afl->afl_env.afl_statsd_host; } /* statds_sock is a global variable. We set it once in the beginning and reuse the socket. If the sendto later fail, we reset it to 0 to be able to recreate -- cgit 1.4.1 From 916b6fd3171bab1b17e576e088f094080ff5f6cf Mon Sep 17 00:00:00 2001 From: Edznux Date: Mon, 5 Oct 2020 22:21:01 +0200 Subject: Refactor global var into afl_state_t struct --- include/afl-fuzz.h | 7 +++++-- src/afl-fuzz-statsd.c | 51 +++++++++++++++++++++++---------------------------- 2 files changed, 28 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index d79920bd..7eeeb79b 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -633,8 +633,11 @@ typedef struct afl_state { u64 plot_prev_qc, plot_prev_uc, plot_prev_uh, plot_prev_ed; u64 stats_last_stats_ms, stats_last_plot_ms, stats_last_ms, stats_last_execs; - // StatsD + + /* StatsD */ u64 statsd_last_send_ms; + struct sockaddr_in statsd_server; + int statsd_sock; double stats_avg_exec; @@ -958,7 +961,7 @@ void show_init_stats(afl_state_t *); /* StatsD */ -int statsd_socket_init(char *host, int port); +int statsd_socket_init(afl_state_t *afl); int statsd_send_metric(afl_state_t *afl); int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen); diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index 7ae2efca..0ec1bcb7 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -12,11 +12,16 @@ #define MAX_TAG_LEN 200 #define METRIC_PREFIX "fuzzing" -struct sockaddr_in server; -int error = 0; -int statds_sock = 0; +int statsd_socket_init(afl_state_t *afl) { + /* Default port and host. + Will be overwritten by AFL_STATSD_PORT and AFL_STATSD_HOST environment + variable, if they exists. + */ + u16 port = STATSD_DEFAULT_PORT; + char *host = STATSD_DEFAULT_HOST; -int statsd_socket_init(char *host, int port) { + if (afl->afl_env.afl_statsd_port) { port = atoi(afl->afl_env.afl_statsd_port); } + if (afl->afl_env.afl_statsd_host) { host = afl->afl_env.afl_statsd_host; } int sock; if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { @@ -25,9 +30,9 @@ int statsd_socket_init(char *host, int port) { } - memset(&server, 0, sizeof(server)); - server.sin_family = AF_INET; - server.sin_port = htons(port); + memset(&afl->statsd_server, 0, sizeof(afl->statsd_server)); + afl->statsd_server.sin_family = AF_INET; + afl->statsd_server.sin_port = htons(port); struct addrinfo *result; struct addrinfo hints; @@ -36,13 +41,13 @@ int statsd_socket_init(char *host, int port) { hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; - if ((error = getaddrinfo(host, NULL, &hints, &result))) { + if ((getaddrinfo(host, NULL, &hints, &result))) { FATAL("Fail to getaddrinfo"); } - memcpy(&(server.sin_addr), &((struct sockaddr_in *)result->ai_addr)->sin_addr, + memcpy(&(afl->statsd_server.sin_addr), &((struct sockaddr_in *)result->ai_addr)->sin_addr, sizeof(struct in_addr)); freeaddrinfo(result); @@ -53,24 +58,14 @@ int statsd_socket_init(char *host, int port) { int statsd_send_metric(afl_state_t *afl) { char buff[MAX_STATSD_PACKET_SIZE] = {0}; - /* Default port and host. - Will be overwritten by AFL_STATSD_PORT and AFL_STATSD_HOST environment - variable, if they exists. - */ - u16 port = STATSD_DEFAULT_PORT; - char *host = STATSD_DEFAULT_HOST; - - if (afl->afl_env.afl_statsd_port) { port = atoi(afl->afl_env.afl_statsd_port); } - if (afl->afl_env.afl_statsd_host) { host = afl->afl_env.afl_statsd_host; } - /* statds_sock is a global variable. We set it once in the beginning and reuse - the socket. If the sendto later fail, we reset it to 0 to be able to recreate - it. + /* afl->statsd_sock is set once in the initialisation of afl-fuzz and reused each time + If the sendto later fail, we reset it to 0 to be able to recreates it. */ - if (!statds_sock) { + if (!afl->statsd_sock) { - statds_sock = statsd_socket_init(host, port); - if (!statds_sock) { + afl->statsd_sock = statsd_socket_init(afl); + if (!afl->statsd_sock) { WARNF("Cannot create socket"); return -1; @@ -80,11 +75,11 @@ int statsd_send_metric(afl_state_t *afl) { } statsd_format_metric(afl, buff, MAX_STATSD_PACKET_SIZE); - if (sendto(statds_sock, buff, strlen(buff), 0, (struct sockaddr *)&server, - sizeof(server)) == -1) { + if (sendto(afl->statsd_sock, buff, strlen(buff), 0, (struct sockaddr *)&afl->statsd_server, + sizeof(afl->statsd_server)) == -1) { - if (!close(statds_sock)) { perror("Cannot close socket"); } - statds_sock = 0; + if (!close(afl->statsd_sock)) { perror("Cannot close socket"); } + afl->statsd_sock = 0; WARNF("Cannot sendto"); return -1; -- cgit 1.4.1 From 9ac9aa25111b67cd6a2ee7ce8376a445368b215f Mon Sep 17 00:00:00 2001 From: Edznux Date: Mon, 5 Oct 2020 22:21:24 +0200 Subject: Fix code format --- include/afl-fuzz.h | 4 ++-- src/afl-fuzz-state.c | 1 + src/afl-fuzz-statsd.c | 19 ++++++++++++++----- 3 files changed, 17 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 7eeeb79b..92375b2c 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -635,9 +635,9 @@ typedef struct afl_state { u64 stats_last_stats_ms, stats_last_plot_ms, stats_last_ms, stats_last_execs; /* StatsD */ - u64 statsd_last_send_ms; + u64 statsd_last_send_ms; struct sockaddr_in statsd_server; - int statsd_sock; + int statsd_sock; double stats_avg_exec; diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index aefa226d..5e719a85 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -369,6 +369,7 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_statsd_host = (u8 *)get_afl_env(afl_environment_variables[i]); + } else if (!strncmp(env, "AFL_STATSD_PORT", afl_environment_variable_len)) { diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index 0ec1bcb7..9cb8fde4 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -13,6 +13,7 @@ #define METRIC_PREFIX "fuzzing" int statsd_socket_init(afl_state_t *afl) { + /* Default port and host. Will be overwritten by AFL_STATSD_PORT and AFL_STATSD_HOST environment variable, if they exists. @@ -20,7 +21,12 @@ int statsd_socket_init(afl_state_t *afl) { u16 port = STATSD_DEFAULT_PORT; char *host = STATSD_DEFAULT_HOST; - if (afl->afl_env.afl_statsd_port) { port = atoi(afl->afl_env.afl_statsd_port); } + if (afl->afl_env.afl_statsd_port) { + + port = atoi(afl->afl_env.afl_statsd_port); + + } + if (afl->afl_env.afl_statsd_host) { host = afl->afl_env.afl_statsd_host; } int sock; @@ -47,7 +53,8 @@ int statsd_socket_init(afl_state_t *afl) { } - memcpy(&(afl->statsd_server.sin_addr), &((struct sockaddr_in *)result->ai_addr)->sin_addr, + memcpy(&(afl->statsd_server.sin_addr), + &((struct sockaddr_in *)result->ai_addr)->sin_addr, sizeof(struct in_addr)); freeaddrinfo(result); @@ -59,8 +66,9 @@ int statsd_send_metric(afl_state_t *afl) { char buff[MAX_STATSD_PACKET_SIZE] = {0}; - /* afl->statsd_sock is set once in the initialisation of afl-fuzz and reused each time - If the sendto later fail, we reset it to 0 to be able to recreates it. + /* afl->statsd_sock is set once in the initialisation of afl-fuzz and reused + each time If the sendto later fail, we reset it to 0 to be able to recreates + it. */ if (!afl->statsd_sock) { @@ -75,7 +83,8 @@ int statsd_send_metric(afl_state_t *afl) { } statsd_format_metric(afl, buff, MAX_STATSD_PACKET_SIZE); - if (sendto(afl->statsd_sock, buff, strlen(buff), 0, (struct sockaddr *)&afl->statsd_server, + if (sendto(afl->statsd_sock, buff, strlen(buff), 0, + (struct sockaddr *)&afl->statsd_server, sizeof(afl->statsd_server)) == -1) { if (!close(afl->statsd_sock)) { perror("Cannot close socket"); } -- cgit 1.4.1 From fd4efd04a1d55b070934e5307b8dd8f81aa8e8ac Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 5 Oct 2020 23:07:22 +0200 Subject: fixed gcc 10 warning --- src/afl-fuzz-stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 1d5b169d..654d9059 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -955,7 +955,7 @@ void show_stats(afl_state_t *afl) { #else SAYF("%s" cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, spacing, cpu_color, - MIN(cur_utilization, 999)); + MIN(cur_utilization, (u32)999)); #endif /* ^HAVE_AFFINITY */ -- cgit 1.4.1 From a4b60ca5b61c9bca5fa7b67528baeb3a8ea9320e Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 6 Oct 2020 15:37:59 +0200 Subject: testcase cache added --- include/afl-fuzz.h | 15 ++++++ include/config.h | 8 +++ src/afl-fuzz-one.c | 148 ++++++++++++++++++--------------------------------- src/afl-fuzz-queue.c | 65 ++++++++++++++++++++++ src/afl-fuzz.c | 3 +- 5 files changed, 142 insertions(+), 97 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index fb661ce5..46da8c7d 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -162,6 +162,9 @@ struct queue_entry { u8 *trace_mini; /* Trace bytes, if kept */ u32 tc_ref; /* Trace bytes ref count */ + u8 *testcase_buf; /* The testcase buffer, if loaded. */ + u32 testcase_refs; /* count of users of testcase buf */ + struct queue_entry *next; /* Next element, if any */ }; @@ -664,6 +667,11 @@ typedef struct afl_state { /* queue entries ready for splicing count (len > 4) */ u32 ready_for_splicing_count; + /* How many queue entries currently have cached testcases */ + u32 q_testcase_cache_count; + /* Refs to each queue entry with cached testcase (for eviction, if cache_count is too large) */ + struct queue_entry *q_testcase_cache[TESTCASE_CACHE_SIZE]; + } afl_state_t; struct custom_mutator { @@ -1101,5 +1109,12 @@ static inline u64 next_p2(u64 val) { } +/* Returns the testcase buf from the file behind this queue entry. + Increases the refcount. */ +u8 *queue_testcase_take(afl_state_t *afl, struct queue_entry *q); + +/* Tell afl that this testcase may be evicted from the cache */ +void queue_testcase_release(afl_state_t *afl, struct queue_entry *q); + #endif diff --git a/include/config.h b/include/config.h index 7c8e0c7d..38a734ce 100644 --- a/include/config.h +++ b/include/config.h @@ -295,6 +295,14 @@ #define RESEED_RNG 100000 +/* The amount of entries in the testcase cache, held in memory. +Decrease if RAM usage is high. */ +#define TESTCASE_CACHE_SIZE 2048 + +#if TESTCASE_CACHE_SIZE < 4 + #error "Dangerously low cache size: Set TESTCASE_CACHE_SIZE to 4 or more in config.h! +#endif + /* Maximum line length passed from GCC to 'as' and used for parsing configuration files: */ diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index c04b492b..20558618 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -370,7 +370,7 @@ static void locate_diffs(u8 *ptr1, u8 *ptr2, u32 len, s32 *first, s32 *last) { u8 fuzz_one_original(afl_state_t *afl) { - s32 len, fd, temp_len; + s32 len, temp_len; u32 j; u32 i; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; @@ -453,28 +453,9 @@ u8 fuzz_one_original(afl_state_t *afl) { } - /* Map the test case into memory. */ - - fd = open(afl->queue_cur->fname, O_RDONLY); - - if (unlikely(fd < 0)) { - - PFATAL("Unable to open '%s'", afl->queue_cur->fname); - - } - + orig_in = in_buf = queue_testcase_take(afl, afl->queue_cur); len = afl->queue_cur->len; - orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - - if (unlikely(orig_in == MAP_FAILED)) { - - PFATAL("Unable to mmap '%s' with len %d", afl->queue_cur->fname, len); - - } - - close(fd); - /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every single byte anyway, so it wouldn't give us any performance or memory usage benefits. */ @@ -1694,7 +1675,7 @@ custom_mutator_stage: for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { - struct queue_entry *target; + struct queue_entry *target = NULL; u32 tid; u8 * new_buf = NULL; u32 target_len = 0; @@ -1717,17 +1698,7 @@ custom_mutator_stage: afl->splicing_with = tid; /* Read the additional testcase into a new buffer. */ - fd = open(target->fname, O_RDONLY); - if (unlikely(fd < 0)) { - - PFATAL("Unable to open '%s'", target->fname); - - } - - new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), target->len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - ck_read(fd, new_buf, target->len, target->fname); - close(fd); + new_buf = queue_testcase_take(afl, target); target_len = target->len; } @@ -1738,6 +1709,11 @@ custom_mutator_stage: el->afl_custom_fuzz(el->data, out_buf, len, &mutated_buf, new_buf, target_len, max_seed_size); + if (new_buf) { + queue_testcase_release(afl, target); + new_buf = NULL; + } + if (unlikely(!mutated_buf)) { FATAL("Error in custom_fuzz. Size returned: %zd", mutated_size); @@ -2320,51 +2296,44 @@ havoc_stage: /* Overwrite bytes with a randomly selected chunk from another testcase or insert that chunk. */ - if (afl->queued_paths < 4) break; + if (afl->queued_paths < 4) { break; } /* Pick a random queue entry and seek to it. */ u32 tid; - do + do { tid = rand_below(afl, afl->queued_paths); - while (tid == afl->current_entry); + } while (tid == afl->current_entry); struct queue_entry *target = afl->queue_buf[tid]; /* Make sure that the target has a reasonable length. */ - while (target && (target->len < 2 || target == afl->queue_cur)) + while (target && (target->len < 2 || target == afl->queue_cur)) { target = target->next; + } - if (!target) break; - - /* Read the testcase into a new buffer. */ - - fd = open(target->fname, O_RDONLY); - - if (unlikely(fd < 0)) { + if (!target) { break; } - PFATAL("Unable to open '%s'", target->fname); - - } u32 new_len = target->len; - u8 *new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), new_len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - - ck_read(fd, new_buf, new_len, target->fname); - close(fd); + /* Get the testcase contents for splicing. */ + u8 *new_buf = queue_testcase_take(afl, target); u8 overwrite = 0; - if (temp_len >= 2 && rand_below(afl, 2)) + if (temp_len >= 2 && rand_below(afl, 2)) { overwrite = 1; + } else if (temp_len + HAVOC_BLK_XL >= MAX_FILE) { - if (temp_len >= 2) + if (temp_len >= 2) { overwrite = 1; - else + } else { + queue_testcase_release(afl, target); + new_buf = NULL; break; + } } @@ -2411,6 +2380,9 @@ havoc_stage: } + /* We don't need this splice testcase anymore */ + queue_testcase_release(afl, target); + new_buf = NULL; break; } @@ -2516,24 +2488,16 @@ retry_splicing: if (!target) { goto retry_splicing; } - /* Read the testcase into a new buffer. */ - - fd = open(target->fname, O_RDONLY); - - if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } - + /* Get the testcase buffer */ + u8 *splice_buf = queue_testcase_take(afl, target); new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), target->len); if (unlikely(!new_buf)) { PFATAL("alloc"); } - ck_read(fd, new_buf, target->len, target->fname); - - close(fd); - /* Find a suitable splicing location, somewhere between the first and the last differing byte. Bail out if the difference is just a single byte or so. */ - locate_diffs(in_buf, new_buf, MIN(len, (s64)target->len), &f_diff, &l_diff); + locate_diffs(in_buf, splice_buf, MIN(len, (s64)target->len), &f_diff, &l_diff); if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { goto retry_splicing; } @@ -2545,6 +2509,7 @@ retry_splicing: len = target->len; memcpy(new_buf, in_buf, split_at); + memcpy(new_buf + split_at, splice_buf + split_at, target->len - split_at); afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); in_buf = new_buf; @@ -2552,6 +2517,9 @@ retry_splicing: if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(out_buf, in_buf, len); + queue_testcase_release(afl, target); + splice_buf = NULL; + goto custom_mutator_stage; /* ???: While integrating Python module, the author decided to jump to python stage, but the reason behind this is not clear.*/ @@ -2582,7 +2550,8 @@ abandon_entry: ++afl->queue_cur->fuzz_level; - munmap(orig_in, afl->queue_cur->len); + queue_testcase_release(afl, afl->queue_cur); + orig_in = NULL; return ret_val; @@ -2604,7 +2573,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { } - s32 len, fd, temp_len; + s32 len, temp_len; u32 i; u32 j; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; @@ -2669,23 +2638,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { } /* Map the test case into memory. */ - - fd = open(afl->queue_cur->fname, O_RDONLY); - - if (fd < 0) { PFATAL("Unable to open '%s'", afl->queue_cur->fname); } - + orig_in = in_buf = queue_testcase_take(afl, afl->queue_cur); len = afl->queue_cur->len; - orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - - if (orig_in == MAP_FAILED) { - - PFATAL("Unable to mmap '%s'", afl->queue_cur->fname); - - } - - close(fd); - /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every single byte anyway, so it wouldn't give us any performance or memory usage benefits. */ @@ -4522,31 +4477,24 @@ pacemaker_fuzzing: if (!target) { goto retry_splicing_puppet; } /* Read the testcase into a new buffer. */ - - fd = open(target->fname, O_RDONLY); - - if (fd < 0) { PFATAL("Unable to open '%s'", target->fname); } - - new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), target->len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - - ck_read(fd, new_buf, target->len, target->fname); - - close(fd); + u8 *splicing_buf = queue_testcase_take(afl, target); /* Find a suitable splicin g location, somewhere between the first and the last differing byte. Bail out if the difference is just a single byte or so. */ - locate_diffs(in_buf, new_buf, MIN(len, (s32)target->len), &f_diff, + locate_diffs(in_buf, splicing_buf, MIN(len, (s32)target->len), &f_diff, &l_diff); if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { + queue_testcase_release(afl, target); goto retry_splicing_puppet; } + new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), target->len); + /* Split somewhere between the first and last differing byte. */ split_at = f_diff + rand_below(afl, l_diff - f_diff); @@ -4555,12 +4503,16 @@ pacemaker_fuzzing: len = target->len; memcpy(new_buf, in_buf, split_at); + memcpy(new_buf + split_at, splicing_buf + split_at, target->len - split_at); afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); in_buf = new_buf; out_buf = afl_realloc(AFL_BUF_PARAM(out), len); if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(out_buf, in_buf, len); + queue_testcase_release(afl, target); + splicing_buf = NULL; + goto havoc_stage_puppet; } /* if splice_cycle */ @@ -4594,7 +4546,8 @@ pacemaker_fuzzing: // if (afl->queue_cur->favored) --afl->pending_favored; // } - munmap(orig_in, afl->queue_cur->len); + queue_testcase_release(afl, afl->queue_cur); + orig_in = NULL; if (afl->key_puppet == 1) { @@ -4730,6 +4683,9 @@ pacemaker_fuzzing: } /* block */ + queue_testcase_release(afl, afl->queue_cur); + orig_in = NULL; + return ret_val; } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 0d7d0314..e2387aaa 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -220,6 +220,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { q->depth = afl->cur_depth + 1; q->passed_det = passed_det; q->trace_mini = NULL; + q->testcase_buf = NULL; if (q->depth > afl->max_depth) { afl->max_depth = q->depth; } @@ -767,3 +768,67 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { } +/* Tell afl that this testcase may be evicted from the cache */ +inline void queue_testcase_release(afl_state_t *afl, struct queue_entry *q) { + (void) afl; + q->testcase_refs--; + if (unlikely(q->testcase_refs < 0)) { FATAL("Testcase refcount smaller than 0"); } +} + +/* Returns the testcase buf from the file behind this queue entry. + Increases the refcount. */ +u8 *queue_testcase_take(afl_state_t *afl, struct queue_entry *q) { + if (!q->testcase_buf) { + u32 tid = 0; + /* Buf not cached, let's do that now */ + + if (likely(afl->q_testcase_cache_count == TESTCASE_CACHE_SIZE)) { + /* Cache full. We neet to evict one to map one. + Get a random one which is not in use */ + do { + + tid = rand_below(afl, afl->q_testcase_cache_count); + + } while (afl->q_testcase_cache[tid]->testcase_refs > 0); + + struct queue_entry *old_cached = afl->q_testcase_cache[tid]; + /* free the current buf from cache */ + munmap(old_cached->testcase_buf, old_cached->len); + old_cached->testcase_buf = NULL; + + } else { + tid = afl->q_testcase_cache_count; + afl->q_testcase_cache_count++; + } + + /* Map the test case into memory. */ + + int fd = open(q->fname, O_RDONLY); + + if (unlikely(fd < 0)) { + + PFATAL("Unable to open '%s'", q->fname); + + } + + u32 len = q->len; + + q->testcase_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + + if (unlikely(q->testcase_buf == MAP_FAILED)) { + + PFATAL("Unable to mmap '%s' with len %d", q->fname, len); + + } + + close(fd); + + /* Register us as cached */ + afl->q_testcase_cache[tid] = q; + + } + q->testcase_refs++; + if (!q->testcase_buf) { FATAL("Testcase buf is NULL, this should never happen"); } + return q->testcase_buf; + +} diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 2f8aa3fd..dd9aaa8f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1152,8 +1152,9 @@ int main(int argc, char **argv_orig, char **envp) { if (extras_dir_cnt) { - for (i = 0; i < extras_dir_cnt; i++) + for (i = 0; i < extras_dir_cnt; i++) { load_extras(afl, extras_dir[i]); + } dedup_extras(afl); OKF("Loaded a total of %u extras.", afl->extras_cnt); -- cgit 1.4.1 From 74dc227c4412d0121c9b972e5d89db89f54c6b3a Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 6 Oct 2020 15:38:36 +0200 Subject: code format --- include/afl-fuzz.h | 5 +++-- include/config.h | 3 ++- src/afl-fuzz-one.c | 22 +++++++++++++++++----- src/afl-fuzz-queue.c | 34 ++++++++++++++++++++++++---------- src/afl-fuzz.c | 2 ++ 5 files changed, 48 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 46da8c7d..5ab787e0 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -163,7 +163,7 @@ struct queue_entry { u32 tc_ref; /* Trace bytes ref count */ u8 *testcase_buf; /* The testcase buffer, if loaded. */ - u32 testcase_refs; /* count of users of testcase buf */ + u32 testcase_refs; /* count of users of testcase buf */ struct queue_entry *next; /* Next element, if any */ @@ -669,7 +669,8 @@ typedef struct afl_state { /* How many queue entries currently have cached testcases */ u32 q_testcase_cache_count; - /* Refs to each queue entry with cached testcase (for eviction, if cache_count is too large) */ + /* Refs to each queue entry with cached testcase (for eviction, if cache_count + * is too large) */ struct queue_entry *q_testcase_cache[TESTCASE_CACHE_SIZE]; } afl_state_t; diff --git a/include/config.h b/include/config.h index 38a734ce..ec378036 100644 --- a/include/config.h +++ b/include/config.h @@ -300,7 +300,8 @@ Decrease if RAM usage is high. */ #define TESTCASE_CACHE_SIZE 2048 #if TESTCASE_CACHE_SIZE < 4 - #error "Dangerously low cache size: Set TESTCASE_CACHE_SIZE to 4 or more in config.h! + #error \ + "Dangerously low cache size: Set TESTCASE_CACHE_SIZE to 4 or more in config.h! #endif /* Maximum line length passed from GCC to 'as' and used for parsing diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 20558618..a5f77f11 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1710,8 +1710,10 @@ custom_mutator_stage: target_len, max_seed_size); if (new_buf) { + queue_testcase_release(afl, target); new_buf = NULL; + } if (unlikely(!mutated_buf)) { @@ -2302,7 +2304,9 @@ havoc_stage: u32 tid; do { + tid = rand_below(afl, afl->queued_paths); + } while (tid == afl->current_entry); struct queue_entry *target = afl->queue_buf[tid]; @@ -2310,12 +2314,13 @@ havoc_stage: /* Make sure that the target has a reasonable length. */ while (target && (target->len < 2 || target == afl->queue_cur)) { + target = target->next; + } if (!target) { break; } - u32 new_len = target->len; /* Get the testcase contents for splicing. */ @@ -2323,16 +2328,21 @@ havoc_stage: u8 overwrite = 0; if (temp_len >= 2 && rand_below(afl, 2)) { + overwrite = 1; - } - else if (temp_len + HAVOC_BLK_XL >= MAX_FILE) { + + } else if (temp_len + HAVOC_BLK_XL >= MAX_FILE) { if (temp_len >= 2) { + overwrite = 1; + } else { + queue_testcase_release(afl, target); new_buf = NULL; break; + } } @@ -2497,7 +2507,8 @@ retry_splicing: the last differing byte. Bail out if the difference is just a single byte or so. */ - locate_diffs(in_buf, splice_buf, MIN(len, (s64)target->len), &f_diff, &l_diff); + locate_diffs(in_buf, splice_buf, MIN(len, (s64)target->len), &f_diff, + &l_diff); if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { goto retry_splicing; } @@ -4503,7 +4514,8 @@ pacemaker_fuzzing: len = target->len; memcpy(new_buf, in_buf, split_at); - memcpy(new_buf + split_at, splicing_buf + split_at, target->len - split_at); + memcpy(new_buf + split_at, splicing_buf + split_at, + target->len - split_at); afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); in_buf = new_buf; out_buf = afl_realloc(AFL_BUF_PARAM(out), len); diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index e2387aaa..721f9ac7 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -770,24 +770,33 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { /* Tell afl that this testcase may be evicted from the cache */ inline void queue_testcase_release(afl_state_t *afl, struct queue_entry *q) { - (void) afl; + + (void)afl; q->testcase_refs--; - if (unlikely(q->testcase_refs < 0)) { FATAL("Testcase refcount smaller than 0"); } + if (unlikely(q->testcase_refs < 0)) { + + FATAL("Testcase refcount smaller than 0"); + + } + } /* Returns the testcase buf from the file behind this queue entry. Increases the refcount. */ u8 *queue_testcase_take(afl_state_t *afl, struct queue_entry *q) { + if (!q->testcase_buf) { + u32 tid = 0; /* Buf not cached, let's do that now */ if (likely(afl->q_testcase_cache_count == TESTCASE_CACHE_SIZE)) { + /* Cache full. We neet to evict one to map one. Get a random one which is not in use */ do { - tid = rand_below(afl, afl->q_testcase_cache_count); + tid = rand_below(afl, afl->q_testcase_cache_count); } while (afl->q_testcase_cache[tid]->testcase_refs > 0); @@ -795,21 +804,19 @@ u8 *queue_testcase_take(afl_state_t *afl, struct queue_entry *q) { /* free the current buf from cache */ munmap(old_cached->testcase_buf, old_cached->len); old_cached->testcase_buf = NULL; - + } else { + tid = afl->q_testcase_cache_count; afl->q_testcase_cache_count++; + } /* Map the test case into memory. */ int fd = open(q->fname, O_RDONLY); - if (unlikely(fd < 0)) { - - PFATAL("Unable to open '%s'", q->fname); - - } + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", q->fname); } u32 len = q->len; @@ -827,8 +834,15 @@ u8 *queue_testcase_take(afl_state_t *afl, struct queue_entry *q) { afl->q_testcase_cache[tid] = q; } + q->testcase_refs++; - if (!q->testcase_buf) { FATAL("Testcase buf is NULL, this should never happen"); } + if (!q->testcase_buf) { + + FATAL("Testcase buf is NULL, this should never happen"); + + } + return q->testcase_buf; } + diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index dd9aaa8f..9b7c1445 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1153,7 +1153,9 @@ int main(int argc, char **argv_orig, char **envp) { if (extras_dir_cnt) { for (i = 0; i < extras_dir_cnt; i++) { + load_extras(afl, extras_dir[i]); + } dedup_extras(afl); -- cgit 1.4.1 From 4f207b4eba26c2b268ba2fd0a51298d6ab88f110 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 6 Oct 2020 16:20:32 +0200 Subject: fixed ref check --- src/afl-fuzz-queue.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 721f9ac7..58e026f5 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -772,13 +772,14 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { inline void queue_testcase_release(afl_state_t *afl, struct queue_entry *q) { (void)afl; - q->testcase_refs--; - if (unlikely(q->testcase_refs < 0)) { + if (unlikely(q->testcase_refs == 0)) { - FATAL("Testcase refcount smaller than 0"); + FATAL("Testcase refcount reduced past 0"); } + q->testcase_refs--; + } /* Returns the testcase buf from the file behind this queue entry. -- cgit 1.4.1 From 2d5fadc1e6a684b5e3e527a64b614f6b1ba8415f Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 6 Oct 2020 16:45:25 +0200 Subject: hunting ref underflow --- src/afl-fuzz-one.c | 3 --- src/afl-fuzz-queue.c | 11 +++++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index a5f77f11..f25ab4ee 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -4695,9 +4695,6 @@ pacemaker_fuzzing: } /* block */ - queue_testcase_release(afl, afl->queue_cur); - orig_in = NULL; - return ret_val; } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 58e026f5..0b491202 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -837,10 +837,17 @@ u8 *queue_testcase_take(afl_state_t *afl, struct queue_entry *q) { } q->testcase_refs++; - if (!q->testcase_buf) { + if (unlikely(!q->testcase_buf || !q->testcase_refs)) { + if (!q->testcase_buf) { + + FATAL("Testcase buf is NULL, this should never happen"); - FATAL("Testcase buf is NULL, this should never happen"); + } + if (!q->testcase_refs) { + FATAL("Testcase ref overflow. Missing a testcase release somwhere?"); + + } } return q->testcase_buf; -- cgit 1.4.1 From 3d7bdc9f0b6892cb359fc07a0cef387851cbd8b1 Mon Sep 17 00:00:00 2001 From: Edznux Date: Tue, 6 Oct 2020 23:00:11 +0200 Subject: [WIP: segfault on non dogstatsd] Adding MACROS for format --- include/afl-fuzz.h | 13 +++-- include/config.h | 3 -- include/envs.h | 1 + src/afl-fuzz-state.c | 7 +++ src/afl-fuzz-statsd.c | 140 +++++++++++++++++++++++++++++++++++++------------- src/afl-fuzz.c | 5 ++ 6 files changed, 127 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 92375b2c..ffb518ad 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -359,7 +359,8 @@ typedef struct afl_env_vars { u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload, - *afl_max_det_extras, *afl_statsd_host, *afl_statsd_port; + *afl_max_det_extras, *afl_statsd_host, *afl_statsd_port, + *afl_statsd_tags_flavor; } afl_env_vars_t; @@ -638,6 +639,9 @@ typedef struct afl_state { u64 statsd_last_send_ms; struct sockaddr_in statsd_server; int statsd_sock; + char * statsd_tags_flavor; + char * statsd_tags_format; + char * statsd_metric_format; double stats_avg_exec; @@ -961,9 +965,10 @@ void show_init_stats(afl_state_t *); /* StatsD */ -int statsd_socket_init(afl_state_t *afl); -int statsd_send_metric(afl_state_t *afl); -int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen); +void statsd_setup_format(afl_state_t *afl); +int statsd_socket_init(afl_state_t *afl); +int statsd_send_metric(afl_state_t *afl); +int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen); /* Run */ diff --git a/include/config.h b/include/config.h index c0a04565..3d6b0395 100644 --- a/include/config.h +++ b/include/config.h @@ -50,9 +50,6 @@ Server config can be adjusted with AFL_STATSD_HOST and AFL_STATSD_PORT env var. #define STATSD_DEFAULT_PORT 8125 #define STATSD_DEFAULT_HOST "127.0.0.1" -/* comment out to disable tags. */ -#define USE_DOGSTATSD_TAGS - /* If you want to have the original afl internal memory corruption checks. Disabled by default for speed. it is better to use "make ASAN_BUILD=1". */ diff --git a/include/envs.h b/include/envs.h index 1fc9e83d..16da14cb 100644 --- a/include/envs.h +++ b/include/envs.h @@ -137,6 +137,7 @@ static char *afl_environment_variables[] = { "AFL_SKIP_CRASHES", "AFL_STATSD_HOST", "AFL_STATSD_PORT", + "AFL_STATSD_TAGS_FLAVOR", "AFL_TMIN_EXACT", "AFL_TMPDIR", "AFL_TOKEN_FILE", diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 5e719a85..77b30b5b 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -377,6 +377,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_statsd_port = (u8 *)get_afl_env(afl_environment_variables[i]); + } else if (!strncmp(env, "AFL_STATSD_TAGS_FLAVOR", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd_tags_flavor = + (u8 *)get_afl_env(afl_environment_variables[i]); + } } else { diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index 9cb8fde4..f77df17e 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -12,6 +12,107 @@ #define MAX_TAG_LEN 200 #define METRIC_PREFIX "fuzzing" +/* Tags format for metrics + DogStatsD: + metric.name:||#key:value,key2:value2 + + InfluxDB + metric.name,key=value,key2=value2:| + + Librato + metric.name#key=value,key2=value2:| + + SignalFX + metric.name[key=value,key2=value2]:| + +*/ + +// after the whole metric. +#define DOGSTATSD_TAGS_FORMAT "|#banner:%s,afl_version:%s" + +// just after the metric name. +#define LIBRATO_TAGS_FORMAT "#banner=%s,afl_version=%s" +#define INFLUXDB_TAGS_FORMAT ",banner=%s,afl_version=%s" +#define SIGNALFX_TAGS_FORMAT "[banner=%s,afl_version=%s]" + +// For DogstatsD +#define STATSD_TAGS_AFTER_METRICS \ + METRIC_PREFIX \ + ".cycle_done:%llu|g%s\n" METRIC_PREFIX \ + ".cycles_wo_finds:%llu|g%s\n" METRIC_PREFIX \ + ".execs_done:%llu|g%s\n" METRIC_PREFIX \ + ".execs_per_sec:%0.02f|g%s\n" METRIC_PREFIX \ + ".paths_total:%u|g%s\n" METRIC_PREFIX \ + ".paths_favored:%u|g%s\n" METRIC_PREFIX \ + ".paths_found:%u|g%s\n" METRIC_PREFIX \ + ".paths_imported:%u|g%s\n" METRIC_PREFIX \ + ".max_depth:%u|g%s\n" METRIC_PREFIX ".cur_path:%u|g%s\n" METRIC_PREFIX \ + ".pending_favs:%u|g%s\n" METRIC_PREFIX \ + ".pending_total:%u|g%s\n" METRIC_PREFIX \ + ".variable_paths:%u|g%s\n" METRIC_PREFIX \ + ".unique_crashes:%llu|g%s\n" METRIC_PREFIX \ + ".unique_hangs:%llu|g%s\n" METRIC_PREFIX \ + ".total_crashes:%llu|g%s\n" METRIC_PREFIX \ + ".slowest_exec_ms:%u|g%s\n" METRIC_PREFIX \ + ".edges_found:%u|g%s\n" METRIC_PREFIX \ + ".var_byte_count:%u|g%s\n" METRIC_PREFIX ".havoc_expansion:%u|g%s\n" + +// For Librato, InfluxDB, SignalFX +#define STATSD_TAGS_MID_METRICS \ + METRIC_PREFIX \ + ".cycle_done%s:%llu|g\n" METRIC_PREFIX \ + ".cycles_wo_finds%s:%llu|g\n" METRIC_PREFIX \ + ".execs_done%s:%llu|g\n" METRIC_PREFIX \ + ".execs_per_sec%s:%0.02f|g\n" METRIC_PREFIX \ + ".paths_total%s:%u|g\n" METRIC_PREFIX \ + ".paths_favored%s:%u|g\n" METRIC_PREFIX \ + ".paths_found%s:%u|g\n" METRIC_PREFIX \ + ".paths_imported%s:%u|g\n" METRIC_PREFIX \ + ".max_depth%s:%u|g\n" METRIC_PREFIX ".cur_path%s:%u|g\n" METRIC_PREFIX \ + ".pending_favs%s:%u|g\n" METRIC_PREFIX \ + ".pending_total%s:%u|g\n" METRIC_PREFIX \ + ".variable_paths%s:%u|g\n" METRIC_PREFIX \ + ".unique_crashes%s:%llu|g\n" METRIC_PREFIX \ + ".unique_hangs%s:%llu|g\n" METRIC_PREFIX \ + ".total_crashes%s:%llu|g\n" METRIC_PREFIX \ + ".slowest_exec_ms%s:%u|g\n" METRIC_PREFIX \ + ".edges_found%s:%u|g\n" METRIC_PREFIX \ + ".var_byte_count%s:%u|g\n" METRIC_PREFIX ".havoc_expansion%s:%u|g\n" + +void statsd_setup_format(afl_state_t *afl) { + + if (strcmp(afl->afl_env.afl_statsd_tags_flavor, "dogstatsd") == 0) { + + afl->statsd_tags_format = DOGSTATSD_TAGS_FORMAT; + afl->statsd_metric_format = STATSD_TAGS_AFTER_METRICS; + + } else if (strcmp(afl->afl_env.afl_statsd_tags_flavor, "librato") == 0) { + + afl->statsd_tags_format = LIBRATO_TAGS_FORMAT; + afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; + + } else if (strcmp(afl->afl_env.afl_statsd_tags_flavor, "influxdb") == 0) { + + afl->statsd_tags_format = INFLUXDB_TAGS_FORMAT; + afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; + + } else if (strcmp(afl->afl_env.afl_statsd_tags_flavor, "signalfx") == 0) { + + afl->statsd_tags_format = SIGNALFX_TAGS_FORMAT; + afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; + + } else { + + // No tags at all. + afl->statsd_tags_format = ""; + // Still need to pick a format. Doesn't change anything since if will be + // replaced by the empty string anyway. + afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; + + } + +} + int statsd_socket_init(afl_state_t *afl) { /* Default port and host. @@ -87,7 +188,7 @@ int statsd_send_metric(afl_state_t *afl) { (struct sockaddr *)&afl->statsd_server, sizeof(afl->statsd_server)) == -1) { - if (!close(afl->statsd_sock)) { perror("Cannot close socket"); } + if (!close(afl->statsd_sock)) { FATAL("Cannot close socket"); } afl->statsd_sock = 0; WARNF("Cannot sendto"); return -1; @@ -100,45 +201,14 @@ int statsd_send_metric(afl_state_t *afl) { int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen) { -/* Metric format: -:| -*/ -#ifdef USE_DOGSTATSD_TAGS - /* Tags format: DogStatsD - :||#key:value,key:value,key - */ char tags[MAX_TAG_LEN * 2] = {0}; - snprintf(tags, MAX_TAG_LEN * 2, "|#banner:%s,afl_version:%s", afl->use_banner, + snprintf(tags, MAX_TAG_LEN * 2, afl->statsd_tags_format, afl->use_banner, VERSION); -#else - /* No tags. - */ - char *tags = ""; -#endif + /* Sends multiple metrics with one UDP Packet. bufflen will limit to the max safe size. */ - snprintf(buff, bufflen, - METRIC_PREFIX ".cycle_done:%llu|g%s\n" METRIC_PREFIX - ".cycles_wo_finds:%llu|g%s\n" METRIC_PREFIX - ".execs_done:%llu|g%s\n" METRIC_PREFIX - ".execs_per_sec:%0.02f|g%s\n" METRIC_PREFIX - ".paths_total:%u|g%s\n" METRIC_PREFIX - ".paths_favored:%u|g%s\n" METRIC_PREFIX - ".paths_found:%u|g%s\n" METRIC_PREFIX - ".paths_imported:%u|g%s\n" METRIC_PREFIX - ".max_depth:%u|g%s\n" METRIC_PREFIX - ".cur_path:%u|g%s\n" METRIC_PREFIX - ".pending_favs:%u|g%s\n" METRIC_PREFIX - ".pending_total:%u|g%s\n" METRIC_PREFIX - ".variable_paths:%u|g%s\n" METRIC_PREFIX - ".unique_crashes:%llu|g%s\n" METRIC_PREFIX - ".unique_hangs:%llu|g%s\n" METRIC_PREFIX - ".total_crashes:%llu|g%s\n" METRIC_PREFIX - ".slowest_exec_ms:%u|g%s\n" METRIC_PREFIX - ".edges_found:%u|g%s\n" METRIC_PREFIX - ".var_byte_count:%u|g%s\n" METRIC_PREFIX - ".havoc_expansion:%u|g%s\n", + snprintf(buff, bufflen, afl->statsd_metric_format, afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, afl->fsrv.total_execs / diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 0b08f426..e0e9487f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -891,6 +891,11 @@ int main(int argc, char **argv_orig, char **envp) { } + #ifdef USE_STATSD + statsd_setup_format(afl); + statsd_socket_init(afl); + #endif + if (strchr(argv[optind], '/') == NULL && !afl->unicorn_mode) { WARNF(cLRD -- cgit 1.4.1 From 17abe7d36e1bf63e149b3c0be20d3d0b7076746f Mon Sep 17 00:00:00 2001 From: Edznux Date: Tue, 6 Oct 2020 23:23:45 +0200 Subject: Fixed segfault because wrong order in args --- include/afl-fuzz.h | 1 + src/afl-fuzz-statsd.c | 137 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 83 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index ffb518ad..df7dd644 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -642,6 +642,7 @@ typedef struct afl_state { char * statsd_tags_flavor; char * statsd_tags_format; char * statsd_metric_format; + int statsd_metric_format_type; double stats_avg_exec; diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index f77df17e..e6640977 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -36,70 +36,74 @@ #define SIGNALFX_TAGS_FORMAT "[banner=%s,afl_version=%s]" // For DogstatsD -#define STATSD_TAGS_AFTER_METRICS \ - METRIC_PREFIX \ - ".cycle_done:%llu|g%s\n" METRIC_PREFIX \ - ".cycles_wo_finds:%llu|g%s\n" METRIC_PREFIX \ - ".execs_done:%llu|g%s\n" METRIC_PREFIX \ - ".execs_per_sec:%0.02f|g%s\n" METRIC_PREFIX \ - ".paths_total:%u|g%s\n" METRIC_PREFIX \ - ".paths_favored:%u|g%s\n" METRIC_PREFIX \ - ".paths_found:%u|g%s\n" METRIC_PREFIX \ - ".paths_imported:%u|g%s\n" METRIC_PREFIX \ - ".max_depth:%u|g%s\n" METRIC_PREFIX ".cur_path:%u|g%s\n" METRIC_PREFIX \ - ".pending_favs:%u|g%s\n" METRIC_PREFIX \ - ".pending_total:%u|g%s\n" METRIC_PREFIX \ - ".variable_paths:%u|g%s\n" METRIC_PREFIX \ - ".unique_crashes:%llu|g%s\n" METRIC_PREFIX \ - ".unique_hangs:%llu|g%s\n" METRIC_PREFIX \ - ".total_crashes:%llu|g%s\n" METRIC_PREFIX \ - ".slowest_exec_ms:%u|g%s\n" METRIC_PREFIX \ - ".edges_found:%u|g%s\n" METRIC_PREFIX \ - ".var_byte_count:%u|g%s\n" METRIC_PREFIX ".havoc_expansion:%u|g%s\n" +#define STATSD_TAGS_TYPE_SUFFIX 1 +#define STATSD_TAGS_SUFFIX_METRICS \ + METRIC_PREFIX \ + ".cycle_done:%llu|g%s\n" METRIC_PREFIX \ + ".cycles_wo_finds:%llu|g%s\n" METRIC_PREFIX \ + ".execs_done:%llu|g%s\n" METRIC_PREFIX \ + ".execs_per_sec:%0.02f|g%s\n" METRIC_PREFIX \ + ".paths_total:%u|g%s\n" METRIC_PREFIX \ + ".paths_favored:%u|g%s\n" METRIC_PREFIX \ + ".paths_found:%u|g%s\n" METRIC_PREFIX \ + ".paths_imported:%u|g%s\n" METRIC_PREFIX ".max_depth:%u|g%s\n" METRIC_PREFIX \ + ".cur_path:%u|g%s\n" METRIC_PREFIX ".pending_favs:%u|g%s\n" METRIC_PREFIX \ + ".pending_total:%u|g%s\n" METRIC_PREFIX \ + ".variable_paths:%u|g%s\n" METRIC_PREFIX \ + ".unique_crashes:%llu|g%s\n" METRIC_PREFIX \ + ".unique_hangs:%llu|g%s\n" METRIC_PREFIX \ + ".total_crashes:%llu|g%s\n" METRIC_PREFIX \ + ".slowest_exec_ms:%u|g%s\n" METRIC_PREFIX \ + ".edges_found:%u|g%s\n" METRIC_PREFIX \ + ".var_byte_count:%u|g%s\n" METRIC_PREFIX ".havoc_expansion:%u|g%s\n" // For Librato, InfluxDB, SignalFX -#define STATSD_TAGS_MID_METRICS \ - METRIC_PREFIX \ - ".cycle_done%s:%llu|g\n" METRIC_PREFIX \ - ".cycles_wo_finds%s:%llu|g\n" METRIC_PREFIX \ - ".execs_done%s:%llu|g\n" METRIC_PREFIX \ - ".execs_per_sec%s:%0.02f|g\n" METRIC_PREFIX \ - ".paths_total%s:%u|g\n" METRIC_PREFIX \ - ".paths_favored%s:%u|g\n" METRIC_PREFIX \ - ".paths_found%s:%u|g\n" METRIC_PREFIX \ - ".paths_imported%s:%u|g\n" METRIC_PREFIX \ - ".max_depth%s:%u|g\n" METRIC_PREFIX ".cur_path%s:%u|g\n" METRIC_PREFIX \ - ".pending_favs%s:%u|g\n" METRIC_PREFIX \ - ".pending_total%s:%u|g\n" METRIC_PREFIX \ - ".variable_paths%s:%u|g\n" METRIC_PREFIX \ - ".unique_crashes%s:%llu|g\n" METRIC_PREFIX \ - ".unique_hangs%s:%llu|g\n" METRIC_PREFIX \ - ".total_crashes%s:%llu|g\n" METRIC_PREFIX \ - ".slowest_exec_ms%s:%u|g\n" METRIC_PREFIX \ - ".edges_found%s:%u|g\n" METRIC_PREFIX \ - ".var_byte_count%s:%u|g\n" METRIC_PREFIX ".havoc_expansion%s:%u|g\n" +#define STATSD_TAGS_TYPE_MID 2 +#define STATSD_TAGS_MID_METRICS \ + METRIC_PREFIX \ + ".cycle_done%s:%llu|g\n" METRIC_PREFIX \ + ".cycles_wo_finds%s:%llu|g\n" METRIC_PREFIX \ + ".execs_done%s:%llu|g\n" METRIC_PREFIX \ + ".execs_per_sec%s:%0.02f|g\n" METRIC_PREFIX \ + ".paths_total%s:%u|g\n" METRIC_PREFIX \ + ".paths_favored%s:%u|g\n" METRIC_PREFIX \ + ".paths_found%s:%u|g\n" METRIC_PREFIX \ + ".paths_imported%s:%u|g\n" METRIC_PREFIX ".max_depth%s:%u|g\n" METRIC_PREFIX \ + ".cur_path%s:%u|g\n" METRIC_PREFIX ".pending_favs%s:%u|g\n" METRIC_PREFIX \ + ".pending_total%s:%u|g\n" METRIC_PREFIX \ + ".variable_paths%s:%u|g\n" METRIC_PREFIX \ + ".unique_crashes%s:%llu|g\n" METRIC_PREFIX \ + ".unique_hangs%s:%llu|g\n" METRIC_PREFIX \ + ".total_crashes%s:%llu|g\n" METRIC_PREFIX \ + ".slowest_exec_ms%s:%u|g\n" METRIC_PREFIX \ + ".edges_found%s:%u|g\n" METRIC_PREFIX \ + ".var_byte_count%s:%u|g\n" METRIC_PREFIX ".havoc_expansion%s:%u|g\n" void statsd_setup_format(afl_state_t *afl) { if (strcmp(afl->afl_env.afl_statsd_tags_flavor, "dogstatsd") == 0) { afl->statsd_tags_format = DOGSTATSD_TAGS_FORMAT; - afl->statsd_metric_format = STATSD_TAGS_AFTER_METRICS; + afl->statsd_metric_format = STATSD_TAGS_SUFFIX_METRICS; + afl->statsd_metric_format_type = STATSD_TAGS_TYPE_SUFFIX; } else if (strcmp(afl->afl_env.afl_statsd_tags_flavor, "librato") == 0) { afl->statsd_tags_format = LIBRATO_TAGS_FORMAT; afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; + afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID; } else if (strcmp(afl->afl_env.afl_statsd_tags_flavor, "influxdb") == 0) { afl->statsd_tags_format = INFLUXDB_TAGS_FORMAT; afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; + afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID; } else if (strcmp(afl->afl_env.afl_statsd_tags_flavor, "signalfx") == 0) { afl->statsd_tags_format = SIGNALFX_TAGS_FORMAT; afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; + afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID; } else { @@ -108,6 +112,7 @@ void statsd_setup_format(afl_state_t *afl) { // Still need to pick a format. Doesn't change anything since if will be // replaced by the empty string anyway. afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; + afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID; } @@ -188,7 +193,7 @@ int statsd_send_metric(afl_state_t *afl) { (struct sockaddr *)&afl->statsd_server, sizeof(afl->statsd_server)) == -1) { - if (!close(afl->statsd_sock)) { FATAL("Cannot close socket"); } + if (!close(afl->statsd_sock)) { PFATAL("Cannot close socket"); } afl->statsd_sock = 0; WARNF("Cannot sendto"); return -1; @@ -208,19 +213,41 @@ int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen) { /* Sends multiple metrics with one UDP Packet. bufflen will limit to the max safe size. */ - snprintf(buff, bufflen, afl->statsd_metric_format, - afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, - afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, - afl->fsrv.total_execs / - ((double)(get_cur_time() - afl->start_time) / 1000), - tags, afl->queued_paths, tags, afl->queued_favored, tags, - afl->queued_discovered, tags, afl->queued_imported, tags, - afl->max_depth, tags, afl->current_entry, tags, afl->pending_favored, - tags, afl->pending_not_fuzzed, tags, afl->queued_variable, tags, - afl->unique_crashes, tags, afl->unique_hangs, tags, - afl->total_crashes, tags, afl->slowest_exec_ms, tags, - count_non_255_bytes(afl, afl->virgin_bits), tags, - afl->var_byte_count, tags, afl->expand_havoc, tags); + if (afl->statsd_metric_format_type == STATSD_TAGS_TYPE_SUFFIX) { + + snprintf(buff, bufflen, afl->statsd_metric_format, + afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, + afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, + afl->fsrv.total_execs / + ((double)(get_cur_time() - afl->start_time) / 1000), + tags, afl->queued_paths, tags, afl->queued_favored, tags, + afl->queued_discovered, tags, afl->queued_imported, tags, + afl->max_depth, tags, afl->current_entry, tags, + afl->pending_favored, tags, afl->pending_not_fuzzed, tags, + afl->queued_variable, tags, afl->unique_crashes, tags, + afl->unique_hangs, tags, afl->total_crashes, tags, + afl->slowest_exec_ms, tags, + count_non_255_bytes(afl, afl->virgin_bits), tags, + afl->var_byte_count, tags, afl->expand_havoc, tags); + + } else if (afl->statsd_metric_format_type == STATSD_TAGS_TYPE_MID) { + + snprintf(buff, bufflen, afl->statsd_metric_format, tags, + afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, + afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, + afl->fsrv.total_execs / + ((double)(get_cur_time() - afl->start_time) / 1000), + tags, afl->queued_paths, tags, afl->queued_favored, tags, + afl->queued_discovered, tags, afl->queued_imported, tags, + afl->max_depth, tags, afl->current_entry, tags, + afl->pending_favored, tags, afl->pending_not_fuzzed, tags, + afl->queued_variable, tags, afl->unique_crashes, tags, + afl->unique_hangs, tags, afl->total_crashes, tags, + afl->slowest_exec_ms, tags, + count_non_255_bytes(afl, afl->virgin_bits), tags, + afl->var_byte_count, tags, afl->expand_havoc); + + } return 0; -- cgit 1.4.1 From 3e16cf5fbf111b75e53abee91cca5202715eb411 Mon Sep 17 00:00:00 2001 From: Edznux Date: Wed, 7 Oct 2020 00:20:13 +0200 Subject: Remove statsd_socket_init, just let it init when doing the first iteration --- src/afl-fuzz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e0e9487f..0b38bdd8 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -893,7 +893,7 @@ int main(int argc, char **argv_orig, char **envp) { #ifdef USE_STATSD statsd_setup_format(afl); - statsd_socket_init(afl); + #endif if (strchr(argv[optind], '/') == NULL && !afl->unicorn_mode) { -- cgit 1.4.1 From 1fd2ffaf14efb3623ae01951ae0266e0f4f553f2 Mon Sep 17 00:00:00 2001 From: Edznux Date: Wed, 7 Oct 2020 00:51:31 +0200 Subject: Fix read on undefined char*. --- src/afl-fuzz-statsd.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index e6640977..69cafd90 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -81,25 +81,32 @@ void statsd_setup_format(afl_state_t *afl) { - if (strcmp(afl->afl_env.afl_statsd_tags_flavor, "dogstatsd") == 0) { + if (afl->afl_env.afl_statsd_tags_flavor && + strcmp(afl->afl_env.afl_statsd_tags_flavor, "dogstatsd") == 0) { afl->statsd_tags_format = DOGSTATSD_TAGS_FORMAT; afl->statsd_metric_format = STATSD_TAGS_SUFFIX_METRICS; afl->statsd_metric_format_type = STATSD_TAGS_TYPE_SUFFIX; - } else if (strcmp(afl->afl_env.afl_statsd_tags_flavor, "librato") == 0) { + } else if (afl->afl_env.afl_statsd_tags_flavor && + + strcmp(afl->afl_env.afl_statsd_tags_flavor, "librato") == 0) { afl->statsd_tags_format = LIBRATO_TAGS_FORMAT; afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID; - } else if (strcmp(afl->afl_env.afl_statsd_tags_flavor, "influxdb") == 0) { + } else if (afl->afl_env.afl_statsd_tags_flavor && + + strcmp(afl->afl_env.afl_statsd_tags_flavor, "influxdb") == 0) { afl->statsd_tags_format = INFLUXDB_TAGS_FORMAT; afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID; - } else if (strcmp(afl->afl_env.afl_statsd_tags_flavor, "signalfx") == 0) { + } else if (afl->afl_env.afl_statsd_tags_flavor && + + strcmp(afl->afl_env.afl_statsd_tags_flavor, "signalfx") == 0) { afl->statsd_tags_format = SIGNALFX_TAGS_FORMAT; afl->statsd_metric_format = STATSD_TAGS_MID_METRICS; @@ -207,8 +214,12 @@ int statsd_send_metric(afl_state_t *afl) { int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen) { char tags[MAX_TAG_LEN * 2] = {0}; - snprintf(tags, MAX_TAG_LEN * 2, afl->statsd_tags_format, afl->use_banner, - VERSION); + if (afl->statsd_tags_format) { + + snprintf(tags, MAX_TAG_LEN * 2, afl->statsd_tags_format, afl->use_banner, + VERSION); + + } /* Sends multiple metrics with one UDP Packet. bufflen will limit to the max safe size. -- cgit 1.4.1 From c12b98e0a4a98200b43b01a9b0b721f8ca1e4dbf Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 7 Oct 2020 17:45:54 +0200 Subject: efficient queue jump --- src/afl-fuzz.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 2f8aa3fd..8458b50f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1331,11 +1331,11 @@ int main(int argc, char **argv_orig, char **envp) { afl->cur_skipped_paths = 0; afl->queue_cur = afl->queue; - while (seek_to) { + if (seek_to) { - ++afl->current_entry; - --seek_to; - afl->queue_cur = afl->queue_cur->next; + afl->current_entry = seek_to; + afl->queue_cur = afl->queue_buf[seek_to]; + seek_to = 0; } -- cgit 1.4.1 From deab5a1532777df16aaf83bda2dc6cf757a8b49e Mon Sep 17 00:00:00 2001 From: Edznux Date: Wed, 7 Oct 2020 19:44:02 +0200 Subject: Add documentation/help for AFL_STATSD_TAGS_FLAVOR --- src/afl-fuzz.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 0b38bdd8..c26e53b9 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -196,6 +196,8 @@ static void usage(u8 *argv0, int more_help) { "AFL_SKIP_CRASHES: during initial dry run do not terminate for crashing inputs\n" "AFL_STATSD_HOST: change default statsd host. (default 127.0.0.1)" "AFL_STATSD_PORT: change default statsd port (default: 8125)" + "AFL_STATSD_TAGS_FLAVOR: change default statsd tags format (default will disable tags)." + " Supported formats are: 'dogstatsd', 'librato', 'signalfx' and 'influxdb'" "AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)\n" //"AFL_PERSISTENT: not supported anymore -> no effect, just a warning\n" //"AFL_DEFER_FORKSRV: not supported anymore -> no effect, just a warning\n" -- cgit 1.4.1 From 0220a8ff6670da2b9fd8cb883528d2a86f683c74 Mon Sep 17 00:00:00 2001 From: Edznux Date: Thu, 8 Oct 2020 20:48:46 +0200 Subject: Add env var toggle for StatsD --- include/afl-fuzz.h | 2 +- include/config.h | 7 +++---- include/envs.h | 1 + src/afl-fuzz-state.c | 7 +++++++ src/afl-fuzz-stats.c | 15 ++++++++------- src/afl-fuzz.c | 8 +++----- 6 files changed, 23 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index df7dd644..8ad0ced1 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -355,7 +355,7 @@ typedef struct afl_env_vars { afl_dumb_forksrv, afl_import_first, afl_custom_mutator_only, afl_no_ui, afl_force_ui, afl_i_dont_care_about_missing_crashes, afl_bench_just_one, afl_bench_until_crash, afl_debug_child_output, afl_autoresume, - afl_cal_fast, afl_cycle_schedules, afl_expand_havoc; + afl_cal_fast, afl_cycle_schedules, afl_expand_havoc, afl_statsd; u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload, diff --git a/include/config.h b/include/config.h index 3d6b0395..405c2712 100644 --- a/include/config.h +++ b/include/config.h @@ -41,11 +41,10 @@ #define USE_COLOR -/* Enable sending statistics over a StatsD daemon. -Server config can be adjusted with AFL_STATSD_HOST and AFL_STATSD_PORT env var. +/* StatsD config + Config can be adjusted via AFL_STATSD_HOST and AFL_STATSD_PORT environment + variable. */ - -#define USE_STATSD #define STATSD_UPDATE_SEC 1 #define STATSD_DEFAULT_PORT 8125 #define STATSD_DEFAULT_HOST "127.0.0.1" diff --git a/include/envs.h b/include/envs.h index 16da14cb..51520312 100644 --- a/include/envs.h +++ b/include/envs.h @@ -135,6 +135,7 @@ static char *afl_environment_variables[] = { "AFL_SKIP_BIN_CHECK", "AFL_SKIP_CPUFREQ", "AFL_SKIP_CRASHES", + "AFL_STATSD", "AFL_STATSD_HOST", "AFL_STATSD_PORT", "AFL_STATSD_TAGS_FLAVOR", diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 77b30b5b..a7c7aff9 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -316,6 +316,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_cal_fast = get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_STATSD", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd = + get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_TMPDIR", afl_environment_variable_len)) { diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 942670b4..0d6c6a66 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -423,16 +423,17 @@ void show_stats(afl_state_t *afl) { } -#ifdef USE_STATSD - if (cur_ms - afl->statsd_last_send_ms > STATSD_UPDATE_SEC * 1000) { + if (unlikely(afl->afl_env.afl_statsd == 1)) { - /* reset counter, even if send failed. */ - afl->statsd_last_send_ms = cur_ms; - if (statsd_send_metric(afl)) { WARNF("coundln't send statsd metric."); } + if (cur_ms - afl->statsd_last_send_ms > STATSD_UPDATE_SEC * 1000) { - } + /* reset counter, even if send failed. */ + afl->statsd_last_send_ms = cur_ms; + if (statsd_send_metric(afl)) { WARNF("coundln't send statsd metric."); } -#endif + } + + } /* Every now and then, write plot data. */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index c26e53b9..2c2f0ba5 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -194,7 +194,8 @@ static void usage(u8 *argv0, int more_help) { "AFL_SKIP_BIN_CHECK: skip the check, if the target is an executable\n" "AFL_SKIP_CPUFREQ: do not warn about variable cpu clocking\n" "AFL_SKIP_CRASHES: during initial dry run do not terminate for crashing inputs\n" - "AFL_STATSD_HOST: change default statsd host. (default 127.0.0.1)" + "AFL_STATSD: enables StatsD metrics collection" + "AFL_STATSD_HOST: change default statsd host (default 127.0.0.1)" "AFL_STATSD_PORT: change default statsd port (default: 8125)" "AFL_STATSD_TAGS_FLAVOR: change default statsd tags format (default will disable tags)." " Supported formats are: 'dogstatsd', 'librato', 'signalfx' and 'influxdb'" @@ -893,10 +894,7 @@ int main(int argc, char **argv_orig, char **envp) { } - #ifdef USE_STATSD - statsd_setup_format(afl); - - #endif + if (unlikely(afl->afl_env.afl_statsd == 1)) { statsd_setup_format(afl); } if (strchr(argv[optind], '/') == NULL && !afl->unicorn_mode) { -- cgit 1.4.1 From 4cb4772e2af9511c2320de833a81bc43500cef14 Mon Sep 17 00:00:00 2001 From: Edznux Date: Fri, 9 Oct 2020 18:58:27 +0200 Subject: Remove ==1 in the condition --- src/afl-fuzz-stats.c | 2 +- src/afl-fuzz.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 0d6c6a66..8a1c2cc6 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -423,7 +423,7 @@ void show_stats(afl_state_t *afl) { } - if (unlikely(afl->afl_env.afl_statsd == 1)) { + if (unlikely(afl->afl_env.afl_statsd)) { if (cur_ms - afl->statsd_last_send_ms > STATSD_UPDATE_SEC * 1000) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 2c2f0ba5..aa36a6c6 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -894,7 +894,7 @@ int main(int argc, char **argv_orig, char **envp) { } - if (unlikely(afl->afl_env.afl_statsd == 1)) { statsd_setup_format(afl); } + if (unlikely(afl->afl_env.afl_statsd)) { statsd_setup_format(afl); } if (strchr(argv[optind], '/') == NULL && !afl->unicorn_mode) { -- cgit 1.4.1 From 125f8b6ba71fba91735374b1bd07333b19aae635 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 9 Oct 2020 23:23:44 +0200 Subject: -m none is the default now --- docs/Changelog.md | 1 + examples/persistent_demo/persistent_demo_new.c | 3 ++- include/config.h | 24 ++++++++---------------- src/afl-fuzz-state.c | 2 +- src/afl-fuzz.c | 4 ++-- 5 files changed, 14 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 0f923423..ba7028df 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,7 @@ sending a mail to . - all compilers combined to afl-cc which emulates the previous ones - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o - afl-fuzz + - memory limits are now disabled by default, set them with -m if required - Marcel Boehme submitted a patch that improves all AFFast schedules :) - reading testcases from -i now descends into subdirectories - allow up to 4 -x command line options diff --git a/examples/persistent_demo/persistent_demo_new.c b/examples/persistent_demo/persistent_demo_new.c index 13123d33..b8b4cda0 100644 --- a/examples/persistent_demo/persistent_demo_new.c +++ b/examples/persistent_demo/persistent_demo_new.c @@ -37,7 +37,8 @@ unsigned char fuzz_buf[1024000]; #define __AFL_FUZZ_TESTCASE_LEN fuzz_len #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf #define __AFL_FUZZ_INIT() void sync(void); - #define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0) + #define __AFL_LOOP(x) \ + ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0) #define __AFL_INIT() sync() #endif diff --git a/include/config.h b/include/config.h index 3f5c5448..5df604e7 100644 --- a/include/config.h +++ b/include/config.h @@ -66,25 +66,17 @@ #define WORD_SIZE_64 1 #endif -/* Default memory limit for child process (MB): */ - -#ifndef __NetBSD__ - #ifndef WORD_SIZE_64 - #define MEM_LIMIT 50 - #else - #define MEM_LIMIT 75 - #endif /* ^!WORD_SIZE_64 */ -#else /* NetBSD's kernel needs more space for stack, see discussion for issue \ - #165 */ - #define MEM_LIMIT 250 -#endif -/* Default memory limit when running in QEMU mode (MB): */ +/* Default memory limit for child process (MB) 0 = disabled : */ + +#define MEM_LIMIT 0 + +/* Default memory limit when running in QEMU mode (MB) 0 = disabled : */ -#define MEM_LIMIT_QEMU 250 +#define MEM_LIMIT_QEMU 0 -/* Default memory limit when running in Unicorn mode (MB): */ +/* Default memory limit when running in Unicorn mode (MB) 0 = disabled : */ -#define MEM_LIMIT_UNICORN 250 +#define MEM_LIMIT_UNICORN 0 /* Number of calibration cycles per every new test case (and for test cases that show variable behavior): */ diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 4a1e739f..a8e56e60 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -87,7 +87,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->w_end = 0.3; afl->g_max = 5000; afl->period_pilot_tmp = 5000.0; - afl->schedule = COE; /* Power schedule (default: COE) */ + afl->schedule = EXPLORE; /* Power schedule (default: EXPLORE) */ afl->havoc_max_mult = HAVOC_MAX_MULT; afl->clear_screen = 1; /* Window resized? */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 8458b50f..cf0a30c9 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -90,13 +90,13 @@ static void usage(u8 *argv0, int more_help) { "Execution control settings:\n" " -p schedule - power schedules compute a seed's performance score:\n" - " -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " "or @@)\n" " -t msec - timeout for each run (auto-scaled, 50-%d ms)\n" - " -m megs - memory limit for child process (%d MB)\n" + " -m megs - memory limit for child process (%d MB, 0 = no limit)\n" " -Q - use binary-only instrumentation (QEMU mode)\n" " -U - use unicorn-based instrumentation (Unicorn mode)\n" " -W - use qemu-based instrumentation with Wine (Wine " -- cgit 1.4.1 From 5dc3bc175b664f0921ebd1265d4419d611aa4a74 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 10 Oct 2020 10:41:30 +0200 Subject: fix typo --- docs/Changelog.md | 1 + src/afl-fuzz-stats.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index ba7028df..aa55fbde 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -15,6 +15,7 @@ sending a mail to . - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o - afl-fuzz - memory limits are now disabled by default, set them with -m if required + - statsd support by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) - reading testcases from -i now descends into subdirectories - allow up to 4 -x command line options diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 0cd6f399..76f24977 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -429,7 +429,7 @@ void show_stats(afl_state_t *afl) { /* reset counter, even if send failed. */ afl->statsd_last_send_ms = cur_ms; - if (statsd_send_metric(afl)) { WARNF("coundln't send statsd metric."); } + if (statsd_send_metric(afl)) { WARNF("could not send statsd metric."); } } -- cgit 1.4.1 From 445aba9221471eebd7ffc2c35b97accd00b40557 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 10 Oct 2020 10:55:56 +0200 Subject: determinstic fuzzing is now disabled by default --- README.md | 8 ++++++-- docs/Changelog.md | 2 ++ docs/life_pro_tips.md | 3 --- docs/status_screen.md | 8 +------- src/afl-fuzz-state.c | 2 ++ src/afl-fuzz.c | 7 +++---- 6 files changed, 14 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/README.md b/README.md index f63b0c1e..819da093 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,13 @@ behaviours: worth it. * When instrumenting targets, afl-cc will not supersede optimizations. This allows to fuzz targets as same as they are built for debug or release. - * afl-fuzz' `-i` option now descends into subdirectories. + * afl-fuzz': + * `-i` option now descends into subdirectories. + * -m none is now default, set memory limits (in MB) with e.g. -m 250 + * deterministic fuzzing is now disabled by default (unless using -M) and + can be enabled with -D * afl-fuzz will skip over empty dictionaries and too-large test cases instead - of failing. + of failing, and use them as a source for splicing mutations ## Contents diff --git a/docs/Changelog.md b/docs/Changelog.md index aa55fbde..9eb47e18 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -15,6 +15,8 @@ sending a mail to . - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o - afl-fuzz - memory limits are now disabled by default, set them with -m if required + - deterministic fuzzing is now disabled by default and can be enabled with + -D. It is still enabled by default for -M. - statsd support by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) - reading testcases from -i now descends into subdirectories diff --git a/docs/life_pro_tips.md b/docs/life_pro_tips.md index 0004c297..323f16f1 100644 --- a/docs/life_pro_tips.md +++ b/docs/life_pro_tips.md @@ -85,6 +85,3 @@ You can find a simple solution in examples/argv_fuzzing. Remove the checksum-checking code or use a postprocessor! See examples/custom_mutators/ for more. -## Dealing with a very slow target or hoping for instant results? - -Specify `-d` when calling afl-fuzz! diff --git a/docs/status_screen.md b/docs/status_screen.md index 2eeb8f3f..f7655bf4 100644 --- a/docs/status_screen.md +++ b/docs/status_screen.md @@ -86,10 +86,7 @@ Every fuzzing session should be allowed to complete at least one cycle; and ideally, should run much longer than that. As noted earlier, the first pass can take a day or longer, so sit back and -relax. If you want to get broader but more shallow coverage right away, try -the `-d` option - it gives you a more familiar experience by skipping the -deterministic fuzzing steps. It is, however, inferior to the standard mode in -a couple of subtle ways. +relax. To help make the call on when to hit `Ctrl-C`, the cycle counter is color-coded. It is shown in magenta during the first pass, progresses to yellow if new finds @@ -118,9 +115,6 @@ inputs it decided to ditch because they were persistently timing out. The "*" suffix sometimes shown in the first line means that the currently processed path is not "favored" (a property discussed later on). -If you feel that the fuzzer is progressing too slowly, see the note about the -`-d` option in this doc. - ### Map coverage ``` diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index b7d44dbf..a0a2795e 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -101,6 +101,8 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->hang_tmout = EXEC_TIMEOUT; afl->stats_update_freq = 1; afl->stats_avg_exec = -1; + afl->skip_deterministic = 1; + afl->use_splicing = 1; #ifdef HAVE_AFFINITY afl->cpu_aff = -1; /* Selected CPU core */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index dc0eb4a7..24df2997 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -103,6 +103,7 @@ static void usage(u8 *argv0, int more_help) { "mode)\n\n" "Mutator settings:\n" + " -D - enable deterministic fuzzing (once per queue entry)\n" " -L minutes - use MOpt(imize) mode and set the time limit for " "entering the\n" " pacemaker mode (minutes of no new paths). 0 = " @@ -116,7 +117,6 @@ static void usage(u8 *argv0, int more_help) { "Fuzzing behavior settings:\n" " -N - do not unlink the fuzzing input file (for devices " "etc.)\n" - " -d - quick & dirty mode (skips deterministic steps)\n" " -n - fuzz without instrumentation (non-instrumented mode)\n" " -x dict_file - fuzzer dictionary (see README.md, specify up to 4 " "times)\n\n" @@ -136,6 +136,7 @@ static void usage(u8 *argv0, int more_help) { " -F path - sync to a foreign fuzzer queue directory (requires " "-M, can\n" " be specified up to %u times)\n" + " -d - skip deterministic fuzzing in -M mode\n" " -T text - text banner to show on the screen\n" " -I command - execute this command/script when a new crash is " "found\n" @@ -403,6 +404,7 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } afl->sync_id = ck_strdup(optarg); + afl->skip_deterministic = 0; if ((c = strchr(afl->sync_id, ':'))) { @@ -431,8 +433,6 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } afl->sync_id = ck_strdup(optarg); afl->is_secondary_node = 1; - afl->skip_deterministic = 1; - afl->use_splicing = 1; break; case 'F': /* foreign sync dir */ @@ -557,7 +557,6 @@ int main(int argc, char **argv_orig, char **envp) { case 'd': /* skip deterministic */ afl->skip_deterministic = 1; - afl->use_splicing = 1; break; case 'B': /* load bitmap */ -- cgit 1.4.1 From 5540a055c5d71d0b348a5ba33b9239ec2a8802dc Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 10 Oct 2020 11:19:45 +0200 Subject: afl-showmap: free malloced coverage map --- src/afl-showmap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/afl-showmap.c b/src/afl-showmap.c index f4a7c336..545bfaa9 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1160,6 +1160,7 @@ int main(int argc, char **argv_orig, char **envp) { afl_fsrv_deinit(fsrv); if (stdin_file) { ck_free(stdin_file); } + if (collect_coverage) { free(coverage_map); } argv_cpy_free(argv); if (fsrv->qemu_mode) { free(use_argv[2]); } -- cgit 1.4.1 From 01ec0cce4759526499becab169c07b2ccf1eee89 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 10 Oct 2020 15:44:47 +0200 Subject: fix for afl-analyze thanks to JJY-sec! --- src/afl-analyze.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-analyze.c b/src/afl-analyze.c index 7c1c269a..bfdd66e9 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -922,11 +922,12 @@ static void usage(u8 *argv0) { /* Main entry point */ -int main(int argc, char **argv, char **envp) { +int main(int argc, char **argv_orig, char **envp) { s32 opt; u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0; char **use_argv; + char **argv = argv_cpy_dup(argc, argv_orig); doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; -- cgit 1.4.1 From 6a397d6111a21ebbf736237609c1c69d47c40f03 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 11 Oct 2020 14:31:31 +0200 Subject: add new seed selection algo and make it the default --- docs/Changelog.md | 4 ++ include/afl-fuzz.h | 14 +++++- src/afl-fuzz-init.c | 8 ++++ src/afl-fuzz-one.c | 10 ++++- src/afl-fuzz-queue.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/afl-fuzz.c | 100 +++++++++++++++++++++++++++++------------ 6 files changed, 227 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 9eb47e18..f15f1d93 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -17,6 +17,10 @@ sending a mail to . - memory limits are now disabled by default, set them with -m if required - deterministic fuzzing is now disabled by default and can be enabled with -D. It is still enabled by default for -M. + - a new seed selection was implemented that uses weighted randoms based on + a schedule performance score, which is much better that the previous + walk the whole queue approach. Select the old mode with -Z (auto enabled + with -M) - statsd support by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) - reading testcases from -i now descends into subdirectories diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index e9d148e9..45de197d 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -151,7 +151,8 @@ struct queue_entry { favored, /* Currently favored? */ fs_redundant, /* Marked as redundant in the fs? */ fully_colorized, /* Do not run redqueen stage again */ - is_ascii; /* Is the input just ascii text? */ + is_ascii, /* Is the input just ascii text? */ + disabled; /* Is disabled from fuzz selection */ u32 bitmap_size, /* Number of bits set in bitmap */ fuzz_level, /* Number of fuzzing iterations */ @@ -165,6 +166,8 @@ struct queue_entry { u8 *trace_mini; /* Trace bytes, if kept */ u32 tc_ref; /* Trace bytes ref count */ + double perf_score; /* performance score */ + struct queue_entry *next; /* Next element, if any */ }; @@ -488,12 +491,17 @@ typedef struct afl_state { disable_trim, /* Never trim in fuzz_one */ shmem_testcase_mode, /* If sharedmem testcases are used */ expand_havoc, /* perform expensive havoc after no find */ - cycle_schedules; /* cycle power schedules? */ + cycle_schedules, /* cycle power schedules? */ + old_seed_selection; /* use vanilla afl seed selection */ u8 *virgin_bits, /* Regions yet untouched by fuzzing */ *virgin_tmout, /* Bits we haven't seen in tmouts */ *virgin_crash; /* Bits we haven't seen in crashes */ + double *alias_probability; /* alias weighted probabilities */ + u32 * alias_table; /* alias weighted random lookup table */ + u32 active_paths; /* enabled entries in the queue */ + u8 *var_bytes; /* Bytes that appear to be variable */ #define N_FUZZ_SIZE (1 << 21) @@ -1009,6 +1017,8 @@ void find_timeout(afl_state_t *); double get_runnable_processes(void); void nuke_resume_dir(afl_state_t *); int check_main_node_exists(afl_state_t *); +u32 select_next_queue_entry(afl_state_t *afl); +void create_alias_table(afl_state_t *afl); void setup_dirs_fds(afl_state_t *); void setup_cmdline_file(afl_state_t *, char **); void setup_stdio_file(afl_state_t *); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 65478a78..881bf10f 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -959,6 +959,8 @@ void perform_dry_run(afl_state_t *afl) { /* Remove from fuzzing queue but keep for splicing */ struct queue_entry *p = afl->queue; + p->disabled = 1; + p->perf_score = 0; while (p && p->next != q) p = p->next; @@ -968,6 +970,7 @@ void perform_dry_run(afl_state_t *afl) { afl->queue = q->next; --afl->pending_not_fuzzed; + --afl->active_paths; afl->max_depth = 0; p = afl->queue; @@ -1054,6 +1057,7 @@ restart_outer_cull_loop: duplicates = 1; --afl->pending_not_fuzzed; + afl->active_paths--; // We do not remove any of the memory allocated because for // splicing the data might still be interesting. @@ -1063,11 +1067,15 @@ restart_outer_cull_loop: // we keep the shorter file if (p->len >= q->len) { + p->disabled = 1; + p->perf_score = 0; q->next = p->next; goto restart_inner_cull_loop; } else { + q->disabled = 1; + q->perf_score = 0; if (prev) prev->next = q = p; else diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index c04b492b..6ef728e0 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -554,7 +554,10 @@ u8 fuzz_one_original(afl_state_t *afl) { * PERFORMANCE SCORE * *********************/ - orig_perf = perf_score = calculate_score(afl, afl->queue_cur); + if (likely(!afl->old_seed_selection)) + orig_perf = perf_score = afl->queue_cur->perf_score; + else + orig_perf = perf_score = calculate_score(afl, afl->queue_cur); if (unlikely(perf_score == 0)) { goto abandon_entry; } @@ -2769,7 +2772,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { * PERFORMANCE SCORE * *********************/ - orig_perf = perf_score = calculate_score(afl, afl->queue_cur); + if (likely(!afl->old_seed_selection)) + orig_perf = perf_score = afl->queue_cur->perf_score; + else + orig_perf = perf_score = calculate_score(afl, afl->queue_cur); if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 0d7d0314..d608e890 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -27,6 +27,129 @@ #include #include +inline u32 select_next_queue_entry(afl_state_t *afl) { + + u32 r = rand_below(afl, 0xffffffff); + u32 s = r % afl->queued_paths; + // fprintf(stderr, "select: r=%u s=%u ... r < prob[s]=%f ? s=%u : + // alias[%u]=%u\n", r, s, afl->alias_probability[s], s, s, + // afl->alias_table[s]); + return (r < afl->alias_probability[s] ? s : afl->alias_table[s]); + +} + +void create_alias_table(afl_state_t *afl) { + + u32 n = afl->queued_paths, i = 0, a, g; + + afl->alias_table = + (u32 *)afl_realloc((void **)&afl->alias_table, n * sizeof(u32)); + afl->alias_probability = (double *)afl_realloc( + (void **)&afl->alias_probability, n * sizeof(double)); + double *P = (double *)afl_realloc(AFL_BUF_PARAM(out), n * sizeof(double)); + int * S = (u32 *)afl_realloc(AFL_BUF_PARAM(out_scratch), n * sizeof(u32)); + int * L = (u32 *)afl_realloc(AFL_BUF_PARAM(in_scratch), n * sizeof(u32)); + + if (!P || !S || !L) FATAL("could not aquire memory for alias table"); + memset((void *)afl->alias_table, 0, n * sizeof(u32)); + memset((void *)afl->alias_probability, 0, n * sizeof(double)); + + double sum = 0; + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + + if (!q->disabled) q->perf_score = calculate_score(afl, q); + + sum += q->perf_score; + /* + if (afl->debug) + fprintf(stderr, "entry %u: score=%f %s (sum: %f)\n", i, q->perf_score, + q->disabled ? "disabled" : "", sum); + */ + + } + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + + P[i] = q->perf_score * n / sum; + + } + + int nS = 0, nL = 0, s; + for (s = (s32)n - 1; s >= 0; --s) { + + if (P[s] < 1) + S[nS++] = s; + else + L[nL++] = s; + + } + + while (nS && nL) { + + a = S[--nS]; + g = L[--nL]; + afl->alias_probability[a] = P[a]; + afl->alias_table[a] = g; + P[g] = P[g] + P[a] - 1; + if (P[g] < 1) + S[nS++] = g; + else + L[nL++] = g; + + } + + while (nL) + afl->alias_probability[L[--nL]] = 1; + + while (nS) + afl->alias_probability[S[--nS]] = 1; + + /* + if (afl->debug) { + + fprintf(stderr, " %-3s %-3s %-9s\n", "entry", "alias", "prob"); + for (u32 i = 0; i < n; ++i) + fprintf(stderr, " %3i %3i %9.7f\n", i, afl->alias_table[i], + afl->alias_probability[i]); + + } + + int prob = 0; + fprintf(stderr, "Alias:"); + for (i = 0; i < n; i++) { + + fprintf(stderr, " [%u]=%u", i, afl->alias_table[i]); + if (afl->alias_table[i] >= n) + prob = i; + + } + + fprintf(stderr, "\n"); + + if (prob) { + + fprintf(stderr, "PROBLEM! alias[%u] = %u\n", prob, + afl->alias_table[prob]); + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + + fprintf(stderr, "%u: score=%f\n", i, q->perf_score); + + } + + } + + */ + +} + /* Mark deterministic checks as done for a particular queue entry. We use the .state file to avoid repeating deterministic fuzzing when resuming aborted scans. */ @@ -237,6 +360,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { if (likely(q->len > 4)) afl->ready_for_splicing_count++; ++afl->queued_paths; + ++afl->active_paths; ++afl->pending_not_fuzzed; afl->cycles_wo_finds = 0; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 24df2997..004adffe 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -115,6 +115,8 @@ static void usage(u8 *argv0, int more_help) { " if using QEMU, just use -c 0.\n\n" "Fuzzing behavior settings:\n" + " -Z - sequential queue selection instead of weighted " + "random\n" " -N - do not unlink the fuzzing input file (for devices " "etc.)\n" " -n - fuzz without instrumentation (non-instrumented mode)\n" @@ -131,8 +133,7 @@ static void usage(u8 *argv0, int more_help) { "Other stuff:\n" " -M/-S id - distributed mode (see docs/parallel_fuzzing.md)\n" - " use -D to force -S secondary to perform deterministic " - "fuzzing\n" + " -M auto-sets -D and -Z (use -d to disable -D)\n" " -F path - sync to a foreign fuzzer queue directory (requires " "-M, can\n" " be specified up to %u times)\n" @@ -250,7 +251,7 @@ int main(int argc, char **argv_orig, char **envp) { s32 opt, i; u64 prev_queued = 0; - u32 sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE; + u32 sync_interval_cnt = 0, seek_to = 0, show_help = 0, map_size = MAP_SIZE; u8 *extras_dir[4]; u8 mem_limit_given = 0, exit_1 = 0, debug = 0, extras_dir_cnt = 0 /*, have_p = 0*/; @@ -287,10 +288,14 @@ int main(int argc, char **argv_orig, char **envp) { while ((opt = getopt( argc, argv, - "+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:")) > 0) { + "+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:Z")) > 0) { switch (opt) { + case 'Z': + afl->old_seed_selection = 1; + break; + case 'I': afl->infoexec = optarg; break; @@ -355,14 +360,16 @@ int main(int argc, char **argv_orig, char **envp) { afl->schedule = RARE; - } else if (!stricmp(optarg, "explore") || !stricmp(optarg, "afl")) { - - afl->schedule = EXPLORE; + } else if (!stricmp(optarg, "explore") || !stricmp(optarg, "afl") || - } else if (!stricmp(optarg, "seek") || !stricmp(optarg, "default") || + !stricmp(optarg, "default") || !stricmp(optarg, "normal")) { + afl->schedule = EXPLORE; + + } else if (!stricmp(optarg, "seek")) { + afl->schedule = SEEK; } else { @@ -404,7 +411,8 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } afl->sync_id = ck_strdup(optarg); - afl->skip_deterministic = 0; + afl->skip_deterministic = 0; // force determinsitic fuzzing + afl->old_seed_selection = 1; // force old queue walking seed selection if ((c = strchr(afl->sync_id, ':'))) { @@ -1131,8 +1139,10 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->is_secondary_node && check_main_node_exists(afl) == 0) { - WARNF("no -M main node found. You need to run one main instance!"); - sleep(3); + WARNF( + "no -M main node found. It is recommended to run exactly one main " + "instance."); + sleep(1); } @@ -1302,7 +1312,7 @@ int main(int argc, char **argv_orig, char **envp) { show_init_stats(afl); - seek_to = find_start_position(afl); + if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl); write_stats_file(afl, 0, 0, 0); maybe_update_plot_file(afl, 0, 0); @@ -1324,28 +1334,37 @@ int main(int argc, char **argv_orig, char **envp) { // real start time, we reset, so this works correctly with -V afl->start_time = get_cur_time(); + u32 runs_in_current_cycle = (u32)-1; + u32 prev_queued_paths = 0; + while (1) { u8 skipped_fuzz; cull_queue(afl); - if (!afl->queue_cur) { + if (unlikely((!afl->old_seed_selection && + runs_in_current_cycle > afl->queued_paths) || + (afl->old_seed_selection && !afl->queue_cur))) { ++afl->queue_cycle; - afl->current_entry = 0; + runs_in_current_cycle = 0; afl->cur_skipped_paths = 0; - afl->queue_cur = afl->queue; - if (seek_to) { + if (unlikely(afl->old_seed_selection)) { - afl->current_entry = seek_to; - afl->queue_cur = afl->queue_buf[seek_to]; - seek_to = 0; + afl->current_entry = 0; + afl->queue_cur = afl->queue; - } + if (unlikely(seek_to)) { - // show_stats(afl); + afl->current_entry = seek_to; + afl->queue_cur = afl->queue_buf[seek_to]; + seek_to = 0; + + } + + } if (unlikely(afl->not_on_tty)) { @@ -1366,9 +1385,11 @@ int main(int argc, char **argv_orig, char **envp) { switch (afl->expand_havoc) { case 0: + // this adds extra splicing mutation options to havoc mode afl->expand_havoc = 1; break; case 1: + // add MOpt mutator if (afl->limit_time_sig == 0 && !afl->custom_only && !afl->python_only) { @@ -1381,25 +1402,26 @@ int main(int argc, char **argv_orig, char **envp) { break; case 2: // if (!have_p) afl->schedule = EXPLOIT; + // increase havoc mutations per fuzz attempt afl->havoc_stack_pow2++; afl->expand_havoc = 3; break; case 3: + // further increase havoc mutations per fuzz attempt afl->havoc_stack_pow2++; afl->expand_havoc = 4; break; case 4: + // if not in sync mode, enable deterministic mode? + // if (!afl->sync_dir) afl->skip_deterministic = 0; + afl->expand_havoc = 5; + break; + case 5: // nothing else currently break; } - if (afl->expand_havoc) { - - } else - - afl->expand_havoc = 1; - } else { afl->use_splicing = 1; @@ -1470,6 +1492,22 @@ int main(int argc, char **argv_orig, char **envp) { } + if (likely(!afl->old_seed_selection)) { + + ++runs_in_current_cycle; + if (unlikely(prev_queued_paths < afl->queued_paths)) { + + // we have new queue entries since the last run, recreate alias table + prev_queued_paths = afl->queued_paths; + create_alias_table(afl); + + } + + afl->current_entry = select_next_queue_entry(afl); + afl->queue_cur = afl->queue_buf[afl->current_entry]; + + } + skipped_fuzz = fuzz_one(afl); if (!skipped_fuzz && !afl->stop_soon && afl->sync_id) { @@ -1490,8 +1528,12 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->stop_soon) { break; } - afl->queue_cur = afl->queue_cur->next; - ++afl->current_entry; + if (unlikely(afl->old_seed_selection)) { + + afl->queue_cur = afl->queue_cur->next; + ++afl->current_entry; + + } } -- cgit 1.4.1 From dab017dddaaab6d836a590f7bba3eea3549758d2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 12 Oct 2020 02:26:14 +0200 Subject: no -M/-S: auto-set -S default --- README.md | 20 +++++++++++--------- docs/Changelog.md | 1 + src/afl-fuzz.c | 16 +++++++++++----- 3 files changed, 23 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/README.md b/README.md index 819da093..384ae830 100644 --- a/README.md +++ b/README.md @@ -28,28 +28,30 @@ ## Major changes in afl++ 3.0 With afl++ 3.0 we introduced changes that break some previous afl and afl++ -behaviours: +behaviours and defaults: * There are no llvm_mode and gcc_plugin subdirectories anymore and there is only one compiler: afl-cc. All previous compilers now symlink to this one compiler. All instrumentation source code is now in the `instrumentation/` folder. - * The gcc_plugin was replaced with a new version submitted by AdaCore, that - supports more features, thank you! + * The gcc_plugin was replaced with a new version submitted by AdaCore that + supports more features. thank you! * qemu_mode got upgraded to QEMU 5.1, but to be able to build this a current ninja build tool version and python3 setuptools are required. qemu_mode also got new options like snapshotting, instrumenting specific - shared libraries, etc. and QEMU 5.1 supports more CPU targets so this is - worth it. + shared libraries, etc. Additionally QEMU 5.1 supports more CPU targets so + this is really worth it. * When instrumenting targets, afl-cc will not supersede optimizations. This allows to fuzz targets as same as they are built for debug or release. - * afl-fuzz': - * `-i` option now descends into subdirectories. + * afl-fuzz: + * if neither -M or -S is specified, `-S default` is assumed, so more + fuzzers can easily be added later + * `-i` input directory option now descends into subdirectories. It also + does not fatal on crashes and too large files, instead it skips them + and uses them for splicing mutations * -m none is now default, set memory limits (in MB) with e.g. -m 250 * deterministic fuzzing is now disabled by default (unless using -M) and can be enabled with -D - * afl-fuzz will skip over empty dictionaries and too-large test cases instead - of failing, and use them as a source for splicing mutations ## Contents diff --git a/docs/Changelog.md b/docs/Changelog.md index f15f1d93..36022399 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -23,6 +23,7 @@ sending a mail to . with -M) - statsd support by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) + - not specifying -M or -S will now auto-set "-S default" - reading testcases from -i now descends into subdirectories - allow up to 4 -x command line options - loaded extras now have a duplicate protection diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 004adffe..d42a0d36 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -411,8 +411,8 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } afl->sync_id = ck_strdup(optarg); - afl->skip_deterministic = 0; // force determinsitic fuzzing - afl->old_seed_selection = 1; // force old queue walking seed selection + afl->skip_deterministic = 0; // force determinsitic fuzzing + afl->old_seed_selection = 1; // force old queue walking seed selection if ((c = strchr(afl->sync_id, ':'))) { @@ -847,6 +847,8 @@ int main(int argc, char **argv_orig, char **envp) { "Eißfeldt, Andrea Fioraldi and Dominik Maier"); OKF("afl++ is open source, get it at " "https://github.com/AFLplusplus/AFLplusplus"); + OKF("NOTE: This is v3.x which changes several defaults and behaviours - see " + "README.md"); if (afl->sync_id && afl->is_main_node && afl->afl_env.afl_custom_mutator_only) { @@ -1135,15 +1137,19 @@ int main(int argc, char **argv_orig, char **envp) { WARNF("it is wasteful to run more than one main node!"); sleep(1); - } - - if (afl->is_secondary_node && check_main_node_exists(afl) == 0) { + } else if (afl->is_secondary_node && check_main_node_exists(afl) == 0) { WARNF( "no -M main node found. It is recommended to run exactly one main " "instance."); sleep(1); + } else if (!afl->sync_id) { + + afl->sync_id = "default"; + afl->is_secondary_node = 1; + OKF("no -M/-S set, autoconfiguring for \"-S %s\"", afl->sync_id); + } #ifdef RAND_TEST_VALUES -- cgit 1.4.1 From b7e0490bcdaa7fa792a9dccfa5983e03af92730e Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 12 Oct 2020 03:44:34 +0200 Subject: Revert "Merge branch 'memcache_marc' into dev" This reverts commit c03fbcedaa68db5324423975a34331287426f7c2, reversing changes made to dab017dddaaab6d836a590f7bba3eea3549758d2. --- include/afl-fuzz.h | 16 ------ include/config.h | 9 --- src/afl-fuzz-one.c | 157 +++++++++++++++++++++++++++++++-------------------- src/afl-fuzz-queue.c | 87 ---------------------------- src/afl-fuzz.c | 5 +- 5 files changed, 97 insertions(+), 177 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index a3e87129..45de197d 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -168,9 +168,6 @@ struct queue_entry { double perf_score; /* performance score */ - u8 *testcase_buf; /* The testcase buffer, if loaded. */ - u32 testcase_refs; /* count of users of testcase buf */ - struct queue_entry *next; /* Next element, if any */ }; @@ -689,12 +686,6 @@ typedef struct afl_state { /* queue entries ready for splicing count (len > 4) */ u32 ready_for_splicing_count; - /* How many queue entries currently have cached testcases */ - u32 q_testcase_cache_count; - /* Refs to each queue entry with cached testcase (for eviction, if cache_count - * is too large) */ - struct queue_entry *q_testcase_cache[TESTCASE_CACHE_SIZE]; - } afl_state_t; struct custom_mutator { @@ -1141,12 +1132,5 @@ static inline u64 next_p2(u64 val) { } -/* Returns the testcase buf from the file behind this queue entry. - Increases the refcount. */ -u8 *queue_testcase_take(afl_state_t *afl, struct queue_entry *q); - -/* Tell afl that this testcase may be evicted from the cache */ -void queue_testcase_release(afl_state_t *afl, struct queue_entry *q); - #endif diff --git a/include/config.h b/include/config.h index 3f498275..7dd045e3 100644 --- a/include/config.h +++ b/include/config.h @@ -295,15 +295,6 @@ #define RESEED_RNG 100000 -/* The amount of entries in the testcase cache, held in memory. -Decrease if RAM usage is high. */ -#define TESTCASE_CACHE_SIZE 3072 - -#if TESTCASE_CACHE_SIZE < 4 - #error \ - "Dangerously low cache size: Set TESTCASE_CACHE_SIZE to 4 or more in config.h!" -#endif - /* Maximum line length passed from GCC to 'as' and used for parsing configuration files: */ diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index ebe541a2..6ef728e0 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -370,7 +370,7 @@ static void locate_diffs(u8 *ptr1, u8 *ptr2, u32 len, s32 *first, s32 *last) { u8 fuzz_one_original(afl_state_t *afl) { - s32 len, temp_len; + s32 len, fd, temp_len; u32 j; u32 i; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; @@ -453,9 +453,28 @@ u8 fuzz_one_original(afl_state_t *afl) { } - orig_in = in_buf = queue_testcase_take(afl, afl->queue_cur); + /* Map the test case into memory. */ + + fd = open(afl->queue_cur->fname, O_RDONLY); + + if (unlikely(fd < 0)) { + + PFATAL("Unable to open '%s'", afl->queue_cur->fname); + + } + len = afl->queue_cur->len; + orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + + if (unlikely(orig_in == MAP_FAILED)) { + + PFATAL("Unable to mmap '%s' with len %d", afl->queue_cur->fname, len); + + } + + close(fd); + /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every single byte anyway, so it wouldn't give us any performance or memory usage benefits. */ @@ -1678,7 +1697,7 @@ custom_mutator_stage: for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { - struct queue_entry *target = NULL; + struct queue_entry *target; u32 tid; u8 * new_buf = NULL; u32 target_len = 0; @@ -1701,7 +1720,17 @@ custom_mutator_stage: afl->splicing_with = tid; /* Read the additional testcase into a new buffer. */ - new_buf = queue_testcase_take(afl, target); + fd = open(target->fname, O_RDONLY); + if (unlikely(fd < 0)) { + + PFATAL("Unable to open '%s'", target->fname); + + } + + new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), target->len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } + ck_read(fd, new_buf, target->len, target->fname); + close(fd); target_len = target->len; } @@ -1712,13 +1741,6 @@ custom_mutator_stage: el->afl_custom_fuzz(el->data, out_buf, len, &mutated_buf, new_buf, target_len, max_seed_size); - if (new_buf) { - - queue_testcase_release(afl, target); - new_buf = NULL; - - } - if (unlikely(!mutated_buf)) { FATAL("Error in custom_fuzz. Size returned: %zd", mutated_size); @@ -2301,53 +2323,52 @@ havoc_stage: /* Overwrite bytes with a randomly selected chunk from another testcase or insert that chunk. */ - if (afl->queued_paths < 4) { break; } + if (afl->queued_paths < 4) break; /* Pick a random queue entry and seek to it. */ u32 tid; - do { - + do tid = rand_below(afl, afl->queued_paths); - - } while (tid == afl->current_entry); + while (tid == afl->current_entry); struct queue_entry *target = afl->queue_buf[tid]; /* Make sure that the target has a reasonable length. */ - while (target && (target->len < 2 || target == afl->queue_cur)) { - + while (target && (target->len < 2 || target == afl->queue_cur)) target = target->next; - } + if (!target) break; - if (!target) { break; } + /* Read the testcase into a new buffer. */ - u32 new_len = target->len; + fd = open(target->fname, O_RDONLY); - /* Get the testcase contents for splicing. */ - u8 *new_buf = queue_testcase_take(afl, target); + if (unlikely(fd < 0)) { - u8 overwrite = 0; - if (temp_len >= 2 && rand_below(afl, 2)) { + PFATAL("Unable to open '%s'", target->fname); - overwrite = 1; + } - } else if (temp_len + HAVOC_BLK_XL >= MAX_FILE) { + u32 new_len = target->len; + u8 *new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), new_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } - if (temp_len >= 2) { + ck_read(fd, new_buf, new_len, target->fname); - overwrite = 1; + close(fd); - } else { + u8 overwrite = 0; + if (temp_len >= 2 && rand_below(afl, 2)) + overwrite = 1; + else if (temp_len + HAVOC_BLK_XL >= MAX_FILE) { - queue_testcase_release(afl, target); - new_buf = NULL; + if (temp_len >= 2) + overwrite = 1; + else break; - } - } if (overwrite) { @@ -2393,9 +2414,6 @@ havoc_stage: } - /* We don't need this splice testcase anymore */ - queue_testcase_release(afl, target); - new_buf = NULL; break; } @@ -2501,17 +2519,24 @@ retry_splicing: if (!target) { goto retry_splicing; } - /* Get the testcase buffer */ - u8 *splice_buf = queue_testcase_take(afl, target); + /* Read the testcase into a new buffer. */ + + fd = open(target->fname, O_RDONLY); + + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } + new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), target->len); if (unlikely(!new_buf)) { PFATAL("alloc"); } + ck_read(fd, new_buf, target->len, target->fname); + + close(fd); + /* Find a suitable splicing location, somewhere between the first and the last differing byte. Bail out if the difference is just a single byte or so. */ - locate_diffs(in_buf, splice_buf, MIN(len, (s64)target->len), &f_diff, - &l_diff); + locate_diffs(in_buf, new_buf, MIN(len, (s64)target->len), &f_diff, &l_diff); if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { goto retry_splicing; } @@ -2523,7 +2548,6 @@ retry_splicing: len = target->len; memcpy(new_buf, in_buf, split_at); - memcpy(new_buf + split_at, splice_buf + split_at, target->len - split_at); afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); in_buf = new_buf; @@ -2531,9 +2555,6 @@ retry_splicing: if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(out_buf, in_buf, len); - queue_testcase_release(afl, target); - splice_buf = NULL; - goto custom_mutator_stage; /* ???: While integrating Python module, the author decided to jump to python stage, but the reason behind this is not clear.*/ @@ -2564,8 +2585,7 @@ abandon_entry: ++afl->queue_cur->fuzz_level; - queue_testcase_release(afl, afl->queue_cur); - orig_in = NULL; + munmap(orig_in, afl->queue_cur->len); return ret_val; @@ -2587,7 +2607,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { } - s32 len, temp_len; + s32 len, fd, temp_len; u32 i; u32 j; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; @@ -2652,9 +2672,23 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { } /* Map the test case into memory. */ - orig_in = in_buf = queue_testcase_take(afl, afl->queue_cur); + + fd = open(afl->queue_cur->fname, O_RDONLY); + + if (fd < 0) { PFATAL("Unable to open '%s'", afl->queue_cur->fname); } + len = afl->queue_cur->len; + orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + + if (orig_in == MAP_FAILED) { + + PFATAL("Unable to mmap '%s'", afl->queue_cur->fname); + + } + + close(fd); + /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every single byte anyway, so it wouldn't give us any performance or memory usage benefits. */ @@ -4494,24 +4528,31 @@ pacemaker_fuzzing: if (!target) { goto retry_splicing_puppet; } /* Read the testcase into a new buffer. */ - u8 *splicing_buf = queue_testcase_take(afl, target); + + fd = open(target->fname, O_RDONLY); + + if (fd < 0) { PFATAL("Unable to open '%s'", target->fname); } + + new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), target->len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } + + ck_read(fd, new_buf, target->len, target->fname); + + close(fd); /* Find a suitable splicin g location, somewhere between the first and the last differing byte. Bail out if the difference is just a single byte or so. */ - locate_diffs(in_buf, splicing_buf, MIN(len, (s32)target->len), &f_diff, + locate_diffs(in_buf, new_buf, MIN(len, (s32)target->len), &f_diff, &l_diff); if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { - queue_testcase_release(afl, target); goto retry_splicing_puppet; } - new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), target->len); - /* Split somewhere between the first and last differing byte. */ split_at = f_diff + rand_below(afl, l_diff - f_diff); @@ -4520,17 +4561,12 @@ pacemaker_fuzzing: len = target->len; memcpy(new_buf, in_buf, split_at); - memcpy(new_buf + split_at, splicing_buf + split_at, - target->len - split_at); afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); in_buf = new_buf; out_buf = afl_realloc(AFL_BUF_PARAM(out), len); if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(out_buf, in_buf, len); - queue_testcase_release(afl, target); - splicing_buf = NULL; - goto havoc_stage_puppet; } /* if splice_cycle */ @@ -4564,8 +4600,7 @@ pacemaker_fuzzing: // if (afl->queue_cur->favored) --afl->pending_favored; // } - queue_testcase_release(afl, afl->queue_cur); - orig_in = NULL; + munmap(orig_in, afl->queue_cur->len); if (afl->key_puppet == 1) { diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index a034b168..d608e890 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -343,7 +343,6 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { q->depth = afl->cur_depth + 1; q->passed_det = passed_det; q->trace_mini = NULL; - q->testcase_buf = NULL; if (q->depth > afl->max_depth) { afl->max_depth = q->depth; } @@ -892,89 +891,3 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { } -/* Tell afl that this testcase may be evicted from the cache */ -inline void queue_testcase_release(afl_state_t *afl, struct queue_entry *q) { - - (void)afl; - if (unlikely(q->testcase_refs == 0)) { - - FATAL("Testcase refcount reduced past 0"); - - } - - q->testcase_refs--; - -} - -/* Returns the testcase buf from the file behind this queue entry. - Increases the refcount. */ -u8 *queue_testcase_take(afl_state_t *afl, struct queue_entry *q) { - - if (!q->testcase_buf) { - - u32 tid = 0; - /* Buf not cached, let's do that now */ - - if (likely(afl->q_testcase_cache_count == TESTCASE_CACHE_SIZE)) { - - /* Cache full. We neet to evict one to map one. - Get a random one which is not in use */ - do { - - tid = rand_below(afl, afl->q_testcase_cache_count); - - } while (afl->q_testcase_cache[tid]->testcase_refs > 0); - - struct queue_entry *old_cached = afl->q_testcase_cache[tid]; - /* free the current buf from cache */ - munmap(old_cached->testcase_buf, old_cached->len); - old_cached->testcase_buf = NULL; - - } else { - - tid = afl->q_testcase_cache_count; - afl->q_testcase_cache_count++; - - } - - /* Map the test case into memory. */ - - int fd = open(q->fname, O_RDONLY); - - if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", q->fname); } - - u32 len = q->len; - - q->testcase_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - - if (unlikely(q->testcase_buf == MAP_FAILED)) { - - PFATAL("Unable to mmap '%s' with len %d", q->fname, len); - - } - - close(fd); - - /* Register us as cached */ - afl->q_testcase_cache[tid] = q; - - } - - q->testcase_refs++; - if (unlikely(!q->testcase_buf || !q->testcase_refs)) { - if (!q->testcase_buf) { - - FATAL("Testcase buf is NULL, this should never happen"); - - } - if (!q->testcase_refs) { - - FATAL("Testcase ref overflow. Missing a testcase release somwhere?"); - - } - } - - return q->testcase_buf; - -} - diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index cb5eb37a..d42a0d36 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1174,12 +1174,9 @@ int main(int argc, char **argv_orig, char **envp) { if (extras_dir_cnt) { - for (i = 0; i < extras_dir_cnt; i++) { - + for (i = 0; i < extras_dir_cnt; i++) load_extras(afl, extras_dir[i]); - } - dedup_extras(afl); OKF("Loaded a total of %u extras.", afl->extras_cnt); -- cgit 1.4.1 From d6da5605c80d65091375c08ae5389d14d671500a Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 12 Oct 2020 04:03:42 +0200 Subject: fix splicing selection --- src/afl-fuzz-one.c | 47 ++++++++--------------------------------------- 1 file changed, 8 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 6ef728e0..fc092f8d 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1712,9 +1712,9 @@ custom_mutator_stage: tid = rand_below(afl, afl->queued_paths); - } while (unlikely(tid == afl->current_entry && + } while (unlikely(tid == afl->current_entry || - afl->queue_buf[tid]->len >= 4)); + afl->queue_buf[tid]->len < 4)); target = afl->queue_buf[tid]; afl->splicing_with = tid; @@ -1872,7 +1872,7 @@ havoc_stage: u32 r_max, r; - if (unlikely(afl->expand_havoc)) { + if (unlikely(afl->expand_havoc && afl->ready_for_splicing_count > 1)) { /* add expensive havoc cases here, they are activated after a full cycle without finds happened */ @@ -2323,24 +2323,15 @@ havoc_stage: /* Overwrite bytes with a randomly selected chunk from another testcase or insert that chunk. */ - if (afl->queued_paths < 4) break; - /* Pick a random queue entry and seek to it. */ u32 tid; do tid = rand_below(afl, afl->queued_paths); - while (tid == afl->current_entry); + while (tid == afl->current_entry || afl->queue_buf[tid]->len < 4); struct queue_entry *target = afl->queue_buf[tid]; - /* Make sure that the target has a reasonable length. */ - - while (target && (target->len < 2 || target == afl->queue_cur)) - target = target->next; - - if (!target) break; - /* Read the testcase into a new buffer. */ fd = open(target->fname, O_RDONLY); @@ -2480,7 +2471,7 @@ havoc_stage: retry_splicing: if (afl->use_splicing && splice_cycle++ < SPLICE_CYCLES && - afl->queued_paths > 1 && afl->queue_cur->len > 1) { + afl->ready_for_splicing_count > 1 && afl->queue_cur->len >= 4) { struct queue_entry *target; u32 tid, split_at; @@ -2503,22 +2494,11 @@ retry_splicing: tid = rand_below(afl, afl->queued_paths); - } while (tid == afl->current_entry); + } while (tid == afl->current_entry || afl->queue_buf[tid]->len < 4); afl->splicing_with = tid; target = afl->queue_buf[tid]; - /* Make sure that the target has a reasonable length. */ - - while (target && (target->len < 2 || target == afl->queue_cur)) { - - target = target->next; - ++afl->splicing_with; - - } - - if (!target) { goto retry_splicing; } - /* Read the testcase into a new buffer. */ fd = open(target->fname, O_RDONLY); @@ -4487,7 +4467,7 @@ pacemaker_fuzzing: if (afl->use_splicing && splice_cycle++ < (u32)afl->SPLICE_CYCLES_puppet && - afl->queued_paths > 1 && afl->queue_cur->len > 1) { + afl->ready_for_splicing_count > 1 && afl->queue_cur->len >= 4) { struct queue_entry *target; u32 tid, split_at; @@ -4511,22 +4491,11 @@ pacemaker_fuzzing: tid = rand_below(afl, afl->queued_paths); - } while (tid == afl->current_entry); + } while (tid == afl->current_entry || afl->queue_buf[tid]->len < 4); afl->splicing_with = tid; target = afl->queue_buf[tid]; - /* Make sure that the target has a reasonable length. */ - - while (target && (target->len < 2 || target == afl->queue_cur)) { - - target = target->next; - ++afl->splicing_with; - - } - - if (!target) { goto retry_splicing_puppet; } - /* Read the testcase into a new buffer. */ fd = open(target->fname, O_RDONLY); -- cgit 1.4.1 From aef0cd5877e0e4d3450610d48c4f6188a114d2b3 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 12 Oct 2020 04:41:04 +0200 Subject: fix sync mode --- src/afl-fuzz.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index d42a0d36..e58d3d34 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -249,7 +249,7 @@ static int stricmp(char const *a, char const *b) { int main(int argc, char **argv_orig, char **envp) { - s32 opt, i; + s32 opt, i, auto_sync = 0; u64 prev_queued = 0; u32 sync_interval_cnt = 0, seek_to = 0, show_help = 0, map_size = MAP_SIZE; u8 *extras_dir[4]; @@ -847,7 +847,7 @@ int main(int argc, char **argv_orig, char **envp) { "Eißfeldt, Andrea Fioraldi and Dominik Maier"); OKF("afl++ is open source, get it at " "https://github.com/AFLplusplus/AFLplusplus"); - OKF("NOTE: This is v3.x which changes several defaults and behaviours - see " + OKF("NOTE: This is v3.x which changes defaults and behaviours - see " "README.md"); if (afl->sync_id && afl->is_main_node && @@ -880,6 +880,15 @@ int main(int argc, char **argv_orig, char **envp) { afl->power_name = power_names[afl->schedule]; + if (!afl->sync_id) { + + auto_sync = 1; + afl->sync_id = ck_strdup("default"); + afl->is_secondary_node = 1; + OKF("no -M/-S set, autoconfiguring for \"-S %s\"", afl->sync_id); + + } + if (afl->sync_id) { fix_up_sync(afl); } if (!strcmp(afl->in_dir, afl->out_dir)) { @@ -1137,19 +1146,15 @@ int main(int argc, char **argv_orig, char **envp) { WARNF("it is wasteful to run more than one main node!"); sleep(1); - } else if (afl->is_secondary_node && check_main_node_exists(afl) == 0) { + } else if (!auto_sync && afl->is_secondary_node && + + check_main_node_exists(afl) == 0) { WARNF( "no -M main node found. It is recommended to run exactly one main " "instance."); sleep(1); - } else if (!afl->sync_id) { - - afl->sync_id = "default"; - afl->is_secondary_node = 1; - OKF("no -M/-S set, autoconfiguring for \"-S %s\"", afl->sync_id); - } #ifdef RAND_TEST_VALUES @@ -1419,7 +1424,7 @@ int main(int argc, char **argv_orig, char **envp) { break; case 4: // if not in sync mode, enable deterministic mode? - // if (!afl->sync_dir) afl->skip_deterministic = 0; + // if (!afl->sync_id) afl->skip_deterministic = 0; afl->expand_havoc = 5; break; case 5: -- cgit 1.4.1 From 15099f7f5a129dd3679df646ed2b50a3196188f1 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 12 Oct 2020 04:48:02 +0200 Subject: fix afl-cc.8 generation --- GNUmakefile.gcc_plugin | 1 - GNUmakefile.llvm | 5 ++--- src/afl-fuzz.c | 5 ++++- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/GNUmakefile.gcc_plugin b/GNUmakefile.gcc_plugin index e3108511..d139387a 100644 --- a/GNUmakefile.gcc_plugin +++ b/GNUmakefile.gcc_plugin @@ -146,7 +146,6 @@ all_done: test_build .NOTPARALLEL: clean -vpath % .. %.8: % @echo .TH $* 8 `date "+%Y-%m-%d"` "afl++" > ./$@ @echo .SH NAME >> ./$@ diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index e0dc793e..1f67ea7f 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -472,12 +472,11 @@ install: all set -e; install -m 644 ./dynamic_list.txt $${DESTDIR}$(HELPER_PATH) install -m 644 instrumentation/README.*.md $${DESTDIR}$(DOC_PATH)/ -vpath % .. %.8: % @echo .TH $* 8 $(BUILD_DATE) "afl++" > ./$@ @echo .SH NAME >> ./$@ - @printf "%s" ".B $* \- " >> ../$@ - @./$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> ../$@ + @printf "%s" ".B $* \- " >> ./$@ + @./$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> ./$@ @echo .B $* >> ./$@ @echo >> ./$@ @echo .SH SYNOPSIS >> ./$@ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e58d3d34..6498eb30 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1179,9 +1179,12 @@ int main(int argc, char **argv_orig, char **envp) { if (extras_dir_cnt) { - for (i = 0; i < extras_dir_cnt; i++) + for (i = 0; i < extras_dir_cnt; i++) { + load_extras(afl, extras_dir[i]); + } + dedup_extras(afl); OKF("Loaded a total of %u extras.", afl->extras_cnt); -- cgit 1.4.1 From d9b63766dfdb8feeb1dc6f7c51c17abf07ee4086 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 12 Oct 2020 11:12:16 +0200 Subject: fix new seed selection algo --- include/afl-fuzz.h | 3 +++ src/afl-fuzz-queue.c | 59 ++++++++++++--------------------------------------- src/afl-performance.c | 10 ++++++++- 3 files changed, 26 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 45de197d..85597150 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1045,6 +1045,9 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, /* xoshiro256** */ uint64_t rand_next(afl_state_t *afl); +/* probability between 0.0 and 1.0 */ +double rand_next_percent(afl_state_t *afl); + /**** Inline routines ****/ /* Generate a random number (from 0 to limit - 1). This may diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index d608e890..f224d851 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -27,17 +27,22 @@ #include #include +/* select next queue entry based on alias algo - fast! */ + inline u32 select_next_queue_entry(afl_state_t *afl) { - u32 r = rand_below(afl, 0xffffffff); - u32 s = r % afl->queued_paths; - // fprintf(stderr, "select: r=%u s=%u ... r < prob[s]=%f ? s=%u : - // alias[%u]=%u\n", r, s, afl->alias_probability[s], s, s, - // afl->alias_table[s]); - return (r < afl->alias_probability[s] ? s : afl->alias_table[s]); + u32 s = rand_below(afl, afl->queued_paths); + double p = rand_next_percent(afl); + /* + fprintf(stderr, "select: p=%f s=%u ... p < prob[s]=%f ? s=%u : alias[%u]=%u" + " ==> %u\n", p, s, afl->alias_probability[s], s, s, afl->alias_table[s], p < afl->alias_probability[s] ? s : afl->alias_table[s]); + */ + return (p < afl->alias_probability[s] ? s : afl->alias_table[s]); } +/* create the alias table that allows weighted random selection - expensive */ + void create_alias_table(afl_state_t *afl) { u32 n = afl->queued_paths, i = 0, a, g; @@ -63,11 +68,6 @@ void create_alias_table(afl_state_t *afl) { if (!q->disabled) q->perf_score = calculate_score(afl, q); sum += q->perf_score; - /* - if (afl->debug) - fprintf(stderr, "entry %u: score=%f %s (sum: %f)\n", i, q->perf_score, - q->disabled ? "disabled" : "", sum); - */ } @@ -110,41 +110,10 @@ void create_alias_table(afl_state_t *afl) { afl->alias_probability[S[--nS]] = 1; /* - if (afl->debug) { - - fprintf(stderr, " %-3s %-3s %-9s\n", "entry", "alias", "prob"); + fprintf(stderr, " %-3s %-3s %-9s %-9s\n", "entry", "alias", "prob", "perf"); for (u32 i = 0; i < n; ++i) - fprintf(stderr, " %3i %3i %9.7f\n", i, afl->alias_table[i], - afl->alias_probability[i]); - - } - - int prob = 0; - fprintf(stderr, "Alias:"); - for (i = 0; i < n; i++) { - - fprintf(stderr, " [%u]=%u", i, afl->alias_table[i]); - if (afl->alias_table[i] >= n) - prob = i; - - } - - fprintf(stderr, "\n"); - - if (prob) { - - fprintf(stderr, "PROBLEM! alias[%u] = %u\n", prob, - afl->alias_table[prob]); - - for (i = 0; i < n; i++) { - - struct queue_entry *q = afl->queue_buf[i]; - - fprintf(stderr, "%u: score=%f\n", i, q->perf_score); - - } - - } + fprintf(stderr, " %3i %3i %9.7f %9.7f\n", i, afl->alias_table[i], + afl->alias_probability[i], afl->queue_buf[i]->perf_score); */ diff --git a/src/afl-performance.c b/src/afl-performance.c index 7a80ac4b..6fa95dea 100644 --- a/src/afl-performance.c +++ b/src/afl-performance.c @@ -47,7 +47,7 @@ void rand_set_seed(afl_state_t *afl, s64 init_seed) { } -uint64_t rand_next(afl_state_t *afl) { +inline uint64_t rand_next(afl_state_t *afl) { const uint64_t result = rotl(afl->rand_seed[0] + afl->rand_seed[3], 23) + afl->rand_seed[0]; @@ -67,6 +67,14 @@ uint64_t rand_next(afl_state_t *afl) { } +/* returns a double between 0.000000000 and 1.000000000 */ + +inline double rand_next_percent(afl_state_t *afl) { + + return (double)(((double)rand_next(afl)) / (double) 0xffffffffffffffff); + +} + /* This is the jump function for the generator. It is equivalent to 2^128 calls to rand_next(); it can be used to generate 2^128 non-overlapping subsequences for parallel computations. */ -- cgit 1.4.1 From 56ac3fcdc511d124ad058412021ead21bbbcf4bf Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 14 Oct 2020 15:30:30 +0200 Subject: configurable testcache with malloc (#581) * cache item number to cache memory size * reload testcase if trimming changed the size * fix splicing selection * slim splicing * import sync fix * write testcache stats to fuzzer_stats * fix new seed selection algo * malloc+read instead of mmap * fix * testcache is configurable now and no reference counts * fixes compilation, test script * fixes * switch TEST_CC to afl-cc in makefile * code format * fix * fix crash * fix crash * fix env help output * remove unnecessary pointer resets * fix endless loop bug * actually use the cache if set * one more fix * increase default cache entries, add default cache size value to config.h Co-authored-by: hexcoder- --- GNUmakefile | 4 +- include/afl-fuzz.h | 31 +++++++- include/config.h | 9 +++ include/envs.h | 1 + src/afl-fuzz-init.c | 4 +- src/afl-fuzz-one.c | 169 +++++++++---------------------------------- src/afl-fuzz-queue.c | 167 ++++++++++++++++++++++++++++++++++++++---- src/afl-fuzz-run.c | 6 ++ src/afl-fuzz-state.c | 8 ++ src/afl-fuzz-stats.c | 7 +- src/afl-fuzz.c | 29 ++++++-- src/afl-performance.c | 2 +- test/test-custom-mutators.sh | 2 +- 13 files changed, 276 insertions(+), 163 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index c885a935..80b7b68b 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -223,8 +223,6 @@ ifneq "$(findstring OpenBSD, $(shell uname))" "" LDFLAGS += -lpthread endif -TEST_CC = afl-gcc - COMM_HDR = include/alloc-inl.h include/config.h include/debug.h include/types.h ifeq "$(shell echo '$(HASH)include @int main() {return 0; }' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test $(PYTHON_INCLUDE) $(LDFLAGS) $(PYTHON_LIB) 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" @@ -488,7 +486,7 @@ code-format: ifndef AFL_NO_X86 test_build: afl-cc afl-as afl-showmap @echo "[*] Testing the CC wrapper and instrumentation output..." - @unset AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_DEBUG=1 AFL_INST_RATIO=100 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS) 2>&1 | grep 'afl-as' >/dev/null || (echo "Oops, afl-as did not get called from "$(TEST_CC)". This is normally achieved by "$(CC)" honoring the -B option."; exit 1 ) + @unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_INST_RATIO=100 AFL_PATH=. ./afl-cc $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS) 2>&1 || (echo "Oops, afl-cc failed"; exit 1 ) ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr @rm -f test-instr diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 85597150..940c5602 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -168,6 +168,8 @@ struct queue_entry { double perf_score; /* performance score */ + u8 *testcase_buf; /* The testcase buffer, if loaded. */ + struct queue_entry *next; /* Next element, if any */ }; @@ -363,7 +365,7 @@ typedef struct afl_env_vars { u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload, *afl_max_det_extras, *afl_statsd_host, *afl_statsd_port, - *afl_statsd_tags_flavor; + *afl_statsd_tags_flavor, *afl_testcache_size; } afl_env_vars_t; @@ -675,6 +677,9 @@ typedef struct afl_state { u8 *in_scratch_buf; u8 *ex_buf; + + u8 *testcase_buf, *splicecase_buf; + u32 custom_mutators_count; list_t custom_mutator_list; @@ -686,6 +691,22 @@ typedef struct afl_state { /* queue entries ready for splicing count (len > 4) */ u32 ready_for_splicing_count; + /* This is the user specified maximum size to use for the testcase cache */ + u64 q_testcase_max_cache_size; + + /* How much of the testcase cache is used so far */ + u64 q_testcase_cache_size; + + /* highest cache count so far */ + u32 q_testcase_max_cache_count; + + /* How many queue entries currently have cached testcases */ + u32 q_testcase_cache_count; + + /* Refs to each queue entry with cached testcase (for eviction, if cache_count + * is too large) */ + struct queue_entry *q_testcase_cache[TESTCASE_ENTRIES]; + } afl_state_t; struct custom_mutator { @@ -1135,5 +1156,13 @@ static inline u64 next_p2(u64 val) { } +/* Returns the testcase buf from the file behind this queue entry. + Increases the refcount. */ +u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q); + +/* If trimming changes the testcase size we have to reload it */ +void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, + u32 old_len); + #endif diff --git a/include/config.h b/include/config.h index 7dd045e3..b4f3a775 100644 --- a/include/config.h +++ b/include/config.h @@ -295,6 +295,15 @@ #define RESEED_RNG 100000 +/* The maximum number of testcases to cache */ + +#define TESTCASE_ENTRIES 16384 + +/* The default maximum testcase cache size in MB, 0 = disable. + A value between 50 and 250 is a good default value. */ + +#define TESTCASE_CACHE 0 + /* Maximum line length passed from GCC to 'as' and used for parsing configuration files: */ diff --git a/include/envs.h b/include/envs.h index 51520312..a1b3ad12 100644 --- a/include/envs.h +++ b/include/envs.h @@ -139,6 +139,7 @@ static char *afl_environment_variables[] = { "AFL_STATSD_HOST", "AFL_STATSD_PORT", "AFL_STATSD_TAGS_FLAVOR", + "AFL_TESTCACHE_SIZE", "AFL_TMIN_EXACT", "AFL_TMPDIR", "AFL_TOKEN_FILE", diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 881bf10f..607b652f 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -1045,7 +1045,7 @@ restart_outer_cull_loop: while (q) { - if (q->cal_failed || !q->exec_cksum) continue; + if (q->cal_failed || !q->exec_cksum) { goto next_entry; } restart_inner_cull_loop: @@ -1090,6 +1090,8 @@ restart_outer_cull_loop: } + next_entry: + prev = q; q = q->next; diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index fc092f8d..154e4b45 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -370,7 +370,7 @@ static void locate_diffs(u8 *ptr1, u8 *ptr2, u32 len, s32 *first, s32 *last) { u8 fuzz_one_original(afl_state_t *afl) { - s32 len, fd, temp_len; + s32 len, temp_len; u32 j; u32 i; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; @@ -453,32 +453,9 @@ u8 fuzz_one_original(afl_state_t *afl) { } - /* Map the test case into memory. */ - - fd = open(afl->queue_cur->fname, O_RDONLY); - - if (unlikely(fd < 0)) { - - PFATAL("Unable to open '%s'", afl->queue_cur->fname); - - } - + orig_in = in_buf = queue_testcase_get(afl, afl->queue_cur); len = afl->queue_cur->len; - orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - - if (unlikely(orig_in == MAP_FAILED)) { - - PFATAL("Unable to mmap '%s' with len %d", afl->queue_cur->fname, len); - - } - - close(fd); - - /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every - single byte anyway, so it wouldn't give us any performance or memory usage - benefits. */ - out_buf = afl_realloc(AFL_BUF_PARAM(out), len); if (unlikely(!out_buf)) { PFATAL("alloc"); } @@ -526,6 +503,7 @@ u8 fuzz_one_original(afl_state_t *afl) { !afl->disable_trim)) { u8 res = trim_case(afl, afl->queue_cur, in_buf); + orig_in = in_buf = queue_testcase_get(afl, afl->queue_cur); if (unlikely(res == FSRV_RUN_ERROR)) { @@ -1720,17 +1698,7 @@ custom_mutator_stage: afl->splicing_with = tid; /* Read the additional testcase into a new buffer. */ - fd = open(target->fname, O_RDONLY); - if (unlikely(fd < 0)) { - - PFATAL("Unable to open '%s'", target->fname); - - } - - new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), target->len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - ck_read(fd, new_buf, target->len, target->fname); - close(fd); + new_buf = queue_testcase_get(afl, target); target_len = target->len; } @@ -2182,7 +2150,6 @@ havoc_stage: afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); out_buf = new_buf; - new_buf = NULL; temp_len += clone_len; } @@ -2326,43 +2293,21 @@ havoc_stage: /* Pick a random queue entry and seek to it. */ u32 tid; - do - tid = rand_below(afl, afl->queued_paths); - while (tid == afl->current_entry || afl->queue_buf[tid]->len < 4); - - struct queue_entry *target = afl->queue_buf[tid]; - - /* Read the testcase into a new buffer. */ - - fd = open(target->fname, O_RDONLY); - - if (unlikely(fd < 0)) { - - PFATAL("Unable to open '%s'", target->fname); - - } - - u32 new_len = target->len; - u8 *new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), new_len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - - ck_read(fd, new_buf, new_len, target->fname); + do { - close(fd); + tid = rand_below(afl, afl->queued_paths); - u8 overwrite = 0; - if (temp_len >= 2 && rand_below(afl, 2)) - overwrite = 1; - else if (temp_len + HAVOC_BLK_XL >= MAX_FILE) { + } while (tid == afl->current_entry || afl->queue_buf[tid]->len < 4); - if (temp_len >= 2) - overwrite = 1; - else - break; + /* Get the testcase for splicing. */ + struct queue_entry *target = afl->queue_buf[tid]; + u32 new_len = target->len; + u8 * new_buf = queue_testcase_get(afl, target); - } + if ((temp_len >= 2 && rand_below(afl, 2)) || + temp_len + HAVOC_BLK_XL >= MAX_FILE) { - if (overwrite) { + /* overwrite mode */ u32 copy_from, copy_to, copy_len; @@ -2376,15 +2321,16 @@ havoc_stage: } else { + /* insert mode */ + u32 clone_from, clone_to, clone_len; clone_len = choose_block_len(afl, new_len); clone_from = rand_below(afl, new_len - clone_len + 1); + clone_to = rand_below(afl, temp_len + 1); - clone_to = rand_below(afl, temp_len); - - u8 *temp_buf = - afl_realloc(AFL_BUF_PARAM(out_scratch), temp_len + clone_len); + u8 *temp_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), + temp_len + clone_len + 1); if (unlikely(!temp_buf)) { PFATAL("alloc"); } /* Head */ @@ -2496,21 +2442,10 @@ retry_splicing: } while (tid == afl->current_entry || afl->queue_buf[tid]->len < 4); + /* Get the testcase */ afl->splicing_with = tid; target = afl->queue_buf[tid]; - - /* Read the testcase into a new buffer. */ - - fd = open(target->fname, O_RDONLY); - - if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } - - new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), target->len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - - ck_read(fd, new_buf, target->len, target->fname); - - close(fd); + new_buf = queue_testcase_get(afl, target); /* Find a suitable splicing location, somewhere between the first and the last differing byte. Bail out if the difference is just a single @@ -2527,18 +2462,16 @@ retry_splicing: /* Do the thing. */ len = target->len; - memcpy(new_buf, in_buf, split_at); - afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); - in_buf = new_buf; + afl->in_scratch_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len); + memcpy(afl->in_scratch_buf, in_buf, split_at); + memcpy(afl->in_scratch_buf + split_at, new_buf, len - split_at); + in_buf = afl->in_scratch_buf; out_buf = afl_realloc(AFL_BUF_PARAM(out), len); if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(out_buf, in_buf, len); goto custom_mutator_stage; - /* ???: While integrating Python module, the author decided to jump to - python stage, but the reason behind this is not clear.*/ - // goto havoc_stage; } @@ -2564,9 +2497,7 @@ abandon_entry: } ++afl->queue_cur->fuzz_level; - - munmap(orig_in, afl->queue_cur->len); - + orig_in = NULL; return ret_val; #undef FLIP_BIT @@ -2587,7 +2518,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { } - s32 len, fd, temp_len; + s32 len, temp_len; u32 i; u32 j; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; @@ -2652,32 +2583,11 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { } /* Map the test case into memory. */ - - fd = open(afl->queue_cur->fname, O_RDONLY); - - if (fd < 0) { PFATAL("Unable to open '%s'", afl->queue_cur->fname); } - + orig_in = in_buf = queue_testcase_get(afl, afl->queue_cur); len = afl->queue_cur->len; - - orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - - if (orig_in == MAP_FAILED) { - - PFATAL("Unable to mmap '%s'", afl->queue_cur->fname); - - } - - close(fd); - - /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every - single byte anyway, so it wouldn't give us any performance or memory usage - benefits. */ - out_buf = afl_realloc(AFL_BUF_PARAM(out), len); if (unlikely(!out_buf)) { PFATAL("alloc"); } - afl->subseq_tmouts = 0; - afl->cur_depth = afl->queue_cur->depth; /******************************************* @@ -2721,6 +2631,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { u32 old_len = afl->queue_cur->len; u8 res = trim_case(afl, afl->queue_cur, in_buf); + orig_in = in_buf = queue_testcase_get(afl, afl->queue_cur); if (res == FSRV_RUN_ERROR) { @@ -4497,17 +4408,7 @@ pacemaker_fuzzing: target = afl->queue_buf[tid]; /* Read the testcase into a new buffer. */ - - fd = open(target->fname, O_RDONLY); - - if (fd < 0) { PFATAL("Unable to open '%s'", target->fname); } - - new_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), target->len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - - ck_read(fd, new_buf, target->len, target->fname); - - close(fd); + new_buf = queue_testcase_get(afl, target); /* Find a suitable splicin g location, somewhere between the first and the last differing byte. Bail out if the difference is just a single @@ -4529,9 +4430,11 @@ pacemaker_fuzzing: /* Do the thing. */ len = target->len; - memcpy(new_buf, in_buf, split_at); - afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); - in_buf = new_buf; + afl->in_scratch_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len); + memcpy(afl->in_scratch_buf, in_buf, split_at); + memcpy(afl->in_scratch_buf + split_at, new_buf, len - split_at); + in_buf = afl->in_scratch_buf; + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); if (unlikely(!out_buf)) { PFATAL("alloc"); } memcpy(out_buf, in_buf, len); @@ -4569,7 +4472,7 @@ pacemaker_fuzzing: // if (afl->queue_cur->favored) --afl->pending_favored; // } - munmap(orig_in, afl->queue_cur->len); + orig_in = NULL; if (afl->key_puppet == 1) { diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index f224d851..c634328f 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -31,11 +31,12 @@ inline u32 select_next_queue_entry(afl_state_t *afl) { - u32 s = rand_below(afl, afl->queued_paths); + u32 s = rand_below(afl, afl->queued_paths); double p = rand_next_percent(afl); /* fprintf(stderr, "select: p=%f s=%u ... p < prob[s]=%f ? s=%u : alias[%u]=%u" - " ==> %u\n", p, s, afl->alias_probability[s], s, s, afl->alias_table[s], p < afl->alias_probability[s] ? s : afl->alias_table[s]); + " ==> %u\n", p, s, afl->alias_probability[s], s, s, afl->alias_table[s], p < + afl->alias_probability[s] ? s : afl->alias_table[s]); */ return (p < afl->alias_probability[s] ? s : afl->alias_table[s]); @@ -55,7 +56,7 @@ void create_alias_table(afl_state_t *afl) { int * S = (u32 *)afl_realloc(AFL_BUF_PARAM(out_scratch), n * sizeof(u32)); int * L = (u32 *)afl_realloc(AFL_BUF_PARAM(in_scratch), n * sizeof(u32)); - if (!P || !S || !L) FATAL("could not aquire memory for alias table"); + if (!P || !S || !L) { FATAL("could not aquire memory for alias table"); } memset((void *)afl->alias_table, 0, n * sizeof(u32)); memset((void *)afl->alias_probability, 0, n * sizeof(double)); @@ -65,7 +66,7 @@ void create_alias_table(afl_state_t *afl) { struct queue_entry *q = afl->queue_buf[i]; - if (!q->disabled) q->perf_score = calculate_score(afl, q); + if (!q->disabled) { q->perf_score = calculate_score(afl, q); } sum += q->perf_score; @@ -74,19 +75,23 @@ void create_alias_table(afl_state_t *afl) { for (i = 0; i < n; i++) { struct queue_entry *q = afl->queue_buf[i]; - - P[i] = q->perf_score * n / sum; + P[i] = (q->perf_score * n) / sum; } int nS = 0, nL = 0, s; for (s = (s32)n - 1; s >= 0; --s) { - if (P[s] < 1) + if (P[s] < 1) { + S[nS++] = s; - else + + } else { + L[nL++] = s; + } + } while (nS && nL) { @@ -96,11 +101,16 @@ void create_alias_table(afl_state_t *afl) { afl->alias_probability[a] = P[a]; afl->alias_table[a] = g; P[g] = P[g] + P[a] - 1; - if (P[g] < 1) + if (P[g] < 1) { + S[nS++] = g; - else + + } else { + L[nL++] = g; + } + } while (nL) @@ -110,11 +120,10 @@ void create_alias_table(afl_state_t *afl) { afl->alias_probability[S[--nS]] = 1; /* - fprintf(stderr, " %-3s %-3s %-9s %-9s\n", "entry", "alias", "prob", "perf"); - for (u32 i = 0; i < n; ++i) - fprintf(stderr, " %3i %3i %9.7f %9.7f\n", i, afl->alias_table[i], - afl->alias_probability[i], afl->queue_buf[i]->perf_score); - + fprintf(stderr, " entry alias probability perf_score\n"); + for (u32 i = 0; i < n; ++i) + fprintf(stderr, " %5u %5u %11u %0.9f\n", i, afl->alias_table[i], + afl->alias_probability[i], afl->queue_buf[i]->perf_score); */ } @@ -860,3 +869,131 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { } +void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, + u32 old_len) { + + if (likely(q->testcase_buf)) { + + free(q->testcase_buf); + int fd = open(q->fname, O_RDONLY); + + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", q->fname); } + + u32 len = q->len; + q->testcase_buf = malloc(len); + + if (unlikely(!q->testcase_buf)) { + + PFATAL("Unable to mmap '%s' with len %d", q->fname, len); + + } + + close(fd); + afl->q_testcase_cache_size = afl->q_testcase_cache_size + q->len - old_len; + + } + +} + +/* Returns the testcase buf from the file behind this queue entry. + Increases the refcount. */ +inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { + + u32 len = q->len; + + /* first handle if no testcase cache is configured */ + + if (unlikely(!afl->q_testcase_max_cache_size)) { + + u8 *buf; + + if (q == afl->queue_cur) { + + buf = afl_realloc((void **)&afl->testcase_buf, len); + + } else { + + buf = afl_realloc((void **)&afl->splicecase_buf, len); + + } + + if (unlikely(!buf)) { + + PFATAL("Unable to malloc '%s' with len %u", q->fname, len); + + } + + int fd = open(q->fname, O_RDONLY); + + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", q->fname); } + + ck_read(fd, buf, len, q->fname); + close(fd); + return buf; + + } + + /* now handle the testcase cache */ + + if (unlikely(!q->testcase_buf)) { + + /* Buf not cached, let's load it */ + u32 tid = 0; + + while (unlikely(afl->q_testcase_cache_size + len >= + afl->q_testcase_max_cache_size || + afl->q_testcase_cache_count >= TESTCASE_ENTRIES - 1)) { + + /* Cache full. We neet to evict one to map one. + Get a random one which is not in use */ + + do { + + tid = rand_below(afl, afl->q_testcase_max_cache_count); + + } while (afl->q_testcase_cache[tid] == NULL || + + afl->q_testcase_cache[tid] == afl->queue_cur); + + struct queue_entry *old_cached = afl->q_testcase_cache[tid]; + free(old_cached->testcase_buf); + old_cached->testcase_buf = NULL; + afl->q_testcase_cache_size -= old_cached->len; + afl->q_testcase_cache[tid] = NULL; + --afl->q_testcase_cache_count; + + } + + while (likely(afl->q_testcase_cache[tid] != NULL)) + ++tid; + + /* Map the test case into memory. */ + + int fd = open(q->fname, O_RDONLY); + + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", q->fname); } + + q->testcase_buf = malloc(len); + + if (unlikely(!q->testcase_buf)) { + + PFATAL("Unable to malloc '%s' with len %u", q->fname, len); + + } + + ck_read(fd, q->testcase_buf, len, q->fname); + close(fd); + + /* Register testcase as cached */ + afl->q_testcase_cache[tid] = q; + afl->q_testcase_cache_size += q->len; + ++afl->q_testcase_cache_count; + if (tid >= afl->q_testcase_max_cache_count) + afl->q_testcase_max_cache_count = tid + 1; + + } + + return q->testcase_buf; + +} + diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index ee22b0f6..ab870319 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -692,6 +692,8 @@ void sync_fuzzers(afl_state_t *afl) { u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { + u32 orig_len = q->len; + /* Custom mutator trimmer */ if (afl->custom_mutators_count) { @@ -709,6 +711,8 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { }); + if (orig_len != q->len) { queue_testcase_retake(afl, q, orig_len); } + if (custom_trimmed) return trimmed_case; } @@ -842,6 +846,8 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { close(fd); + if (orig_len != q->len) queue_testcase_retake(afl, q, orig_len); + memcpy(afl->fsrv.trace_bits, afl->clean_trace, afl->fsrv.map_size); update_bitmap_score(afl, q); diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index a0a2795e..0824b77f 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -103,6 +103,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->stats_avg_exec = -1; afl->skip_deterministic = 1; afl->use_splicing = 1; + afl->q_testcase_max_cache_size = TESTCASE_CACHE * 1024000; #ifdef HAVE_AFFINITY afl->cpu_aff = -1; /* Selected CPU core */ @@ -353,6 +354,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_forksrv_init_tmout = (u8 *)get_afl_env(afl_environment_variables[i]); + } else if (!strncmp(env, "AFL_TESTCACHE_SIZE", + + afl_environment_variable_len)) { + + afl->afl_env.afl_testcache_size = + (u8 *)get_afl_env(afl_environment_variables[i]); + } else if (!strncmp(env, "AFL_STATSD_HOST", afl_environment_variable_len)) { diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 76f24977..4f0cab4c 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -165,6 +165,8 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, "edges_found : %u\n" "var_byte_count : %u\n" "havoc_expansion : %u\n" + "testcache_size : %llu\n" + "testcache_count : %u\n" "afl_banner : %s\n" "afl_version : " VERSION "\n" @@ -198,8 +200,9 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, #else -1, #endif - t_bytes, afl->var_byte_count, afl->expand_havoc, afl->use_banner, - afl->unicorn_mode ? "unicorn" : "", + t_bytes, afl->var_byte_count, afl->expand_havoc, + afl->q_testcase_cache_size, afl->q_testcase_cache_count, + afl->use_banner, afl->unicorn_mode ? "unicorn" : "", afl->fsrv.qemu_mode ? "qemu " : "", afl->non_instrumented_mode ? " non_instrumented " : "", afl->no_forkserver ? "no_fsrv " : "", afl->crash_mode ? "crash " : "", diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 6498eb30..a59abb7d 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -196,11 +196,13 @@ static void usage(u8 *argv0, int more_help) { "AFL_SKIP_BIN_CHECK: skip the check, if the target is an executable\n" "AFL_SKIP_CPUFREQ: do not warn about variable cpu clocking\n" "AFL_SKIP_CRASHES: during initial dry run do not terminate for crashing inputs\n" - "AFL_STATSD: enables StatsD metrics collection" - "AFL_STATSD_HOST: change default statsd host (default 127.0.0.1)" - "AFL_STATSD_PORT: change default statsd port (default: 8125)" - "AFL_STATSD_TAGS_FLAVOR: change default statsd tags format (default will disable tags)." - " Supported formats are: 'dogstatsd', 'librato', 'signalfx' and 'influxdb'" + "AFL_STATSD: enables StatsD metrics collection\n" + "AFL_STATSD_HOST: change default statsd host (default 127.0.0.1)\n" + "AFL_STATSD_PORT: change default statsd port (default: 8125)\n" + "AFL_STATSD_TAGS_FLAVOR: set statsd tags format (default: disable tags)\n" + " Supported formats are: 'dogstatsd', 'librato', 'signalfx'\n" + " and 'influxdb'\n" + "AFL_TESTCACHE_SIZE: use a cache for testcases, improves performance (in MB)\n" "AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)\n" //"AFL_PERSISTENT: not supported anymore -> no effect, just a warning\n" //"AFL_DEFER_FORKSRV: not supported anymore -> no effect, just a warning\n" @@ -885,7 +887,7 @@ int main(int argc, char **argv_orig, char **envp) { auto_sync = 1; afl->sync_id = ck_strdup("default"); afl->is_secondary_node = 1; - OKF("no -M/-S set, autoconfiguring for \"-S %s\"", afl->sync_id); + OKF("No -M/-S set, autoconfiguring for \"-S %s\"", afl->sync_id); } @@ -1006,6 +1008,21 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->afl_env.afl_testcache_size) { + + afl->q_testcase_max_cache_size = + (u64)atoi(afl->afl_env.afl_testcache_size) * 1024000; + OKF("Enabled testcache with %llu MB", + afl->q_testcase_max_cache_size / 1024000); + + } else { + + ACTF( + "No testcache was configured. it is recommended to use a testcache, it " + "improves performance: set AFL_TESTCACHE_SIZE=(value in MB)"); + + } + if (afl->afl_env.afl_forksrv_init_tmout) { afl->fsrv.init_tmout = atoi(afl->afl_env.afl_forksrv_init_tmout); diff --git a/src/afl-performance.c b/src/afl-performance.c index 6fa95dea..e070a05e 100644 --- a/src/afl-performance.c +++ b/src/afl-performance.c @@ -71,7 +71,7 @@ inline uint64_t rand_next(afl_state_t *afl) { inline double rand_next_percent(afl_state_t *afl) { - return (double)(((double)rand_next(afl)) / (double) 0xffffffffffffffff); + return (double)(((double)rand_next(afl)) / (double)0xffffffffffffffff); } diff --git a/test/test-custom-mutators.sh b/test/test-custom-mutators.sh index f7677ac5..d4d21048 100755 --- a/test/test-custom-mutators.sh +++ b/test/test-custom-mutators.sh @@ -1,4 +1,4 @@ -f#!/bin/sh +#!/bin/sh . ./test-pre.sh -- cgit 1.4.1 From 735e8c39561d3d4d8bae01251e025e52e05472e2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 14 Oct 2020 17:32:51 +0200 Subject: check for minimum cache size --- include/afl-fuzz.h | 2 +- src/afl-fuzz.c | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 514d558b..8bac1457 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1165,7 +1165,7 @@ void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, u32 old_len); #if TESTCASE_CACHE == 1 -#error define of TESTCASE_CACHE must be zero or larger than 1 + #error define of TESTCASE_CACHE must be zero or larger than 1 #endif #endif diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index a59abb7d..3167b742 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -200,8 +200,8 @@ static void usage(u8 *argv0, int more_help) { "AFL_STATSD_HOST: change default statsd host (default 127.0.0.1)\n" "AFL_STATSD_PORT: change default statsd port (default: 8125)\n" "AFL_STATSD_TAGS_FLAVOR: set statsd tags format (default: disable tags)\n" - " Supported formats are: 'dogstatsd', 'librato', 'signalfx'\n" - " and 'influxdb'\n" + " Supported formats are: 'dogstatsd', 'librato',\n" + " 'signalfx' and 'influxdb'\n" "AFL_TESTCACHE_SIZE: use a cache for testcases, improves performance (in MB)\n" "AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)\n" //"AFL_PERSISTENT: not supported anymore -> no effect, just a warning\n" @@ -1012,15 +1012,26 @@ int main(int argc, char **argv_orig, char **envp) { afl->q_testcase_max_cache_size = (u64)atoi(afl->afl_env.afl_testcache_size) * 1024000; - OKF("Enabled testcache with %llu MB", - afl->q_testcase_max_cache_size / 1024000); - } else { + } + + if (!afl->q_testcase_max_cache_size) { ACTF( "No testcache was configured. it is recommended to use a testcache, it " "improves performance: set AFL_TESTCACHE_SIZE=(value in MB)"); + } else if (afl->q_testcase_max_cache_size < 2 * MAX_FILE) { + + FATAL("AFL_TESTCACHE_SIZE must be set to %u or more, or 0 to disable", + (2 * MAX_FILE) % 1024000 ? 1 + ((2 * MAX_FILE) / 1024000) + : (2 * MAX_FILE) / 1024000); + + } else { + + OKF("Enabled testcache with %llu MB", + afl->q_testcase_max_cache_size / 1024000); + } if (afl->afl_env.afl_forksrv_init_tmout) { -- cgit 1.4.1 From 638bf19b651864bdc6ec801f87123d08f92af04e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 14 Oct 2020 17:14:36 +0100 Subject: DragonFlyBSD build fix proposal. --- GNUmakefile.gcc_plugin | 7 ++++++- include/afl-fuzz.h | 1 + src/afl-fuzz-init.c | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/GNUmakefile.gcc_plugin b/GNUmakefile.gcc_plugin index d139387a..bf3a3288 100644 --- a/GNUmakefile.gcc_plugin +++ b/GNUmakefile.gcc_plugin @@ -56,7 +56,8 @@ ifeq "$(findstring Foundation,$(shell $(CC) --version))" "" CXX = g++ endif -PLUGIN_FLAGS = -fPIC -fno-rtti -I"$(shell $(CC) -print-file-name=plugin)/include" +PLUGIN_BASE = "$(shell $(CC) -print-file-name=plugin)" +PLUGIN_FLAGS = -fPIC -fno-rtti -I$(PLUGIN_BASE)/include -I$(PLUGIN_BASE) HASH=\# GCCVER = $(shell $(CC) --version 2>/dev/null | awk 'NR == 1 {print $$NF}') @@ -88,6 +89,10 @@ ifeq "$(shell uname -s)" "OpenBSD" PLUGIN_FLAGS += -I/usr/local/include endif +ifeq "$(shell uname -s)" "DragonFly" + PLUGIN_FLAGS += -I/usr/local/include +endif + ifeq "$(shell uname -s)" "SunOS" PLUGIN_FLAGS += -I/usr/include/gmp endif diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 940c5602..972d2a60 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -66,6 +66,7 @@ #include #include +#include #include #include diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 607b652f..13e42e03 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -272,7 +272,7 @@ void bind_to_free_cpu(afl_state_t *afl) { #elif defined(__DragonFly__) - if (procs[i].kp_lwp.kl_cpuid < sizeof(cpu_used) && + if (procs[i].kp_lwp.kl_cpuid < (s32)sizeof(cpu_used) && procs[i].kp_lwp.kl_pctcpu > 10) cpu_used[procs[i].kp_lwp.kl_cpuid] = 1; -- cgit 1.4.1 From 24e0c9cf65428efce181eaecc8c69ee030b8dfcc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 15 Oct 2020 10:22:40 +0200 Subject: add missing swap bufs --- src/afl-fuzz-one.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 154e4b45..9c5e2e3c 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -2465,6 +2465,7 @@ retry_splicing: afl->in_scratch_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len); memcpy(afl->in_scratch_buf, in_buf, split_at); memcpy(afl->in_scratch_buf + split_at, new_buf, len - split_at); + afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); in_buf = afl->in_scratch_buf; out_buf = afl_realloc(AFL_BUF_PARAM(out), len); -- cgit 1.4.1 From 0139b8cdcb81ec1ed873f182946b686a04f46ac6 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 15 Oct 2020 10:28:11 +0200 Subject: add missing swap bufs --- src/afl-fuzz-one.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 9c5e2e3c..cbfbbc58 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -4434,6 +4434,7 @@ pacemaker_fuzzing: afl->in_scratch_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len); memcpy(afl->in_scratch_buf, in_buf, split_at); memcpy(afl->in_scratch_buf + split_at, new_buf, len - split_at); + afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); in_buf = afl->in_scratch_buf; out_buf = afl_realloc(AFL_BUF_PARAM(out), len); -- cgit 1.4.1 From d1e18f9edf43dc71ab81619eeed7a0f5fa0bb15f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 15 Oct 2020 12:20:33 +0200 Subject: fix afl_swap_bufs usage? --- src/afl-fuzz-one.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index cbfbbc58..1899193e 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -2149,7 +2149,6 @@ havoc_stage: temp_len - clone_to); afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); - out_buf = new_buf; temp_len += clone_len; } @@ -2346,7 +2345,6 @@ havoc_stage: temp_len - clone_to); afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); - out_buf = temp_buf; temp_len += clone_len; } @@ -2465,8 +2463,8 @@ retry_splicing: afl->in_scratch_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len); memcpy(afl->in_scratch_buf, in_buf, split_at); memcpy(afl->in_scratch_buf + split_at, new_buf, len - split_at); - afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); in_buf = afl->in_scratch_buf; + afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); out_buf = afl_realloc(AFL_BUF_PARAM(out), len); if (unlikely(!out_buf)) { PFATAL("alloc"); } @@ -4142,7 +4140,6 @@ pacemaker_fuzzing: temp_len - clone_to); afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); - out_buf = new_buf; temp_len += clone_len; MOpt_globals.cycles_v2[STAGE_Clone75] += 1; @@ -4434,8 +4431,8 @@ pacemaker_fuzzing: afl->in_scratch_buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len); memcpy(afl->in_scratch_buf, in_buf, split_at); memcpy(afl->in_scratch_buf + split_at, new_buf, len - split_at); - afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); in_buf = afl->in_scratch_buf; + afl_swap_bufs(AFL_BUF_PARAM(in), AFL_BUF_PARAM(in_scratch)); out_buf = afl_realloc(AFL_BUF_PARAM(out), len); if (unlikely(!out_buf)) { PFATAL("alloc"); } -- cgit 1.4.1 From ea0851c654285cc33ac25637d2054044ee6ee2ee Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 15 Oct 2020 12:54:18 +0200 Subject: fix previous commit --- src/afl-fuzz-one.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 1899193e..02550d36 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -2148,6 +2148,7 @@ havoc_stage: memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); + out_buf = new_buf; afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); temp_len += clone_len; @@ -2344,6 +2345,7 @@ havoc_stage: memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); + out_buf = temp_buf; afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); temp_len += clone_len; @@ -4139,6 +4141,7 @@ pacemaker_fuzzing: memcpy(new_buf + clone_to + clone_len, out_buf + clone_to, temp_len - clone_to); + out_buf = new_buf; afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); temp_len += clone_len; MOpt_globals.cycles_v2[STAGE_Clone75] += 1; -- cgit 1.4.1 From 0f8529a3db242131486cc3bf4a66c024c2b3e126 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 15 Oct 2020 15:22:40 +0200 Subject: prepare halloween fuzzbench run --- src/afl-fuzz-queue.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index c634328f..38d7f77e 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -120,10 +120,11 @@ void create_alias_table(afl_state_t *afl) { afl->alias_probability[S[--nS]] = 1; /* - fprintf(stderr, " entry alias probability perf_score\n"); + fprintf(stderr, " entry alias probability perf_score filename\n"); for (u32 i = 0; i < n; ++i) - fprintf(stderr, " %5u %5u %11u %0.9f\n", i, afl->alias_table[i], - afl->alias_probability[i], afl->queue_buf[i]->perf_score); + fprintf(stderr, " %5u %5u %11u %0.9f %s\n", i, afl->alias_table[i], + afl->alias_probability[i], afl->queue_buf[i]->perf_score, + afl->queue_buf[i]->fname); */ } -- cgit 1.4.1 From 354bda28465588e424c0a93b413af01a603191ce Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 15 Oct 2020 15:33:47 +0200 Subject: fix reget of testcase after trim --- src/afl-fuzz-queue.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 38d7f77e..095a391f 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -875,22 +875,27 @@ void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, if (likely(q->testcase_buf)) { - free(q->testcase_buf); - int fd = open(q->fname, O_RDONLY); + u32 len = q->len; - if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", q->fname); } + if (len != old_len) { - u32 len = q->len; - q->testcase_buf = malloc(len); + afl->q_testcase_cache_size = + afl->q_testcase_cache_size + q->len - old_len; + q->testcase_buf = realloc(q->testcase_buf, len); + if (unlikely(!q->testcase_buf)) { - if (unlikely(!q->testcase_buf)) { + PFATAL("Unable to malloc '%s' with len %d", q->fname, len); - PFATAL("Unable to mmap '%s' with len %d", q->fname, len); + } } + int fd = open(q->fname, O_RDONLY); + + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", q->fname); } + + ck_read(fd, q->testcase_buf, len, q->fname); close(fd); - afl->q_testcase_cache_size = afl->q_testcase_cache_size + q->len - old_len; } -- cgit 1.4.1 From f41aafa4f7aa446c3cb1cbe6d77364cf32a6c6cb Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 15 Oct 2020 15:48:39 +0200 Subject: retake from mem if possible --- include/afl-fuzz.h | 4 ++++ src/afl-fuzz-queue.c | 39 ++++++++++++++++++++++++++++++++++----- src/afl-fuzz-run.c | 8 ++++++-- 3 files changed, 44 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index acded98f..6204c81b 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1165,6 +1165,10 @@ u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q); void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, u32 old_len); +/* If trimming changes the testcase size we have to replace it */ +void queue_testcase_retake_mem(afl_state_t *afl, struct queue_entry *q, u8 *in, + u32 len, u32 old_len); + #if TESTCASE_CACHE == 1 #error define of TESTCASE_CACHE must be zero or larger than 1 #endif diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 095a391f..92b722f6 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -870,8 +870,10 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { } -void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, - u32 old_len) { +/* after a custom trim we need to reload the testcase from disk */ + +inline void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, + u32 old_len) { if (likely(q->testcase_buf)) { @@ -879,9 +881,9 @@ void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, if (len != old_len) { - afl->q_testcase_cache_size = - afl->q_testcase_cache_size + q->len - old_len; + afl->q_testcase_cache_size = afl->q_testcase_cache_size + len - old_len; q->testcase_buf = realloc(q->testcase_buf, len); + if (unlikely(!q->testcase_buf)) { PFATAL("Unable to malloc '%s' with len %d", q->fname, len); @@ -901,8 +903,35 @@ void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, } +/* after a normal trim we need to replace the testcase with the new data */ + +inline void queue_testcase_retake_mem(afl_state_t *afl, struct queue_entry *q, + u8 *in, u32 len, u32 old_len) { + + if (likely(q->testcase_buf)) { + + if (len != old_len) { + + afl->q_testcase_cache_size = afl->q_testcase_cache_size + len - old_len; + q->testcase_buf = realloc(q->testcase_buf, len); + + if (unlikely(!q->testcase_buf)) { + + PFATAL("Unable to malloc '%s' with len %d", q->fname, len); + + } + + } + + memcpy(q->testcase_buf, in, len); + + } + +} + /* Returns the testcase buf from the file behind this queue entry. Increases the refcount. */ + inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { u32 len = q->len; @@ -913,7 +942,7 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { u8 *buf; - if (q == afl->queue_cur) { + if (unlikely(q == afl->queue_cur)) { buf = afl_realloc((void **)&afl->testcase_buf, len); diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index ab870319..dfd3abfb 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -711,7 +711,11 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { }); - if (orig_len != q->len) { queue_testcase_retake(afl, q, orig_len); } + if (orig_len != q->len || custom_trimmed) { + + queue_testcase_retake(afl, q, orig_len); + + } if (custom_trimmed) return trimmed_case; @@ -846,7 +850,7 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { close(fd); - if (orig_len != q->len) queue_testcase_retake(afl, q, orig_len); + queue_testcase_retake_mem(afl, q, in_buf, q->len, orig_len); memcpy(afl->fsrv.trace_bits, afl->clean_trace, afl->fsrv.map_size); update_bitmap_score(afl, q); -- cgit 1.4.1 From ee66cd7b27315b2003b005d2f215191d5db954c8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 15 Oct 2020 17:08:45 +0200 Subject: testcache_size = 2 ok fix --- src/afl-fuzz.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 3167b742..9a82edeb 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1011,7 +1011,7 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->afl_env.afl_testcache_size) { afl->q_testcase_max_cache_size = - (u64)atoi(afl->afl_env.afl_testcache_size) * 1024000; + (u64)atoi(afl->afl_env.afl_testcache_size) * 1048576; } @@ -1024,8 +1024,8 @@ int main(int argc, char **argv_orig, char **envp) { } else if (afl->q_testcase_max_cache_size < 2 * MAX_FILE) { FATAL("AFL_TESTCACHE_SIZE must be set to %u or more, or 0 to disable", - (2 * MAX_FILE) % 1024000 ? 1 + ((2 * MAX_FILE) / 1024000) - : (2 * MAX_FILE) / 1024000); + (2 * MAX_FILE) % 1024000 == 0 ? (2 * MAX_FILE) / 1048576 + : 1 + ((2 * MAX_FILE) / 1048576)); } else { -- cgit 1.4.1 From b82e9ad3dbf3068223498445ff5e7f4ea63ce6f7 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 16 Oct 2020 09:16:35 +0200 Subject: next afl-showmap fix attempt --- TODO.md | 4 ---- src/afl-showmap.c | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/TODO.md b/TODO.md index 0f60f267..6b43d6be 100644 --- a/TODO.md +++ b/TODO.md @@ -7,7 +7,6 @@ - afl-plot to support multiple plot_data - afl_custom_fuzz_splice_optin() - intel-pt tracer - - https://github.com/zyingp/desockmulti ? ## Further down the road @@ -18,9 +17,6 @@ afl-fuzz: llvm_mode: - add __sanitizer_cov_trace_cmp* support -gcc_plugin: - - (wait for submission then decide) - qemu_mode: - non colliding instrumentation - rename qemu specific envs to AFL_QEMU (AFL_ENTRYPOINT, AFL_CODE_START/END, diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 545bfaa9..822e62df 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -742,8 +742,10 @@ int main(int argc, char **argv_orig, char **envp) { case 'f': // only in here to avoid a compiler warning for use_stdin - fsrv->use_stdin = 0; FATAL("Option -f is not supported in afl-showmap"); + // currently not reached: + fsrv->use_stdin = 0; + fsrv->out_file = strdup(optarg); break; @@ -1015,6 +1017,7 @@ int main(int argc, char **argv_orig, char **envp) { alloc_printf("%s/.afl-showmap-temp-%u", use_dir, (u32)getpid()); unlink(stdin_file); atexit(at_exit_handler); + afl->fsrv.out_file = stdin_file; fsrv->out_fd = open(stdin_file, O_RDWR | O_CREAT | O_EXCL, 0600); if (fsrv->out_fd < 0) { PFATAL("Unable to create '%s'", out_file); } -- cgit 1.4.1 From d5c3b4bafdae8a68e7f63c0afdd1cc5820636f2d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 16 Oct 2020 09:35:35 +0200 Subject: directly add new queue to cache --- include/afl-fuzz.h | 4 ++++ src/afl-fuzz-bitmap.c | 6 ++++++ src/afl-fuzz-queue.c | 43 ++++++++++++++++++++++++++++++++++++++++++- src/afl-showmap.c | 2 +- 4 files changed, 53 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 6204c81b..e94f389a 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1169,6 +1169,10 @@ void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, void queue_testcase_retake_mem(afl_state_t *afl, struct queue_entry *q, u8 *in, u32 len, u32 old_len); +/* Add a new queue entry directly to the cache */ + +void queue_testcase_store_mem(afl_state_t *afl, struct queue_entry *q, u8 *mem); + #if TESTCASE_CACHE == 1 #error define of TESTCASE_CACHE must be zero or larger than 1 #endif diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index a22223b9..2653b9fd 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -623,6 +623,12 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { ck_write(fd, mem, len, queue_fn); close(fd); + if (likely(afl->q_testcase_max_cache_size)) { + + queue_testcase_store_mem(afl, afl->queue_top, mem); + + } + keeping = 1; } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 92b722f6..f8034ebd 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -1021,7 +1021,7 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { /* Register testcase as cached */ afl->q_testcase_cache[tid] = q; - afl->q_testcase_cache_size += q->len; + afl->q_testcase_cache_size += len; ++afl->q_testcase_cache_count; if (tid >= afl->q_testcase_max_cache_count) afl->q_testcase_max_cache_count = tid + 1; @@ -1032,3 +1032,44 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { } +/* Adds the new queue entry to the cache. */ + +inline void queue_testcase_store_mem(afl_state_t *afl, struct queue_entry *q, + u8 *mem) { + + u32 len = q->len; + + if (unlikely(afl->q_testcase_cache_size + len >= + afl->q_testcase_max_cache_size || + afl->q_testcase_cache_count >= TESTCASE_ENTRIES - 1)) { + + return; + + } + + u32 tid = 0; + + while (likely(afl->q_testcase_cache[tid] != NULL)) + ++tid; + + /* Map the test case into memory. */ + + q->testcase_buf = malloc(len); + + if (unlikely(!q->testcase_buf)) { + + PFATAL("Unable to malloc '%s' with len %u", q->fname, len); + + } + + memcpy(q->testcase_buf, mem, len); + + /* Register testcase as cached */ + afl->q_testcase_cache[tid] = q; + afl->q_testcase_cache_size += len; + ++afl->q_testcase_cache_count; + if (tid >= afl->q_testcase_max_cache_count) + afl->q_testcase_max_cache_count = tid + 1; + +} + diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 822e62df..6213c447 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1017,7 +1017,7 @@ int main(int argc, char **argv_orig, char **envp) { alloc_printf("%s/.afl-showmap-temp-%u", use_dir, (u32)getpid()); unlink(stdin_file); atexit(at_exit_handler); - afl->fsrv.out_file = stdin_file; + fsrv->out_file = stdin_file; fsrv->out_fd = open(stdin_file, O_RDWR | O_CREAT | O_EXCL, 0600); if (fsrv->out_fd < 0) { PFATAL("Unable to create '%s'", out_file); } -- cgit 1.4.1 From fcea01a8ea7ec507b675d839035eb0fed2f06867 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 17 Oct 2020 11:38:55 +0200 Subject: add eviction stat --- include/afl-fuzz.h | 3 +++ src/afl-fuzz-queue.c | 1 + src/afl-fuzz-stats.c | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index e94f389a..880b8d50 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -704,6 +704,9 @@ typedef struct afl_state { /* How many queue entries currently have cached testcases */ u32 q_testcase_cache_count; + /* How often did we evict from the cache */ + u32 q_testcase_evictions; + /* Refs to each queue entry with cached testcase (for eviction, if cache_count * is too large) */ struct queue_entry *q_testcase_cache[TESTCASE_ENTRIES]; diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index f8034ebd..e0df7206 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -996,6 +996,7 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { afl->q_testcase_cache_size -= old_cached->len; afl->q_testcase_cache[tid] = NULL; --afl->q_testcase_cache_count; + ++afl->q_testcase_evictions; } diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 4f0cab4c..d213d054 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -167,6 +167,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, "havoc_expansion : %u\n" "testcache_size : %llu\n" "testcache_count : %u\n" + "testcache_evict : %u\n" "afl_banner : %s\n" "afl_version : " VERSION "\n" @@ -202,7 +203,8 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, #endif t_bytes, afl->var_byte_count, afl->expand_havoc, afl->q_testcase_cache_size, afl->q_testcase_cache_count, - afl->use_banner, afl->unicorn_mode ? "unicorn" : "", + afl->q_testcase_evictions, afl->use_banner, + afl->unicorn_mode ? "unicorn" : "", afl->fsrv.qemu_mode ? "qemu " : "", afl->non_instrumented_mode ? " non_instrumented " : "", afl->no_forkserver ? "no_fsrv " : "", afl->crash_mode ? "crash " : "", -- cgit 1.4.1 From d8a058bf592a040256bb4b0a1ff9dcb97a8e3eda Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 17 Oct 2020 14:34:14 +0200 Subject: fix AFL_CMIN_CRASHES_ONLY=1 afl-showmap --- afl-cmin | 4 ++-- src/afl-showmap.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/afl-cmin b/afl-cmin index f8d3518d..0dbf1390 100755 --- a/afl-cmin +++ b/afl-cmin @@ -409,8 +409,8 @@ BEGIN { retval = system( AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string" /dev/null") diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 6213c447..bd0d1a29 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -209,6 +209,13 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { if (!outfile) { FATAL("Output filename not set (Bug in AFL++?)"); } + if (cmin_mode && (fsrv->last_run_timed_out + || (!caa && child_crashed != cco))) { + + return ret; + + } + if (!strncmp(outfile, "/dev/", 5)) { fd = open(outfile, O_WRONLY); @@ -255,9 +262,6 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { if (cmin_mode) { - if (fsrv->last_run_timed_out) { break; } - if (!caa && child_crashed != cco) { break; } - fprintf(f, "%u%u\n", fsrv->trace_bits[i], i); } else { @@ -292,6 +296,37 @@ static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem, classify_counts(fsrv); + if (!quiet_mode) { SAYF(cRST "-- Program output ends --\n"); } + + if (!fsrv->last_run_timed_out && !stop_soon && WIFSIGNALED(fsrv->child_status)) { + + child_crashed = 1; + + } else { + + child_crashed = 0; + + } + + if (!quiet_mode) { + + if (fsrv->last_run_timed_out) { + + SAYF(cLRD "\n+++ Program timed off +++\n" cRST); + + } else if (stop_soon) { + + SAYF(cLRD "\n+++ Program aborted by user +++\n" cRST); + + } else if (child_crashed) { + + SAYF(cLRD "\n+++ Program killed by signal %u +++\n" cRST, + WTERMSIG(fsrv->child_status)); + + } + + } + if (stop_soon) { SAYF(cRST cLRD "\n+++ afl-showmap folder mode aborted by user +++\n" cRST); @@ -1156,8 +1191,17 @@ int main(int argc, char **argv_orig, char **envp) { afl_shm_deinit(&shm); if (fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); - u32 ret = child_crashed * 2 + fsrv->last_run_timed_out; + u32 ret; + + if (cmin_mode && !!getenv("AFL_CMIN_CRASHES_ONLY")) { + + ret = fsrv->last_run_timed_out; + } else { + + ret = child_crashed * 2 + fsrv->last_run_timed_out; + + } if (fsrv->target_path) { ck_free(fsrv->target_path); } afl_fsrv_deinit(fsrv); -- cgit 1.4.1 From ac1c3b87015dd2c9b1bae0198f7925816aa63aec Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 19 Oct 2020 11:34:57 +0200 Subject: mini improvements --- TODO.md | 1 + src/afl-fuzz-one.c | 2 +- src/afl-fuzz-queue.c | 8 ++++---- src/afl-fuzz.c | 56 +++++++++++++++++++++++++++------------------------- src/afl-showmap.c | 10 ++++++---- 5 files changed, 41 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/TODO.md b/TODO.md index 6b43d6be..7e203d26 100644 --- a/TODO.md +++ b/TODO.md @@ -7,6 +7,7 @@ - afl-plot to support multiple plot_data - afl_custom_fuzz_splice_optin() - intel-pt tracer + - own sancov for llvm 12 ## Further down the road diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 02550d36..1e63abc7 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -2540,7 +2540,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { #else - if (afl->pending_favored) { + if (likely(afl->pending_favored)) { /* If we have any favored, non-fuzzed new arrivals in the queue, possibly skip to them at the expense of already-fuzzed or non-favored diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index e0df7206..7f157121 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -179,9 +179,9 @@ void mark_as_variable(afl_state_t *afl, struct queue_entry *q) { void mark_as_redundant(afl_state_t *afl, struct queue_entry *q, u8 state) { - u8 fn[PATH_MAX]; + if (likely(state == q->fs_redundant)) { return; } - if (state == q->fs_redundant) { return; } + u8 fn[PATH_MAX]; q->fs_redundant = state; @@ -521,13 +521,13 @@ void update_bitmap_score(afl_state_t *afl, struct queue_entry *q) { void cull_queue(afl_state_t *afl) { + if (likely(!afl->score_changed || afl->non_instrumented_mode)) { return; } + struct queue_entry *q; u32 len = (afl->fsrv.map_size >> 3); u32 i; u8 * temp_v = afl->map_tmp_buf; - if (afl->non_instrumented_mode || !afl->score_changed) { return; } - afl->score_changed = 0; memset(temp_v, 255, len); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 9a82edeb..7215ecec 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1378,10 +1378,9 @@ int main(int argc, char **argv_orig, char **envp) { u32 runs_in_current_cycle = (u32)-1; u32 prev_queued_paths = 0; + u8 skipped_fuzz; - while (1) { - - u8 skipped_fuzz; + while (likely(!afl->stop_soon)) { cull_queue(afl); @@ -1418,8 +1417,8 @@ int main(int argc, char **argv_orig, char **envp) { /* If we had a full queue cycle with no new finds, try recombination strategies next. */ - if (afl->queued_paths == prev_queued && - (get_cur_time() - afl->start_time) >= 3600) { + if (unlikely(afl->queued_paths == prev_queued && + (get_cur_time() - afl->start_time) >= 3600)) { if (afl->use_splicing) { @@ -1534,46 +1533,49 @@ int main(int argc, char **argv_orig, char **envp) { } - if (likely(!afl->old_seed_selection)) { + ++runs_in_current_cycle; - ++runs_in_current_cycle; - if (unlikely(prev_queued_paths < afl->queued_paths)) { + do { - // we have new queue entries since the last run, recreate alias table - prev_queued_paths = afl->queued_paths; - create_alias_table(afl); + if (likely(!afl->old_seed_selection)) { - } + if (unlikely(prev_queued_paths < afl->queued_paths)) { - afl->current_entry = select_next_queue_entry(afl); - afl->queue_cur = afl->queue_buf[afl->current_entry]; + // we have new queue entries since the last run, recreate alias table + prev_queued_paths = afl->queued_paths; + create_alias_table(afl); - } + } - skipped_fuzz = fuzz_one(afl); + afl->current_entry = select_next_queue_entry(afl); + afl->queue_cur = afl->queue_buf[afl->current_entry]; - if (!skipped_fuzz && !afl->stop_soon && afl->sync_id) { + } - if (unlikely(afl->is_main_node)) { + skipped_fuzz = fuzz_one(afl); - if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) { sync_fuzzers(afl); } + if (unlikely(!afl->stop_soon && exit_1)) { afl->stop_soon = 2; } - } else { + if (unlikely(afl->old_seed_selection)) { - if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); } + afl->queue_cur = afl->queue_cur->next; + ++afl->current_entry; } - } + } while (skipped_fuzz && afl->queue_cur && !afl->stop_soon); - if (!afl->stop_soon && exit_1) { afl->stop_soon = 2; } + if (!afl->stop_soon && afl->sync_id) { - if (afl->stop_soon) { break; } + if (unlikely(afl->is_main_node)) { - if (unlikely(afl->old_seed_selection)) { + if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) { sync_fuzzers(afl); } - afl->queue_cur = afl->queue_cur->next; - ++afl->current_entry; + } else { + + if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); } + + } } diff --git a/src/afl-showmap.c b/src/afl-showmap.c index bd0d1a29..4b357254 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -209,10 +209,10 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { if (!outfile) { FATAL("Output filename not set (Bug in AFL++?)"); } - if (cmin_mode && (fsrv->last_run_timed_out - || (!caa && child_crashed != cco))) { + if (cmin_mode && + (fsrv->last_run_timed_out || (!caa && child_crashed != cco))) { - return ret; + return ret; } @@ -298,7 +298,8 @@ static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem, if (!quiet_mode) { SAYF(cRST "-- Program output ends --\n"); } - if (!fsrv->last_run_timed_out && !stop_soon && WIFSIGNALED(fsrv->child_status)) { + if (!fsrv->last_run_timed_out && !stop_soon && + WIFSIGNALED(fsrv->child_status)) { child_crashed = 1; @@ -1202,6 +1203,7 @@ int main(int argc, char **argv_orig, char **envp) { ret = child_crashed * 2 + fsrv->last_run_timed_out; } + if (fsrv->target_path) { ck_free(fsrv->target_path); } afl_fsrv_deinit(fsrv); -- cgit 1.4.1 From d0cdbc48aebf88bc0cc519db553ca762f794296e Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 20 Oct 2020 00:07:40 +0200 Subject: always close file descriptor when opened, use standard types --- src/afl-fuzz-queue.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 7f157121..1bda0835 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -211,13 +211,16 @@ static u8 check_if_text(struct queue_entry *q) { if (q->len < AFL_TXT_MIN_LEN) return 0; u8 buf[MAX_FILE]; - s32 fd, len = q->len, offset = 0, ascii = 0, utf8 = 0, comp; + int fd; + u32 len = q->len, offset = 0, ascii = 0, utf8 = 0; + ssize_t comp; if (len >= MAX_FILE) len = MAX_FILE - 1; if ((fd = open(q->fname, O_RDONLY)) < 0) return 0; - if ((comp = read(fd, buf, len)) != len) return 0; - buf[len] = 0; + comp = read(fd, buf, len); close(fd); + if (comp != (ssize_t)len) return 0; + buf[len] = 0; while (offset < len) { -- cgit 1.4.1 From 73c0e1357fa33276ca0a0ee63aacc933aa9ceac5 Mon Sep 17 00:00:00 2001 From: bigredb Date: Tue, 20 Oct 2020 05:16:34 -0700 Subject: qemu argv index was still wrong --- src/afl-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-common.c b/src/afl-common.c index ddae2ac1..19c9419b 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -146,7 +146,7 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { u8 * tmp, *cp = NULL, *rsl, *own_copy; memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1)); - new_argv[argc + 2] = NULL; + new_argv[argc + 3] = NULL; new_argv[2] = *target_path_p; new_argv[1] = "--"; -- cgit 1.4.1 From 982260c134e7b373a6ec102fe2ef9904d342fa20 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 20 Oct 2020 20:48:33 +0200 Subject: fix timeout bug in read_s32_timed on non linux OSes --- src/afl-forkserver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 33b16817..df300950 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -140,7 +140,7 @@ read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, volatile u8 *stop_soon_p) { timeout.tv_sec = (timeout_ms / 1000); timeout.tv_usec = (timeout_ms % 1000) * 1000; #if !defined(__linux__) - u64 read_start = get_cur_time_us(); + u32 read_start = get_cur_time_us(); #endif /* set exceptfds as well to return when a child exited/closed the pipe. */ @@ -166,7 +166,7 @@ restart_select: timeout_ms, ((u64)timeout_ms - (timeout.tv_sec * 1000 + timeout.tv_usec / 1000))); #else - u32 exec_ms = MIN(timeout_ms, get_cur_time_us() - read_start); + u32 exec_ms = MIN(timeout_ms, (get_cur_time_us() - read_start) / 1000); #endif // ensure to report 1 ms has passed (0 is an error) -- cgit 1.4.1 From 8d75c089384a0975e39e54f3ba23c37a6101b61c Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 22 Oct 2020 15:48:09 +0200 Subject: change queue cache algo --- src/afl-fuzz-queue.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 1bda0835..3e6deb0c 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -210,9 +210,9 @@ static u8 check_if_text(struct queue_entry *q) { if (q->len < AFL_TXT_MIN_LEN) return 0; - u8 buf[MAX_FILE]; - int fd; - u32 len = q->len, offset = 0, ascii = 0, utf8 = 0; + u8 buf[MAX_FILE]; + int fd; + u32 len = q->len, offset = 0, ascii = 0, utf8 = 0; ssize_t comp; if (len >= MAX_FILE) len = MAX_FILE - 1; @@ -913,7 +913,7 @@ inline void queue_testcase_retake_mem(afl_state_t *afl, struct queue_entry *q, if (likely(q->testcase_buf)) { - if (len != old_len) { + if (likely(len != old_len)) { afl->q_testcase_cache_size = afl->q_testcase_cache_size + len - old_len; q->testcase_buf = realloc(q->testcase_buf, len); @@ -976,17 +976,21 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { if (unlikely(!q->testcase_buf)) { /* Buf not cached, let's load it */ - u32 tid = 0; + u32 tid = afl->q_testcase_max_cache_count; while (unlikely(afl->q_testcase_cache_size + len >= afl->q_testcase_max_cache_size || afl->q_testcase_cache_count >= TESTCASE_ENTRIES - 1)) { - /* Cache full. We neet to evict one to map one. + /* Cache full. We neet to evict one or more to map one. Get a random one which is not in use */ do { + // if the cache (MB) is not enough for the queue then this gets + // undesirable because q_testcase_max_cache_count grows sometimes + // although the number of items in the cache will not change hence + // more and more loops tid = rand_below(afl, afl->q_testcase_max_cache_count); } while (afl->q_testcase_cache[tid] == NULL || @@ -1003,7 +1007,16 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { } - while (likely(afl->q_testcase_cache[tid] != NULL)) + if (tid >= TESTCASE_ENTRIES) { + + // uh we were full, so now we have to search from start + tid = 0; + + } + + // we need this while loop in case there were ever previous evictions but + // not in this call. + while (unlikely(afl->q_testcase_cache[tid] != NULL)) ++tid; /* Map the test case into memory. */ @@ -1047,6 +1060,7 @@ inline void queue_testcase_store_mem(afl_state_t *afl, struct queue_entry *q, afl->q_testcase_max_cache_size || afl->q_testcase_cache_count >= TESTCASE_ENTRIES - 1)) { + // no space? will be loaded regularly later. return; } -- cgit 1.4.1 From c866aef37fcf799506d93b9a47d4eb2b77c75f5b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 22 Oct 2020 21:07:37 +0200 Subject: maybe enhancement to cache algo --- include/afl-fuzz.h | 5 ++++- src/afl-fuzz-queue.c | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 880b8d50..220380b9 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -704,7 +704,10 @@ typedef struct afl_state { /* How many queue entries currently have cached testcases */ u32 q_testcase_cache_count; - /* How often did we evict from the cache */ + /* the smallest id currently known free entry */ + u32 q_testcase_smallest_free; + + /* How often did we evict from the cache (for statistics only) */ u32 q_testcase_evictions; /* Refs to each queue entry with cached testcase (for eviction, if cache_count diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 3e6deb0c..db387c33 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -1004,13 +1004,15 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { afl->q_testcase_cache[tid] = NULL; --afl->q_testcase_cache_count; ++afl->q_testcase_evictions; + if (tid < afl->q_testcase_smallest_free) + afl->q_testcase_smallest_free = tid; } - if (tid >= TESTCASE_ENTRIES) { + if (unlikely(tid >= TESTCASE_ENTRIES)) { // uh we were full, so now we have to search from start - tid = 0; + tid = afl->q_testcase_smallest_free; } @@ -1042,6 +1044,8 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { ++afl->q_testcase_cache_count; if (tid >= afl->q_testcase_max_cache_count) afl->q_testcase_max_cache_count = tid + 1; + if (tid == afl->q_testcase_smallest_free) + afl->q_testcase_smallest_free = tid + 1; } -- cgit 1.4.1 From 0e748ccda713708de6a501d23a58788aba9d0b03 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 23 Oct 2020 14:05:34 +0200 Subject: set max testcache entries automated if not specified by the user --- include/afl-fuzz.h | 7 ++++-- include/config.h | 10 ++++---- include/envs.h | 1 + src/afl-fuzz-queue.c | 11 +++++---- src/afl-fuzz-state.c | 10 +++++++- src/afl-fuzz-stats.c | 2 +- src/afl-fuzz.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++--- 7 files changed, 91 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 220380b9..11feb9f7 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -366,7 +366,7 @@ typedef struct afl_env_vars { u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload, *afl_max_det_extras, *afl_statsd_host, *afl_statsd_port, - *afl_statsd_tags_flavor, *afl_testcache_size; + *afl_statsd_tags_flavor, *afl_testcache_size, *afl_testcache_entries; } afl_env_vars_t; @@ -695,6 +695,9 @@ typedef struct afl_state { /* This is the user specified maximum size to use for the testcase cache */ u64 q_testcase_max_cache_size; + /* This is the user specified maximum entries in the testcase cache */ + u32 q_testcase_max_cache_entries; + /* How much of the testcase cache is used so far */ u64 q_testcase_cache_size; @@ -712,7 +715,7 @@ typedef struct afl_state { /* Refs to each queue entry with cached testcase (for eviction, if cache_count * is too large) */ - struct queue_entry *q_testcase_cache[TESTCASE_ENTRIES]; + struct queue_entry **q_testcase_cache; } afl_state_t; diff --git a/include/config.h b/include/config.h index b4f3a775..491d8132 100644 --- a/include/config.h +++ b/include/config.h @@ -295,14 +295,12 @@ #define RESEED_RNG 100000 -/* The maximum number of testcases to cache */ - -#define TESTCASE_ENTRIES 16384 - /* The default maximum testcase cache size in MB, 0 = disable. - A value between 50 and 250 is a good default value. */ + A value between 50 and 250 is a good default value. Note that the + number of entries will be auto assigned if not specified via the + AFL_TESTCACHE_ENTRIES env variable */ -#define TESTCASE_CACHE 0 +#define TESTCASE_CACHE_SIZE 50 /* Maximum line length passed from GCC to 'as' and used for parsing configuration files: */ diff --git a/include/envs.h b/include/envs.h index a1b3ad12..b753d5f8 100644 --- a/include/envs.h +++ b/include/envs.h @@ -140,6 +140,7 @@ static char *afl_environment_variables[] = { "AFL_STATSD_PORT", "AFL_STATSD_TAGS_FLAVOR", "AFL_TESTCACHE_SIZE", + "AFL_TESTCACHE_ENTRIES", "AFL_TMIN_EXACT", "AFL_TMPDIR", "AFL_TOKEN_FILE", diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index db387c33..baa80e61 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -978,9 +978,9 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { /* Buf not cached, let's load it */ u32 tid = afl->q_testcase_max_cache_count; - while (unlikely(afl->q_testcase_cache_size + len >= - afl->q_testcase_max_cache_size || - afl->q_testcase_cache_count >= TESTCASE_ENTRIES - 1)) { + while (unlikely( + afl->q_testcase_cache_size + len >= afl->q_testcase_max_cache_size || + afl->q_testcase_cache_count >= afl->q_testcase_max_cache_entries - 1)) { /* Cache full. We neet to evict one or more to map one. Get a random one which is not in use */ @@ -1009,7 +1009,7 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { } - if (unlikely(tid >= TESTCASE_ENTRIES)) { + if (unlikely(tid >= afl->q_testcase_max_cache_entries)) { // uh we were full, so now we have to search from start tid = afl->q_testcase_smallest_free; @@ -1062,7 +1062,8 @@ inline void queue_testcase_store_mem(afl_state_t *afl, struct queue_entry *q, if (unlikely(afl->q_testcase_cache_size + len >= afl->q_testcase_max_cache_size || - afl->q_testcase_cache_count >= TESTCASE_ENTRIES - 1)) { + afl->q_testcase_cache_count >= + afl->q_testcase_max_cache_entries - 1)) { // no space? will be loaded regularly later. return; diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 0824b77f..ae7d410b 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -103,7 +103,8 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->stats_avg_exec = -1; afl->skip_deterministic = 1; afl->use_splicing = 1; - afl->q_testcase_max_cache_size = TESTCASE_CACHE * 1024000; + afl->q_testcase_max_cache_size = TESTCASE_CACHE_SIZE * 1048576UL; + afl->q_testcase_max_cache_entries = 4096; #ifdef HAVE_AFFINITY afl->cpu_aff = -1; /* Selected CPU core */ @@ -361,6 +362,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_testcache_size = (u8 *)get_afl_env(afl_environment_variables[i]); + } else if (!strncmp(env, "AFL_TESTCACHE_ENTRIES", + + afl_environment_variable_len)) { + + afl->afl_env.afl_testcache_entries = + (u8 *)get_afl_env(afl_environment_variables[i]); + } else if (!strncmp(env, "AFL_STATSD_HOST", afl_environment_variable_len)) { diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index d213d054..bec90519 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -1028,7 +1028,7 @@ void show_init_stats(afl_state_t *afl) { } - SAYF("\n"); + // SAYF("\n"); if (avg_us > ((afl->fsrv.qemu_mode || afl->unicorn_mode) ? 50000 : 10000)) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 7215ecec..637e1985 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -251,7 +251,7 @@ static int stricmp(char const *a, char const *b) { int main(int argc, char **argv_orig, char **envp) { - s32 opt, i, auto_sync = 0; + s32 opt, i, auto_sync = 0, user_set_cache = 0; u64 prev_queued = 0; u32 sync_interval_cnt = 0, seek_to = 0, show_help = 0, map_size = MAP_SIZE; u8 *extras_dir[4]; @@ -1015,6 +1015,22 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->afl_env.afl_testcache_entries) { + + afl->q_testcase_max_cache_entries = + (u32)atoi(afl->afl_env.afl_testcache_entries); + + user_set_cache = 1; + + } + + if (!afl->afl_env.afl_testcache_size || !afl->afl_env.afl_testcache_entries) { + + afl->afl_env.afl_testcache_entries = 0; + afl->afl_env.afl_testcache_size = 0; + + } + if (!afl->q_testcase_max_cache_size) { ACTF( @@ -1347,6 +1363,52 @@ int main(int argc, char **argv_orig, char **envp) { perform_dry_run(afl); + if (!user_set_cache && afl->q_testcase_max_cache_size) { + + /* The user defined not a fixed number of entries for the cache. + Hence we autodetect a good value. After the dry run inputs are + trimmed and we know the average and max size of the input seeds. + We use this information to set a fitting size to max entries + based on the cache size. */ + + struct queue_entry *q = afl->queue; + u64 size = 0, count = 0, avg = 0, max = 0; + + while (q) { + + ++count; + size += q->len; + if (max < q->len) { max = q->len; } + q = q->next; + + } + + if (count) { + + avg = size / count; + avg = ((avg + max) / 2) + 1; + + } + + if (avg < 10240) { avg = 10240; } + + afl->q_testcase_max_cache_entries = afl->q_testcase_max_cache_size / avg; + + if (afl->q_testcase_max_cache_entries > 32768) + afl->q_testcase_max_cache_entries = 32768; + + } + + if (afl->q_testcase_max_cache_entries) { + + OKF("Setting %u maximum entries for the testcase cache", + afl->q_testcase_max_cache_entries); + afl->q_testcase_cache = + ck_alloc(afl->q_testcase_max_cache_entries * sizeof(size_t)); + if (!afl->q_testcase_cache) { PFATAL("malloc failed for cache entries"); } + + } + cull_queue(afl); if (!afl->pending_not_fuzzed) @@ -1366,8 +1428,7 @@ int main(int argc, char **argv_orig, char **envp) { if (!afl->not_on_tty) { - sleep(4); - afl->start_time += 4000; + sleep(1); if (afl->stop_soon) { goto stop_fuzzing; } } @@ -1654,6 +1715,7 @@ stop_fuzzing: ck_free(afl->fsrv.target_path); ck_free(afl->fsrv.out_file); ck_free(afl->sync_id); + if (afl->q_testcase_cache) { ck_free(afl->q_testcase_cache); } afl_state_deinit(afl); free(afl); /* not tracked */ -- cgit 1.4.1 From aa0d3785206d52c1815aff850817d55bf50f3598 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 23 Oct 2020 15:21:21 +0200 Subject: better cache entry algo --- src/afl-fuzz-queue.c | 18 +++++++++++++++++ src/afl-fuzz-state.c | 2 +- src/afl-fuzz.c | 55 ++++++++++++++++++++++++++-------------------------- 3 files changed, 47 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index baa80e61..4989a0ba 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -985,6 +985,24 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { /* Cache full. We neet to evict one or more to map one. Get a random one which is not in use */ + if (unlikely(afl->q_testcase_cache_size + len >= afl->q_testcase_max_cache_size && + (afl->q_testcase_cache_count < afl->q_testcase_max_cache_entries && + afl->q_testcase_max_cache_count < + afl->q_testcase_max_cache_entries))) { + + if (afl->q_testcase_max_cache_count > afl->q_testcase_cache_count) { + + afl->q_testcase_max_cache_entries = + afl->q_testcase_max_cache_count + 1; + + } else { + + afl->q_testcase_max_cache_entries = afl->q_testcase_cache_count + 1; + + } + + } + do { // if the cache (MB) is not enough for the queue then this gets diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index ae7d410b..3ce16cad 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -104,7 +104,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->skip_deterministic = 1; afl->use_splicing = 1; afl->q_testcase_max_cache_size = TESTCASE_CACHE_SIZE * 1048576UL; - afl->q_testcase_max_cache_entries = 4096; + afl->q_testcase_max_cache_entries = 64 * 1024; #ifdef HAVE_AFFINITY afl->cpu_aff = -1; /* Selected CPU core */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 637e1985..70e21c0f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -251,7 +251,7 @@ static int stricmp(char const *a, char const *b) { int main(int argc, char **argv_orig, char **envp) { - s32 opt, i, auto_sync = 0, user_set_cache = 0; + s32 opt, i, auto_sync = 0 /*, user_set_cache = 0*/; u64 prev_queued = 0; u32 sync_interval_cnt = 0, seek_to = 0, show_help = 0, map_size = MAP_SIZE; u8 *extras_dir[4]; @@ -1020,7 +1020,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->q_testcase_max_cache_entries = (u32)atoi(afl->afl_env.afl_testcache_entries); - user_set_cache = 1; + // user_set_cache = 1; } @@ -1363,46 +1363,47 @@ int main(int argc, char **argv_orig, char **envp) { perform_dry_run(afl); - if (!user_set_cache && afl->q_testcase_max_cache_size) { + /* + if (!user_set_cache && afl->q_testcase_max_cache_size) { - /* The user defined not a fixed number of entries for the cache. - Hence we autodetect a good value. After the dry run inputs are - trimmed and we know the average and max size of the input seeds. - We use this information to set a fitting size to max entries - based on the cache size. */ + / * The user defined not a fixed number of entries for the cache. + Hence we autodetect a good value. After the dry run inputs are + trimmed and we know the average and max size of the input seeds. + We use this information to set a fitting size to max entries + based on the cache size. * / - struct queue_entry *q = afl->queue; - u64 size = 0, count = 0, avg = 0, max = 0; + struct queue_entry *q = afl->queue; + u64 size = 0, count = 0, avg = 0, max = 0; - while (q) { + while (q) { - ++count; - size += q->len; - if (max < q->len) { max = q->len; } - q = q->next; + ++count; + size += q->len; + if (max < q->len) { max = q->len; } + q = q->next; - } + } - if (count) { + if (count) { - avg = size / count; - avg = ((avg + max) / 2) + 1; + avg = size / count; + avg = ((avg + max) / 2) + 1; - } + } - if (avg < 10240) { avg = 10240; } + if (avg < 10240) { avg = 10240; } - afl->q_testcase_max_cache_entries = afl->q_testcase_max_cache_size / avg; + afl->q_testcase_max_cache_entries = afl->q_testcase_max_cache_size / avg; - if (afl->q_testcase_max_cache_entries > 32768) - afl->q_testcase_max_cache_entries = 32768; + if (afl->q_testcase_max_cache_entries > 32768) + afl->q_testcase_max_cache_entries = 32768; - } + } + + */ if (afl->q_testcase_max_cache_entries) { - OKF("Setting %u maximum entries for the testcase cache", - afl->q_testcase_max_cache_entries); afl->q_testcase_cache = ck_alloc(afl->q_testcase_max_cache_entries * sizeof(size_t)); if (!afl->q_testcase_cache) { PFATAL("malloc failed for cache entries"); } -- cgit 1.4.1 From 2e8ec1e33943c5067637361a8c182d13412a307c Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Fri, 23 Oct 2020 22:49:20 +0200 Subject: allow symbolize=1 for asan/debug --- GNUmakefile | 2 +- include/afl-fuzz.h | 2 +- src/afl-fuzz-init.c | 8 ++++++-- src/afl-fuzz.c | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index 80b7b68b..c576ae67 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -247,7 +247,7 @@ ifeq "$(shell command -v svn >/dev/null && svn proplist . 2>/dev/null && echo 1 endif ifeq "$(shell echo 'int main() { return 0;}' | $(CC) $(CFLAGS) -fsanitize=address -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" - ASAN_CFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer + ASAN_CFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer -DASAN_BUILD ASAN_LDFLAGS=-fsanitize=address -fstack-protector-all -fno-omit-frame-pointer endif diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 11feb9f7..85b31795 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1057,7 +1057,7 @@ void check_crash_handling(void); void check_cpu_governor(afl_state_t *); void get_core_count(afl_state_t *); void fix_up_sync(afl_state_t *); -void check_asan_opts(void); +void check_asan_opts(afl_state_t *); void check_binary(afl_state_t *, u8 *); void fix_up_banner(afl_state_t *, u8 *); void check_if_tty(afl_state_t *); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 13e42e03..1bccff8f 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2338,10 +2338,12 @@ static void handle_resize(int sig) { /* Check ASAN options. */ -void check_asan_opts(void) { +void check_asan_opts(afl_state_t *afl) { u8 *x = get_afl_env("ASAN_OPTIONS"); + (void)(afl); + if (x) { if (!strstr(x, "abort_on_error=1")) { @@ -2350,11 +2352,13 @@ void check_asan_opts(void) { } - if (!strstr(x, "symbolize=0")) { +#ifndef ASAN_BUILD + if (!afl->debug && !strstr(x, "symbolize=0")) { FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); } +#endif } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 70e21c0f..22e6d577 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -878,7 +878,7 @@ int main(int argc, char **argv_orig, char **envp) { #endif setup_signal_handlers(); - check_asan_opts(); + check_asan_opts(afl); afl->power_name = power_names[afl->schedule]; -- cgit 1.4.1 From 4e99e3b36c8af6488fde10fac8cdb0797c95e02a Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 24 Oct 2020 03:16:22 +0200 Subject: initializing testcase_buf --- src/afl-fuzz-queue.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 4989a0ba..02e66a4e 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -325,6 +325,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { q->depth = afl->cur_depth + 1; q->passed_det = passed_det; q->trace_mini = NULL; + q->testcase_buf = NULL; if (q->depth > afl->max_depth) { afl->max_depth = q->depth; } -- cgit 1.4.1 From e5f30c690822d69b4d99117024ae2570b1572481 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 24 Oct 2020 16:28:46 +0200 Subject: fix testcache bug --- src/afl-fuzz-init.c | 1 + src/afl-fuzz-queue.c | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 1bccff8f..19a8d77b 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2358,6 +2358,7 @@ void check_asan_opts(afl_state_t *afl) { FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); } + #endif } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 02e66a4e..d107dbc8 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -914,20 +914,22 @@ inline void queue_testcase_retake_mem(afl_state_t *afl, struct queue_entry *q, if (likely(q->testcase_buf)) { + u32 is_same = in == q->testcase_buf; + if (likely(len != old_len)) { - afl->q_testcase_cache_size = afl->q_testcase_cache_size + len - old_len; - q->testcase_buf = realloc(q->testcase_buf, len); + u8 *ptr = realloc(q->testcase_buf, len); - if (unlikely(!q->testcase_buf)) { + if (likely(ptr)) { - PFATAL("Unable to malloc '%s' with len %d", q->fname, len); + q->testcase_buf = ptr; + afl->q_testcase_cache_size = afl->q_testcase_cache_size + len - old_len; } } - memcpy(q->testcase_buf, in, len); + if (unlikely(!is_same)) { memcpy(q->testcase_buf, in, len); } } @@ -986,10 +988,12 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { /* Cache full. We neet to evict one or more to map one. Get a random one which is not in use */ - if (unlikely(afl->q_testcase_cache_size + len >= afl->q_testcase_max_cache_size && - (afl->q_testcase_cache_count < afl->q_testcase_max_cache_entries && - afl->q_testcase_max_cache_count < - afl->q_testcase_max_cache_entries))) { + if (unlikely(afl->q_testcase_cache_size + len >= + afl->q_testcase_max_cache_size && + (afl->q_testcase_cache_count < + afl->q_testcase_max_cache_entries && + afl->q_testcase_max_cache_count < + afl->q_testcase_max_cache_entries))) { if (afl->q_testcase_max_cache_count > afl->q_testcase_cache_count) { -- cgit 1.4.1 From ca938e7c4e0c2829625a505732f14410b6299290 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 24 Oct 2020 19:11:41 +0200 Subject: asan_build for tmin and analyze --- src/afl-analyze.c | 5 ++++- src/afl-tmin.c | 5 ++++- test/test-basic.sh | 4 +--- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/afl-analyze.c b/src/afl-analyze.c index bfdd66e9..0d9ed423 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -743,12 +743,15 @@ static void set_up_environment(void) { } - if (!strstr(x, "symbolize=0")) { +#fndef ASAN_BUILD + if (!getenv("AFL_DEBUG") && !strstr(x, "symbolize=0")) { FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); } +#endif + } x = get_afl_env("MSAN_OPTIONS"); diff --git a/src/afl-tmin.c b/src/afl-tmin.c index e1d08054..efc12d4f 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -672,12 +672,15 @@ static void set_up_environment(afl_forkserver_t *fsrv) { } - if (!strstr(x, "symbolize=0")) { +#ifndef ASAN_BUILD + if (!getenv("AFL_DEBUG") && !strstr(x, "symbolize=0")) { FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); } +#endif + } x = get_afl_env("MSAN_OPTIONS"); diff --git a/test/test-basic.sh b/test/test-basic.sh index 86c6037d..0d16ebd1 100755 --- a/test/test-basic.sh +++ b/test/test-basic.sh @@ -102,10 +102,8 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc $ECHO "$GRAY[*] no bash available, cannot test afl-cmin.bash" } fi - ../afl-tmin -m ${MEM_LIMIT} -i in/in2 -o in2/in2 -- ./test-instr.plain #> /dev/null 2>&1 + ../afl-tmin -m ${MEM_LIMIT} -i in/in2 -o in2/in2 -- ./test-instr.plain > /dev/null 2>&1 SIZE=`ls -l in2/in2 2>/dev/null | awk '{print$5}'` - ls -l in2 - echo SIZE=$SIZE test "$SIZE" = 1 && $ECHO "$GREEN[+] afl-tmin correctly minimized the testcase" test "$SIZE" = 1 || { $ECHO "$RED[!] afl-tmin did incorrectly minimize the testcase to $SIZE" -- cgit 1.4.1 From 029d44a6eca8d11a4c545cfc46accedd63ccb8f2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 24 Oct 2020 19:20:27 +0200 Subject: asan_build for tmin and analyze --- src/afl-analyze.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-analyze.c b/src/afl-analyze.c index 0d9ed423..c8acebb3 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -743,7 +743,7 @@ static void set_up_environment(void) { } -#fndef ASAN_BUILD +#ifndef ASAN_BUILD if (!getenv("AFL_DEBUG") && !strstr(x, "symbolize=0")) { FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); -- cgit 1.4.1 From 44c65fa0a0eb0a0382d8b80fa0c8fd3bf25b687d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 26 Oct 2020 14:44:05 +0100 Subject: add no splicing compile option and print used compile options in afl-fuzz help --- GNUmakefile | 7 +++++++ README.md | 1 + docs/Changelog.md | 2 ++ src/afl-fuzz-state.c | 2 ++ src/afl-fuzz.c | 24 ++++++++++++++++++++++++ 5 files changed, 36 insertions(+) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index ce0e1247..c8d155e4 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -36,6 +36,10 @@ SH_PROGS = afl-plot afl-cmin afl-cmin.bash afl-whatsup afl-system-config MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8) afl-as.8 ASAN_OPTIONS=detect_leaks=0 +ifdef NO_SPLICING + override CFLAGS += -DNO_SPLICING +endif + ifdef ASAN_BUILD $(info Compiling ASAN version of binaries) override CFLAGS+=$(ASAN_CFLAGS) @@ -344,7 +348,10 @@ help: @echo ASAN_BUILD - compiles with memory sanitizer for debug purposes @echo DEBUG - no optimization, -ggdb3, all warnings and -Werror @echo PROFILING - compile afl-fuzz with profiling information + @echo NO_PYTHON - disable python support + @echo NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing @echo AFL_NO_X86 - if compiling on non-intel/amd platforms + @echo "LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g. Debian)" @echo "==========================================" @echo e.g.: make ASAN_BUILD=1 diff --git a/README.md b/README.md index eac8b677..f09d9163 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,7 @@ These build options exist: * DEBUG - no optimization, -ggdb3, all warnings and -Werror * PROFILING - compile with profiling information (gprof) * NO_PYTHON - disable python support +* NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing * AFL_NO_X86 - if compiling on non-intel/amd platforms * LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g. Debian) diff --git a/docs/Changelog.md b/docs/Changelog.md index 36022399..f8f15fc8 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -34,6 +34,8 @@ sending a mail to . - crashing seeds are now not prohibiting a run anymore but are skipped. They are used for splicing though. - set the default power schedule to the superiour "seek" schedule + - added NO_SPLICING compile option and makefile define + - print special compile time options used in help output - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 3ce16cad..61bd06b7 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -102,7 +102,9 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->stats_update_freq = 1; afl->stats_avg_exec = -1; afl->skip_deterministic = 1; +#ifndef NO_SPLICING afl->use_splicing = 1; +#endif afl->q_testcase_max_cache_size = TESTCASE_CACHE_SIZE * 1048576UL; afl->q_testcase_max_cache_entries = 64 * 1024; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 22e6d577..cad26841 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -224,6 +224,26 @@ static void usage(u8 *argv0, int more_help) { SAYF("Compiled without python module support\n"); #endif +#ifdef ASAN_BUILD + SAYF("Compiled with ASAN_BUILD\n\n"); +#endif + +#ifdef NO_SPLICING + SAYF("Compiled with NO_SPLICING\n\n"); +#endif + +#ifdef PROFILING + SAYF("Compiled with PROFILING\n\n"); +#endif + +#ifdef _DEBUG + SAYF("Compiled with _DEBUG\n\n"); +#endif + +#ifdef _AFL_DOCUMENT_MUTATIONS + SAYF("Compiled with _AFL_DOCUMENT_MUTATIONS\n\n"); +#endif + SAYF("For additional help please consult %s/README.md\n\n", doc_path); exit(1); @@ -1527,7 +1547,11 @@ int main(int argc, char **argv_orig, char **envp) { } else { + #ifndef NO_SPLICING afl->use_splicing = 1; + #else + afl->use_splicing = 0; + #endif } -- cgit 1.4.1 From 868cb61ea6a2949e80e8a94fe7b19229bebecd10 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 26 Oct 2020 15:24:33 +0100 Subject: hopeful the final testcache improvement ... --- src/afl-fuzz-queue.c | 56 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index d107dbc8..c78df8be 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -979,21 +979,23 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { if (unlikely(!q->testcase_buf)) { /* Buf not cached, let's load it */ - u32 tid = afl->q_testcase_max_cache_count; + u32 tid = afl->q_testcase_max_cache_count; + static u32 do_once = 0; // because even threaded we would want this. WIP while (unlikely( afl->q_testcase_cache_size + len >= afl->q_testcase_max_cache_size || afl->q_testcase_cache_count >= afl->q_testcase_max_cache_entries - 1)) { - /* Cache full. We neet to evict one or more to map one. - Get a random one which is not in use */ + /* We want a max number of entries to the cache that we learn. + Very simple: once the cache is filled by size - that is the max. */ if (unlikely(afl->q_testcase_cache_size + len >= afl->q_testcase_max_cache_size && (afl->q_testcase_cache_count < afl->q_testcase_max_cache_entries && afl->q_testcase_max_cache_count < - afl->q_testcase_max_cache_entries))) { + afl->q_testcase_max_cache_entries) && + !do_once)) { if (afl->q_testcase_max_cache_count > afl->q_testcase_cache_count) { @@ -1006,8 +1008,19 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { } + do_once = 1; + // release unneeded memory + u8 *ptr = ck_realloc( + afl->q_testcase_cache, + (afl->q_testcase_max_cache_entries + 1) * sizeof(size_t)); + + if (ptr) { afl->q_testcase_cache = (struct queue_entry **)ptr; } + } + /* Cache full. We neet to evict one or more to map one. + Get a random one which is not in use */ + do { // if the cache (MB) is not enough for the queue then this gets @@ -1065,11 +1078,16 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { afl->q_testcase_cache[tid] = q; afl->q_testcase_cache_size += len; ++afl->q_testcase_cache_count; - if (tid >= afl->q_testcase_max_cache_count) + if (likely(tid >= afl->q_testcase_max_cache_count)) { + afl->q_testcase_max_cache_count = tid + 1; - if (tid == afl->q_testcase_smallest_free) + + } else if (unlikely(tid == afl->q_testcase_smallest_free)) { + afl->q_testcase_smallest_free = tid + 1; + } + } return q->testcase_buf; @@ -1093,9 +1111,21 @@ inline void queue_testcase_store_mem(afl_state_t *afl, struct queue_entry *q, } - u32 tid = 0; + u32 tid; + + if (unlikely(afl->q_testcase_max_cache_count >= + afl->q_testcase_max_cache_entries)) { - while (likely(afl->q_testcase_cache[tid] != NULL)) + // uh we were full, so now we have to search from start + tid = afl->q_testcase_smallest_free; + + } else { + + tid = afl->q_testcase_max_cache_count; + + } + + while (unlikely(afl->q_testcase_cache[tid] != NULL)) ++tid; /* Map the test case into memory. */ @@ -1114,8 +1144,16 @@ inline void queue_testcase_store_mem(afl_state_t *afl, struct queue_entry *q, afl->q_testcase_cache[tid] = q; afl->q_testcase_cache_size += len; ++afl->q_testcase_cache_count; - if (tid >= afl->q_testcase_max_cache_count) + + if (likely(tid >= afl->q_testcase_max_cache_count)) { + afl->q_testcase_max_cache_count = tid + 1; + } else if (unlikely(tid == afl->q_testcase_smallest_free)) { + + afl->q_testcase_smallest_free = tid + 1; + + } + } -- cgit 1.4.1 From 0b9b4adbd3955102902823d8f07f4d155d954b8b Mon Sep 17 00:00:00 2001 From: Ruben ten Hove Date: Wed, 28 Oct 2020 14:03:01 +0100 Subject: show supplied -t option so we know it's used --- src/afl-fuzz-stats.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index bec90519..b4b2f747 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -1152,6 +1152,10 @@ void show_init_stats(afl_state_t *afl) { ACTF("Applying timeout settings from resumed session (%u ms).", afl->fsrv.exec_tmout); + } else { + + OKF("-t option specified. We'll use an exec timeout of %s ms.", fsrv->exec_tmout); + } /* In non-instrumented mode, re-running every timing out test case with a -- cgit 1.4.1 From fe705bb9567341427ce1ea39d5fc6b19fdee1646 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 28 Oct 2020 14:32:53 +0100 Subject: expand havoc if not new findings in the last 5 seconds --- src/afl-fuzz-one.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index bf568c38..2e186b90 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1884,16 +1884,22 @@ havoc_stage: u32 r_max, r; + r_max = 15 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0); + if (unlikely(afl->expand_havoc)) { /* add expensive havoc cases here, they are activated after a full cycle without finds happened */ - r_max = 16 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0); + r_max += 1; - } else { + } + + if (unlikely(get_cur_time() - afl->last_path_time > 5000)) { + + /* add expensive havoc cases here if there is no findings in the last 5s */ - r_max = 15 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0); + r_max += 1; } -- cgit 1.4.1 From 587f66f1b341e9ad42655bc74843a2ed1d93ee09 Mon Sep 17 00:00:00 2001 From: Ruben ten Hove Date: Wed, 28 Oct 2020 15:42:07 +0100 Subject: whoops.. --- src/afl-fuzz-stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index b4b2f747..6841eb88 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -1154,7 +1154,7 @@ void show_init_stats(afl_state_t *afl) { } else { - OKF("-t option specified. We'll use an exec timeout of %s ms.", fsrv->exec_tmout); + OKF("-t option specified. We'll use an exec timeout of %s ms.", afl->fsrv.exec_tmout); } -- cgit 1.4.1 From b5686eb63e1fcd6dac49cc458c50e52b51709f8c Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 29 Oct 2020 00:05:28 +0100 Subject: fixes two huge bugs --- src/afl-fuzz-one.c | 5 +++-- src/afl-fuzz-run.c | 3 ++- src/afl-fuzz-stats.c | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 35ff5466..0f3393d2 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1850,8 +1850,9 @@ havoc_stage: r_max += 1; } - - if (unlikely(get_cur_time() - afl->last_path_time > 5000)) { + + if (unlikely(get_cur_time() - afl->last_path_time > 5000 && + afl->ready_for_splicing_count > 1)) { /* add expensive havoc cases here if there is no findings in the last 5s */ diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index dfd3abfb..fb259b5d 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -587,9 +587,10 @@ void sync_fuzzers(afl_state_t *afl) { u8 entry[12]; sprintf(entry, "id:%06u", next_min_accept); + while (m < n) { - if (memcmp(namelist[m]->d_name, entry, 9)) { + if (strcmp(namelist[m]->d_name, entry)) { m++; diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 6841eb88..321bbb35 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -1154,7 +1154,8 @@ void show_init_stats(afl_state_t *afl) { } else { - OKF("-t option specified. We'll use an exec timeout of %s ms.", afl->fsrv.exec_tmout); + ACTF("-t option specified. We'll use an exec timeout of %d ms.", + afl->fsrv.exec_tmout); } -- cgit 1.4.1 From abac876b3aa20d381319d73cbb6c7ad1e7f2395c Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 29 Oct 2020 10:45:32 +0100 Subject: better warn if skipping large dict --- instrumentation/split-compares-pass.so.cc | 8 ++++---- src/afl-fuzz-extras.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc index 6d0c52a4..3f05dd97 100644 --- a/instrumentation/split-compares-pass.so.cc +++ b/instrumentation/split-compares-pass.so.cc @@ -1251,7 +1251,7 @@ size_t SplitComparesTransform::splitIntCompares(Module &M, unsigned bitw) { bool SplitComparesTransform::runOnModule(Module &M) { int bitw = 64; - size_t count; + size_t count = 0; char *bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW"); if (!bitw_env) bitw_env = getenv("LAF_SPLIT_COMPARES_BITW"); @@ -1296,7 +1296,7 @@ bool SplitComparesTransform::runOnModule(Module &M) { switch (bitw) { case 64: - count = splitIntCompares(M, bitw); + count += splitIntCompares(M, bitw); /* if (!be_quiet) errs() << "Split-integer-compare-pass " << bitw << "bit: " << @@ -1309,7 +1309,7 @@ bool SplitComparesTransform::runOnModule(Module &M) { [[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */ #endif case 32: - count = splitIntCompares(M, bitw); + count += splitIntCompares(M, bitw); /* if (!be_quiet) errs() << "Split-integer-compare-pass " << bitw << "bit: " << @@ -1322,7 +1322,7 @@ bool SplitComparesTransform::runOnModule(Module &M) { [[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */ #endif case 16: - count = splitIntCompares(M, bitw); + count += splitIntCompares(M, bitw); /* if (!be_quiet) errs() << "Split-integer-compare-pass " << bitw << "bit: " << diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 58ce5b6f..88a4fe67 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -451,8 +451,8 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { if (len > MAX_DICT_FILE) { - WARNF("Extra '%.*s' is too big (%s, limit is %s)", (int)len, mem, - stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), len), + WARNF("Extra '%.*s' is too big (%s, limit is %s), skipping file!", (int)len, + mem, stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), len), stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); return; -- cgit 1.4.1 From 9347ad49b8adb867c9829c6c03e574ce26bc0942 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Thu, 29 Oct 2020 11:05:07 +0100 Subject: Don't crash for unset out_file (fixed #562) --- src/afl-forkserver.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index df300950..04195d00 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -968,7 +968,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { s32 fd = fsrv->out_fd; - if (!fsrv->use_stdin) { + if (!fsrv->use_stdin && fsrv->out_file) { if (fsrv->no_unlink) { @@ -983,6 +983,11 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { if (fd < 0) { PFATAL("Unable to create '%s'", fsrv->out_file); } + } else if (unlikely(!fd)) { + + // We should never have stdin as fd here, 0 is likely unset. + FATAL("Nowhere to write output to (neither out_fd nor out_file set)"); + } else { lseek(fd, 0, SEEK_SET); -- cgit 1.4.1 From a2739ef5ff2cbb16d23d3d9c0ee1b13b892c1f72 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 30 Oct 2020 09:40:51 +0100 Subject: extras: afl_realloc -> ck_realloc --- src/afl-forkserver.c | 2 +- src/afl-fuzz-extras.c | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 04195d00..d23cf6eb 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -984,7 +984,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { if (fd < 0) { PFATAL("Unable to create '%s'", fsrv->out_file); } } else if (unlikely(!fd)) { - + // We should never have stdin as fd here, 0 is likely unset. FATAL("Nowhere to write output to (neither out_fd nor out_file set)"); diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 88a4fe67..4fb48860 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -423,8 +423,8 @@ void dedup_extras(afl_state_t *afl) { } if (afl->extras_cnt != orig_cnt) - afl->extras = afl_realloc((void **)&afl->extras, - afl->extras_cnt * sizeof(struct extra_data)); + afl->extras = ck_realloc((void **)&afl->extras, + afl->extras_cnt * sizeof(struct extra_data)); } @@ -462,8 +462,18 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { } - afl->extras = afl_realloc((void **)&afl->extras, - (afl->extras_cnt + 1) * sizeof(struct extra_data)); + if (afl->extra) { + + afl->extras = ck_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); + + } else { + + afl->extras = ck_alloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); + + } + if (unlikely(!afl->extras)) { PFATAL("alloc"); } afl->extras[afl->extras_cnt].data = ck_alloc(len); -- cgit 1.4.1 From b33306ca2cb823e40769dda733542063dce0658f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 30 Oct 2020 09:43:03 +0100 Subject: fix ck_alloc call --- src/afl-fuzz-extras.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 4fb48860..5beceac9 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -469,8 +469,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { } else { - afl->extras = ck_alloc((void **)&afl->extras, - (afl->extras_cnt + 1) * sizeof(struct extra_data)); + afl->extras = ck_alloc((afl->extras_cnt + 1) * sizeof(struct extra_data)); } -- cgit 1.4.1 From e0bdfd87b62ea7f30d9ae7fa4a838004a3983f83 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 30 Oct 2020 09:44:06 +0100 Subject: fix ck_alloc call --- src/afl-fuzz-extras.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 5beceac9..adec986e 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -462,7 +462,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { } - if (afl->extra) { + if (afl->extras) { afl->extras = ck_realloc((void **)&afl->extras, (afl->extras_cnt + 1) * sizeof(struct extra_data)); -- cgit 1.4.1 From f810639ab188bf3bbd27fe58fb0b0bb2fe4fbdf0 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 31 Oct 2020 14:18:58 +0100 Subject: add our own inline trace-pc-guard --- GNUmakefile.llvm | 5 +- docs/Changelog.md | 3 + instrumentation/SanitizerCoveragePCGUARD.so.cc | 1331 ++++++++++++++++++++++++ src/afl-cc.c | 15 +- 4 files changed, 1351 insertions(+), 3 deletions(-) create mode 100644 instrumentation/SanitizerCoveragePCGUARD.so.cc (limited to 'src') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 1f67ea7f..3605b425 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -297,7 +297,7 @@ ifeq "$(TEST_MMAP)" "1" endif PROGS_ALWAYS = ./afl-cc ./afl-compiler-rt.o ./afl-compiler-rt-32.o ./afl-compiler-rt-64.o -PROGS = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./libLLVMInsTrim.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./afl-llvm-lto-instrumentation.so ./SanitizerCoverageLTO.so +PROGS = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./SanitizerCoveragePCGUARD.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./libLLVMInsTrim.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./afl-llvm-lto-instrumentation.so ./SanitizerCoverageLTO.so # If prerequisites are not given, warn, do not build anything, and exit with code 0 ifeq "$(LLVMVER)" "" @@ -382,6 +382,9 @@ ifeq "$(LLVM_MIN_4_0_1)" "0" endif $(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o +./SanitizerCoveragePCGUARD.so: instrumentation/SanitizerCoveragePCGUARD.so.cc instrumentation/afl-llvm-common.o | test_deps + $(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o + ./afl-llvm-lto-instrumentlist.so: instrumentation/afl-llvm-lto-instrumentlist.so.cc instrumentation/afl-llvm-common.o ifeq "$(LLVM_LTO)" "1" $(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o diff --git a/docs/Changelog.md b/docs/Changelog.md index f8f15fc8..798a056f 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -40,6 +40,9 @@ sending a mail to . - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! - not overriding -Ox or -fno-unroll-loops anymore + - we now have our own trace-pc-guard implementation. It is the same as + -fsanitize-coverage=trace-pc-guard from llvm 12, but: it is a) inline + and b) works from llvm 10+ on :) - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz -x dictionary of string comparisons found during compilation - LTO autodict now also collects interesting cmp comparisons, diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc new file mode 100644 index 00000000..97e8d32b --- /dev/null +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -0,0 +1,1331 @@ +//===-- SanitizerCoverage.cpp - coverage instrumentation for sanitizers ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Coverage instrumentation done on LLVM IR level, works with Sanitizers. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Support/SpecialCaseList.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Analysis/EHPersonalities.h" +#include "llvm/Analysis/PostDominators.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Mangler.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/SpecialCaseList.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +#include "config.h" +#include "debug.h" +#include "afl-llvm-common.h" + +namespace llvm { + +/// This is the ModuleSanitizerCoverage pass used in the new pass manager. The +/// pass instruments functions for coverage, adds initialization calls to the +/// module for trace PC guards and 8bit counters if they are requested, and +/// appends globals to llvm.compiler.used. +class ModuleSanitizerCoveragePass + : public PassInfoMixin { + + public: + explicit ModuleSanitizerCoveragePass( + SanitizerCoverageOptions Options = SanitizerCoverageOptions(), + const std::vector &AllowlistFiles = + std::vector(), + const std::vector &BlocklistFiles = + std::vector()) + : Options(Options) { + + if (AllowlistFiles.size() > 0) + Allowlist = SpecialCaseList::createOrDie(AllowlistFiles, + *vfs::getRealFileSystem()); + if (BlocklistFiles.size() > 0) + Blocklist = SpecialCaseList::createOrDie(BlocklistFiles, + *vfs::getRealFileSystem()); + + } + + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + static bool isRequired() { + + return true; + + } + + private: + SanitizerCoverageOptions Options; + + std::unique_ptr Allowlist; + std::unique_ptr Blocklist; + +}; + +// Insert SanitizerCoverage instrumentation. +ModulePass *createModuleSanitizerCoverageLegacyPassPass( + const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(), + const std::vector &AllowlistFiles = std::vector(), + const std::vector &BlocklistFiles = + std::vector()); + +} // namespace llvm + +using namespace llvm; + +#define DEBUG_TYPE "sancov" + +static const char *const SanCovTracePCIndirName = + "__sanitizer_cov_trace_pc_indir"; +static const char *const SanCovTracePCName = "__sanitizer_cov_trace_pc"; +static const char *const SanCovTraceCmp1 = "__sanitizer_cov_trace_cmp1"; +static const char *const SanCovTraceCmp2 = "__sanitizer_cov_trace_cmp2"; +static const char *const SanCovTraceCmp4 = "__sanitizer_cov_trace_cmp4"; +static const char *const SanCovTraceCmp8 = "__sanitizer_cov_trace_cmp8"; +static const char *const SanCovTraceConstCmp1 = + "__sanitizer_cov_trace_const_cmp1"; +static const char *const SanCovTraceConstCmp2 = + "__sanitizer_cov_trace_const_cmp2"; +static const char *const SanCovTraceConstCmp4 = + "__sanitizer_cov_trace_const_cmp4"; +static const char *const SanCovTraceConstCmp8 = + "__sanitizer_cov_trace_const_cmp8"; +static const char *const SanCovTraceDiv4 = "__sanitizer_cov_trace_div4"; +static const char *const SanCovTraceDiv8 = "__sanitizer_cov_trace_div8"; +static const char *const SanCovTraceGep = "__sanitizer_cov_trace_gep"; +static const char *const SanCovTraceSwitchName = "__sanitizer_cov_trace_switch"; +static const char *const SanCovModuleCtorTracePcGuardName = + "sancov.module_ctor_trace_pc_guard"; +static const char *const SanCovModuleCtor8bitCountersName = + "sancov.module_ctor_8bit_counters"; +static const char *const SanCovModuleCtorBoolFlagName = + "sancov.module_ctor_bool_flag"; +static const uint64_t SanCtorAndDtorPriority = 2; + +static const char *const SanCovTracePCGuardName = + "__sanitizer_cov_trace_pc_guard"; +static const char *const SanCovTracePCGuardInitName = + "__sanitizer_cov_trace_pc_guard_init"; +static const char *const SanCov8bitCountersInitName = + "__sanitizer_cov_8bit_counters_init"; +static const char *const SanCovBoolFlagInitName = + "__sanitizer_cov_bool_flag_init"; +static const char *const SanCovPCsInitName = "__sanitizer_cov_pcs_init"; + +static const char *const SanCovGuardsSectionName = "sancov_guards"; +static const char *const SanCovCountersSectionName = "sancov_cntrs"; +static const char *const SanCovBoolFlagSectionName = "sancov_bools"; +static const char *const SanCovPCsSectionName = "sancov_pcs"; + +static const char *const SanCovLowestStackName = "__sancov_lowest_stack"; + +static char *skip_nozero; + +/* +static cl::opt ClCoverageLevel( + "sanitizer-coverage-level", + cl::desc("Sanitizer Coverage. 0: none, 1: entry block, 2: all blocks, " + "3: all blocks and critical edges"), + cl::Hidden, cl::init(3)); + +static cl::opt ClTracePC("sanitizer-coverage-trace-pc", + cl::desc("Experimental pc tracing"), cl::Hidden, + cl::init(false)); + +static cl::opt ClTracePCGuard("sanitizer-coverage-trace-pc-guard", + cl::desc("pc tracing with a guard"), + cl::Hidden, cl::init(true)); + +// If true, we create a global variable that contains PCs of all instrumented +// BBs, put this global into a named section, and pass this section's bounds +// to __sanitizer_cov_pcs_init. +// This way the coverage instrumentation does not need to acquire the PCs +// at run-time. Works with trace-pc-guard, inline-8bit-counters, and +// inline-bool-flag. +static cl::opt ClCreatePCTable("sanitizer-coverage-pc-table", + cl::desc("create a static PC table"), + cl::Hidden, cl::init(false)); + +static cl::opt ClInline8bitCounters( + "sanitizer-coverage-inline-8bit-counters", + cl::desc("increments 8-bit counter for every edge"), cl::Hidden, + cl::init(false)); + +static cl::opt ClInlineBoolFlag( + "sanitizer-coverage-inline-bool-flag", + cl::desc("sets a boolean flag for every edge"), cl::Hidden, + cl::init(false)); + +static cl::opt ClCMPTracing( + "sanitizer-coverage-trace-compares", + cl::desc("Tracing of CMP and similar instructions"), cl::Hidden, + cl::init(false)); + +static cl::opt ClDIVTracing("sanitizer-coverage-trace-divs", + cl::desc("Tracing of DIV instructions"), + cl::Hidden, cl::init(false)); + +static cl::opt ClGEPTracing("sanitizer-coverage-trace-geps", + cl::desc("Tracing of GEP instructions"), + cl::Hidden, cl::init(false)); + +static cl::opt ClPruneBlocks( + "sanitizer-coverage-prune-blocks", + cl::desc("Reduce the number of instrumented blocks"), cl::Hidden, + cl::init(true)); + +static cl::opt ClStackDepth("sanitizer-coverage-stack-depth", + cl::desc("max stack depth tracing"), + cl::Hidden, cl::init(false)); +*/ +namespace { + +/* +SanitizerCoverageOptions getOptions(int LegacyCoverageLevel) { + + SanitizerCoverageOptions Res; + switch (LegacyCoverageLevel) { + + case 0: + Res.CoverageType = SanitizerCoverageOptions::SCK_None; + break; + case 1: + Res.CoverageType = SanitizerCoverageOptions::SCK_Function; + break; + case 2: + Res.CoverageType = SanitizerCoverageOptions::SCK_BB; + break; + case 3: + Res.CoverageType = SanitizerCoverageOptions::SCK_Edge; + break; + case 4: + Res.CoverageType = SanitizerCoverageOptions::SCK_Edge; + Res.IndirectCalls = true; + break; + + } + + return Res; + +} + +*/ + +SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) { + + // Sets CoverageType and IndirectCalls. + // SanitizerCoverageOptions CLOpts = getOptions(ClCoverageLevel); + Options.CoverageType = + SanitizerCoverageOptions::SCK_Edge; // std::max(Options.CoverageType, + // CLOpts.CoverageType); + Options.IndirectCalls = true; // CLOpts.IndirectCalls; + Options.TraceCmp = false; //|= ClCMPTracing; + Options.TraceDiv = false; //|= ClDIVTracing; + Options.TraceGep = false; //|= ClGEPTracing; + Options.TracePC = false; //|= ClTracePC; + Options.TracePCGuard = true; // |= ClTracePCGuard; + Options.Inline8bitCounters = 0; //|= ClInline8bitCounters; + // Options.InlineBoolFlag = 0; //|= ClInlineBoolFlag; + Options.PCTable = false; //|= ClCreatePCTable; + Options.NoPrune = false; //|= !ClPruneBlocks; + Options.StackDepth = false; //|= ClStackDepth; + if (!Options.TracePCGuard && !Options.TracePC && + !Options.Inline8bitCounters && !Options.StackDepth /*&& + !Options.InlineBoolFlag*/) + Options.TracePCGuard = true; // TracePCGuard is default. + + return Options; + +} + +using DomTreeCallback = function_ref; +using PostDomTreeCallback = + function_ref; + +class ModuleSanitizerCoverage { + + public: + ModuleSanitizerCoverage( + const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(), + const SpecialCaseList * Allowlist = nullptr, + const SpecialCaseList * Blocklist = nullptr) + : Options(OverrideFromCL(Options)), + Allowlist(Allowlist), + Blocklist(Blocklist) { + + } + + bool instrumentModule(Module &M, DomTreeCallback DTCallback, + PostDomTreeCallback PDTCallback); + + private: + void instrumentFunction(Function &F, DomTreeCallback DTCallback, + PostDomTreeCallback PDTCallback); + void InjectCoverageForIndirectCalls(Function & F, + ArrayRef IndirCalls); + void InjectTraceForCmp(Function &F, ArrayRef CmpTraceTargets); + void InjectTraceForDiv(Function & F, + ArrayRef DivTraceTargets); + void InjectTraceForGep(Function & F, + ArrayRef GepTraceTargets); + void InjectTraceForSwitch(Function & F, + ArrayRef SwitchTraceTargets); + bool InjectCoverage(Function &F, ArrayRef AllBlocks, + bool IsLeafFunc = true); + GlobalVariable *CreateFunctionLocalArrayInSection(size_t NumElements, + Function &F, Type *Ty, + const char *Section); + GlobalVariable *CreatePCArray(Function &F, ArrayRef AllBlocks); + void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks); + void InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx, + bool IsLeafFunc = true); + Function *CreateInitCallsForSections(Module &M, const char *CtorName, + const char *InitFunctionName, Type *Ty, + const char *Section); + std::pair CreateSecStartEnd(Module &M, const char *Section, + Type *Ty); + + void SetNoSanitizeMetadata(Instruction *I) { + + I->setMetadata(I->getModule()->getMDKindID("nosanitize"), + MDNode::get(*C, None)); + + } + + std::string getSectionName(const std::string &Section) const; + std::string getSectionStart(const std::string &Section) const; + std::string getSectionEnd(const std::string &Section) const; + FunctionCallee SanCovTracePCIndir; + FunctionCallee SanCovTracePC, SanCovTracePCGuard; + FunctionCallee SanCovTraceCmpFunction[4]; + FunctionCallee SanCovTraceConstCmpFunction[4]; + FunctionCallee SanCovTraceDivFunction[2]; + FunctionCallee SanCovTraceGepFunction; + FunctionCallee SanCovTraceSwitchFunction; + GlobalVariable *SanCovLowestStack; + Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy, + *Int16Ty, *Int8Ty, *Int8PtrTy, *Int1Ty, *Int1PtrTy; + Module * CurModule; + std::string CurModuleUniqueId; + Triple TargetTriple; + LLVMContext * C; + const DataLayout *DL; + + GlobalVariable *FunctionGuardArray; // for trace-pc-guard. + GlobalVariable *Function8bitCounterArray; // for inline-8bit-counters. + GlobalVariable *FunctionBoolArray; // for inline-bool-flag. + GlobalVariable *FunctionPCsArray; // for pc-table. + SmallVector GlobalsToAppendToUsed; + SmallVector GlobalsToAppendToCompilerUsed; + + SanitizerCoverageOptions Options; + + const SpecialCaseList *Allowlist; + const SpecialCaseList *Blocklist; + + uint32_t instr = 0; + GlobalVariable *AFLMapPtr = NULL; + ConstantInt * One = NULL; + ConstantInt * Zero = NULL; + +}; + +class ModuleSanitizerCoverageLegacyPass : public ModulePass { + + public: + ModuleSanitizerCoverageLegacyPass( + const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(), + const std::vector &AllowlistFiles = + std::vector(), + const std::vector &BlocklistFiles = + std::vector()) + : ModulePass(ID), Options(Options) { + + if (AllowlistFiles.size() > 0) + Allowlist = SpecialCaseList::createOrDie(AllowlistFiles, + *vfs::getRealFileSystem()); + if (BlocklistFiles.size() > 0) + Blocklist = SpecialCaseList::createOrDie(BlocklistFiles, + *vfs::getRealFileSystem()); + initializeModuleSanitizerCoverageLegacyPassPass( + *PassRegistry::getPassRegistry()); + + } + + bool runOnModule(Module &M) override { + + ModuleSanitizerCoverage ModuleSancov(Options, Allowlist.get(), + Blocklist.get()); + auto DTCallback = [this](Function &F) -> const DominatorTree * { + + return &this->getAnalysis(F).getDomTree(); + + }; + + auto PDTCallback = [this](Function &F) -> const PostDominatorTree * { + + return &this->getAnalysis(F) + .getPostDomTree(); + + }; + + return ModuleSancov.instrumentModule(M, DTCallback, PDTCallback); + + } + + static char ID; // Pass identification, replacement for typeid + StringRef getPassName() const override { + + return "ModuleSanitizerCoverage"; + + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + + AU.addRequired(); + AU.addRequired(); + + } + + private: + SanitizerCoverageOptions Options; + + std::unique_ptr Allowlist; + std::unique_ptr Blocklist; + +}; + +} // namespace + +PreservedAnalyses ModuleSanitizerCoveragePass::run(Module & M, + ModuleAnalysisManager &MAM) { + + ModuleSanitizerCoverage ModuleSancov(Options, Allowlist.get(), + Blocklist.get()); + auto &FAM = MAM.getResult(M).getManager(); + auto DTCallback = [&FAM](Function &F) -> const DominatorTree * { + + return &FAM.getResult(F); + + }; + + auto PDTCallback = [&FAM](Function &F) -> const PostDominatorTree * { + + return &FAM.getResult(F); + + }; + + if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback)) + return PreservedAnalyses::none(); + return PreservedAnalyses::all(); + +} + +std::pair ModuleSanitizerCoverage::CreateSecStartEnd( + Module &M, const char *Section, Type *Ty) { + + GlobalVariable *SecStart = new GlobalVariable( + M, Ty->getPointerElementType(), false, GlobalVariable::ExternalLinkage, + nullptr, getSectionStart(Section)); + SecStart->setVisibility(GlobalValue::HiddenVisibility); + GlobalVariable *SecEnd = new GlobalVariable( + M, Ty->getPointerElementType(), false, GlobalVariable::ExternalLinkage, + nullptr, getSectionEnd(Section)); + SecEnd->setVisibility(GlobalValue::HiddenVisibility); + IRBuilder<> IRB(M.getContext()); + if (!TargetTriple.isOSBinFormatCOFF()) + return std::make_pair(SecStart, SecEnd); + + // Account for the fact that on windows-msvc __start_* symbols actually + // point to a uint64_t before the start of the array. + auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy); + auto GEP = IRB.CreateGEP(Int8Ty, SecStartI8Ptr, + ConstantInt::get(IntptrTy, sizeof(uint64_t))); + return std::make_pair(IRB.CreatePointerCast(GEP, Ty), SecEnd); + +} + +Function *ModuleSanitizerCoverage::CreateInitCallsForSections( + Module &M, const char *CtorName, const char *InitFunctionName, Type *Ty, + const char *Section) { + + auto SecStartEnd = CreateSecStartEnd(M, Section, Ty); + auto SecStart = SecStartEnd.first; + auto SecEnd = SecStartEnd.second; + Function *CtorFunc; + std::tie(CtorFunc, std::ignore) = createSanitizerCtorAndInitFunctions( + M, CtorName, InitFunctionName, {Ty, Ty}, {SecStart, SecEnd}); + assert(CtorFunc->getName() == CtorName); + + if (TargetTriple.supportsCOMDAT()) { + + // Use comdat to dedup CtorFunc. + CtorFunc->setComdat(M.getOrInsertComdat(CtorName)); + appendToGlobalCtors(M, CtorFunc, SanCtorAndDtorPriority, CtorFunc); + + } else { + + appendToGlobalCtors(M, CtorFunc, SanCtorAndDtorPriority); + + } + + if (TargetTriple.isOSBinFormatCOFF()) { + + // In COFF files, if the contructors are set as COMDAT (they are because + // COFF supports COMDAT) and the linker flag /OPT:REF (strip unreferenced + // functions and data) is used, the constructors get stripped. To prevent + // this, give the constructors weak ODR linkage and ensure the linker knows + // to include the sancov constructor. This way the linker can deduplicate + // the constructors but always leave one copy. + CtorFunc->setLinkage(GlobalValue::WeakODRLinkage); + appendToUsed(M, CtorFunc); + + } + + return CtorFunc; + +} + +bool ModuleSanitizerCoverage::instrumentModule( + Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { + + setvbuf(stdout, NULL, _IONBF, 0); + if (getenv("AFL_DEBUG")) debug = 1; + + if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { + + SAYF(cCYA "SanitizerCoveragePCGUARD" VERSION cRST "\n"); + + } else + + be_quiet = 1; + + skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); + // scanForDangerousFunctions(&M); + + if (debug) { + + fprintf(stderr, + "SANCOV: covtype:%u indirect:%d stack:%d noprune:%d " + "createtable:%d tracepcguard:%d tracepc:%d\n", + Options.CoverageType, Options.IndirectCalls == true ? 1 : 0, + Options.StackDepth == true ? 1 : 0, Options.NoPrune == true ? 1 : 0, + // Options.InlineBoolFlag == true ? 1 : 0, + Options.PCTable == true ? 1 : 0, + Options.TracePCGuard == true ? 1 : 0, + Options.TracePC == true ? 1 : 0); + + } + + if (Options.CoverageType == SanitizerCoverageOptions::SCK_None) return false; + if (Allowlist && + !Allowlist->inSection("coverage", "src", M.getSourceFileName())) + return false; + if (Blocklist && + Blocklist->inSection("coverage", "src", M.getSourceFileName())) + return false; + C = &(M.getContext()); + DL = &M.getDataLayout(); + CurModule = &M; + CurModuleUniqueId = getUniqueModuleId(CurModule); + TargetTriple = Triple(M.getTargetTriple()); + FunctionGuardArray = nullptr; + Function8bitCounterArray = nullptr; + FunctionBoolArray = nullptr; + FunctionPCsArray = nullptr; + IntptrTy = Type::getIntNTy(*C, DL->getPointerSizeInBits()); + IntptrPtrTy = PointerType::getUnqual(IntptrTy); + Type * VoidTy = Type::getVoidTy(*C); + IRBuilder<> IRB(*C); + Int64PtrTy = PointerType::getUnqual(IRB.getInt64Ty()); + Int32PtrTy = PointerType::getUnqual(IRB.getInt32Ty()); + Int8PtrTy = PointerType::getUnqual(IRB.getInt8Ty()); + Int1PtrTy = PointerType::getUnqual(IRB.getInt1Ty()); + Int64Ty = IRB.getInt64Ty(); + Int32Ty = IRB.getInt32Ty(); + Int16Ty = IRB.getInt16Ty(); + Int8Ty = IRB.getInt8Ty(); + Int1Ty = IRB.getInt1Ty(); + LLVMContext &Ctx = M.getContext(); + + AFLMapPtr = + new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, + GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); + One = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 1); + Zero = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 0); + + SanCovTracePCIndir = + M.getOrInsertFunction(SanCovTracePCIndirName, VoidTy, IntptrTy); + // Make sure smaller parameters are zero-extended to i64 if required by the + // target ABI. + AttributeList SanCovTraceCmpZeroExtAL; + SanCovTraceCmpZeroExtAL = + SanCovTraceCmpZeroExtAL.addParamAttribute(*C, 0, Attribute::ZExt); + SanCovTraceCmpZeroExtAL = + SanCovTraceCmpZeroExtAL.addParamAttribute(*C, 1, Attribute::ZExt); + + SanCovTraceCmpFunction[0] = + M.getOrInsertFunction(SanCovTraceCmp1, SanCovTraceCmpZeroExtAL, VoidTy, + IRB.getInt8Ty(), IRB.getInt8Ty()); + SanCovTraceCmpFunction[1] = + M.getOrInsertFunction(SanCovTraceCmp2, SanCovTraceCmpZeroExtAL, VoidTy, + IRB.getInt16Ty(), IRB.getInt16Ty()); + SanCovTraceCmpFunction[2] = + M.getOrInsertFunction(SanCovTraceCmp4, SanCovTraceCmpZeroExtAL, VoidTy, + IRB.getInt32Ty(), IRB.getInt32Ty()); + SanCovTraceCmpFunction[3] = + M.getOrInsertFunction(SanCovTraceCmp8, VoidTy, Int64Ty, Int64Ty); + + SanCovTraceConstCmpFunction[0] = M.getOrInsertFunction( + SanCovTraceConstCmp1, SanCovTraceCmpZeroExtAL, VoidTy, Int8Ty, Int8Ty); + SanCovTraceConstCmpFunction[1] = M.getOrInsertFunction( + SanCovTraceConstCmp2, SanCovTraceCmpZeroExtAL, VoidTy, Int16Ty, Int16Ty); + SanCovTraceConstCmpFunction[2] = M.getOrInsertFunction( + SanCovTraceConstCmp4, SanCovTraceCmpZeroExtAL, VoidTy, Int32Ty, Int32Ty); + SanCovTraceConstCmpFunction[3] = + M.getOrInsertFunction(SanCovTraceConstCmp8, VoidTy, Int64Ty, Int64Ty); + + { + + AttributeList AL; + AL = AL.addParamAttribute(*C, 0, Attribute::ZExt); + SanCovTraceDivFunction[0] = + M.getOrInsertFunction(SanCovTraceDiv4, AL, VoidTy, IRB.getInt32Ty()); + + } + + SanCovTraceDivFunction[1] = + M.getOrInsertFunction(SanCovTraceDiv8, VoidTy, Int64Ty); + SanCovTraceGepFunction = + M.getOrInsertFunction(SanCovTraceGep, VoidTy, IntptrTy); + SanCovTraceSwitchFunction = + M.getOrInsertFunction(SanCovTraceSwitchName, VoidTy, Int64Ty, Int64PtrTy); + + Constant *SanCovLowestStackConstant = + M.getOrInsertGlobal(SanCovLowestStackName, IntptrTy); + SanCovLowestStack = dyn_cast(SanCovLowestStackConstant); + if (!SanCovLowestStack) { + + C->emitError(StringRef("'") + SanCovLowestStackName + + "' should not be declared by the user"); + return true; + + } + + SanCovLowestStack->setThreadLocalMode( + GlobalValue::ThreadLocalMode::InitialExecTLSModel); + if (Options.StackDepth && !SanCovLowestStack->isDeclaration()) + SanCovLowestStack->setInitializer(Constant::getAllOnesValue(IntptrTy)); + + SanCovTracePC = M.getOrInsertFunction(SanCovTracePCName, VoidTy); + SanCovTracePCGuard = + M.getOrInsertFunction(SanCovTracePCGuardName, VoidTy, Int32PtrTy); + + for (auto &F : M) + instrumentFunction(F, DTCallback, PDTCallback); + + Function *Ctor = nullptr; + + if (FunctionGuardArray) + Ctor = CreateInitCallsForSections(M, SanCovModuleCtorTracePcGuardName, + SanCovTracePCGuardInitName, Int32PtrTy, + SanCovGuardsSectionName); + if (Function8bitCounterArray) + Ctor = CreateInitCallsForSections(M, SanCovModuleCtor8bitCountersName, + SanCov8bitCountersInitName, Int8PtrTy, + SanCovCountersSectionName); + if (FunctionBoolArray) { + + Ctor = CreateInitCallsForSections(M, SanCovModuleCtorBoolFlagName, + SanCovBoolFlagInitName, Int1PtrTy, + SanCovBoolFlagSectionName); + + } + + if (Ctor && Options.PCTable) { + + auto SecStartEnd = CreateSecStartEnd(M, SanCovPCsSectionName, IntptrPtrTy); + FunctionCallee InitFunction = declareSanitizerInitFunction( + M, SanCovPCsInitName, {IntptrPtrTy, IntptrPtrTy}); + IRBuilder<> IRBCtor(Ctor->getEntryBlock().getTerminator()); + IRBCtor.CreateCall(InitFunction, {SecStartEnd.first, SecStartEnd.second}); + + } + + // We don't reference these arrays directly in any of our runtime functions, + // so we need to prevent them from being dead stripped. + if (TargetTriple.isOSBinFormatMachO()) appendToUsed(M, GlobalsToAppendToUsed); + appendToCompilerUsed(M, GlobalsToAppendToCompilerUsed); + + if (!be_quiet) { + + if (!instr) + WARNF("No instrumentation targets found."); + else { + + char modeline[100]; + snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", + getenv("AFL_HARDEN") ? "hardened" : "non-hardened", + getenv("AFL_USE_ASAN") ? ", ASAN" : "", + getenv("AFL_USE_MSAN") ? ", MSAN" : "", + getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", + getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); + OKF("Instrumented %u locations with no collisions (%s mode).", instr, + modeline); + + } + + } + + return true; + +} + +// True if block has successors and it dominates all of them. +static bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) { + + if (succ_begin(BB) == succ_end(BB)) return false; + + for (const BasicBlock *SUCC : make_range(succ_begin(BB), succ_end(BB))) { + + if (!DT->dominates(BB, SUCC)) return false; + + } + + return true; + +} + +// True if block has predecessors and it postdominates all of them. +static bool isFullPostDominator(const BasicBlock * BB, + const PostDominatorTree *PDT) { + + if (pred_begin(BB) == pred_end(BB)) return false; + + for (const BasicBlock *PRED : make_range(pred_begin(BB), pred_end(BB))) { + + if (!PDT->dominates(BB, PRED)) return false; + + } + + return true; + +} + +static bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB, + const DominatorTree * DT, + const PostDominatorTree * PDT, + const SanitizerCoverageOptions &Options) { + + // Don't insert coverage for blocks containing nothing but unreachable: we + // will never call __sanitizer_cov() for them, so counting them in + // NumberOfInstrumentedBlocks() might complicate calculation of code coverage + // percentage. Also, unreachable instructions frequently have no debug + // locations. + if (isa(BB->getFirstNonPHIOrDbgOrLifetime())) return false; + + // Don't insert coverage into blocks without a valid insertion point + // (catchswitch blocks). + if (BB->getFirstInsertionPt() == BB->end()) return false; + + if (Options.NoPrune || &F.getEntryBlock() == BB) return true; + + if (Options.CoverageType == SanitizerCoverageOptions::SCK_Function && + &F.getEntryBlock() != BB) + return false; + + // Do not instrument full dominators, or full post-dominators with multiple + // predecessors. + return !isFullDominator(BB, DT) && + !(isFullPostDominator(BB, PDT) && !BB->getSinglePredecessor()); + +} + +// Returns true iff From->To is a backedge. +// A twist here is that we treat From->To as a backedge if +// * To dominates From or +// * To->UniqueSuccessor dominates From +static bool IsBackEdge(BasicBlock *From, BasicBlock *To, + const DominatorTree *DT) { + + if (DT->dominates(To, From)) return true; + if (auto Next = To->getUniqueSuccessor()) + if (DT->dominates(Next, From)) return true; + return false; + +} + +// Prunes uninteresting Cmp instrumentation: +// * CMP instructions that feed into loop backedge branch. +// +// Note that Cmp pruning is controlled by the same flag as the +// BB pruning. +static bool IsInterestingCmp(ICmpInst *CMP, const DominatorTree *DT, + const SanitizerCoverageOptions &Options) { + + if (!Options.NoPrune) + if (CMP->hasOneUse()) + if (auto BR = dyn_cast(CMP->user_back())) + for (BasicBlock *B : BR->successors()) + if (IsBackEdge(BR->getParent(), B, DT)) return false; + return true; + +} + +void ModuleSanitizerCoverage::instrumentFunction( + Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { + + if (F.empty()) return; + if (F.getName().find(".module_ctor") != std::string::npos) + return; // Should not instrument sanitizer init functions. + if (F.getName().startswith("__sanitizer_")) + return; // Don't instrument __sanitizer_* callbacks. + // Don't touch available_externally functions, their actual body is elewhere. + if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) return; + // Don't instrument MSVC CRT configuration helpers. They may run before normal + // initialization. + if (F.getName() == "__local_stdio_printf_options" || + F.getName() == "__local_stdio_scanf_options") + return; + if (isa(F.getEntryBlock().getTerminator())) return; + // Don't instrument functions using SEH for now. Splitting basic blocks like + // we do for coverage breaks WinEHPrepare. + // FIXME: Remove this when SEH no longer uses landingpad pattern matching. + if (F.hasPersonalityFn() && + isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) + return; + if (Allowlist && !Allowlist->inSection("coverage", "fun", F.getName())) + return; + if (Blocklist && Blocklist->inSection("coverage", "fun", F.getName())) return; + if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge) + SplitAllCriticalEdges( + F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests()); + SmallVector IndirCalls; + SmallVector BlocksToInstrument; + SmallVector CmpTraceTargets; + SmallVector SwitchTraceTargets; + SmallVector DivTraceTargets; + SmallVector GepTraceTargets; + + const DominatorTree * DT = DTCallback(F); + const PostDominatorTree *PDT = PDTCallback(F); + bool IsLeafFunc = true; + + for (auto &BB : F) { + + if (shouldInstrumentBlock(F, &BB, DT, PDT, Options)) + BlocksToInstrument.push_back(&BB); + for (auto &Inst : BB) { + + if (Options.IndirectCalls) { + + CallBase *CB = dyn_cast(&Inst); + if (CB && !CB->getCalledFunction()) IndirCalls.push_back(&Inst); + + } + + if (Options.TraceCmp) { + + if (ICmpInst *CMP = dyn_cast(&Inst)) + if (IsInterestingCmp(CMP, DT, Options)) + CmpTraceTargets.push_back(&Inst); + if (isa(&Inst)) SwitchTraceTargets.push_back(&Inst); + + } + + if (Options.TraceDiv) + if (BinaryOperator *BO = dyn_cast(&Inst)) + if (BO->getOpcode() == Instruction::SDiv || + BO->getOpcode() == Instruction::UDiv) + DivTraceTargets.push_back(BO); + if (Options.TraceGep) + if (GetElementPtrInst *GEP = dyn_cast(&Inst)) + GepTraceTargets.push_back(GEP); + if (Options.StackDepth) + if (isa(Inst) || + (isa(Inst) && !isa(Inst))) + IsLeafFunc = false; + + } + + } + + InjectCoverage(F, BlocksToInstrument, IsLeafFunc); + InjectCoverageForIndirectCalls(F, IndirCalls); + InjectTraceForCmp(F, CmpTraceTargets); + InjectTraceForSwitch(F, SwitchTraceTargets); + InjectTraceForDiv(F, DivTraceTargets); + InjectTraceForGep(F, GepTraceTargets); + +} + +GlobalVariable *ModuleSanitizerCoverage::CreateFunctionLocalArrayInSection( + size_t NumElements, Function &F, Type *Ty, const char *Section) { + + ArrayType *ArrayTy = ArrayType::get(Ty, NumElements); + auto Array = new GlobalVariable( + *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage, + Constant::getNullValue(ArrayTy), "__sancov_gen_"); + + if (TargetTriple.supportsCOMDAT() && !F.isInterposable()) + if (auto Comdat = + GetOrCreateFunctionComdat(F, TargetTriple, CurModuleUniqueId)) + Array->setComdat(Comdat); + Array->setSection(getSectionName(Section)); + Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize())); + GlobalsToAppendToUsed.push_back(Array); + GlobalsToAppendToCompilerUsed.push_back(Array); + MDNode *MD = MDNode::get(F.getContext(), ValueAsMetadata::get(&F)); + Array->addMetadata(LLVMContext::MD_associated, *MD); + + return Array; + +} + +GlobalVariable *ModuleSanitizerCoverage::CreatePCArray( + Function &F, ArrayRef AllBlocks) { + + size_t N = AllBlocks.size(); + assert(N); + SmallVector PCs; + IRBuilder<> IRB(&*F.getEntryBlock().getFirstInsertionPt()); + for (size_t i = 0; i < N; i++) { + + if (&F.getEntryBlock() == AllBlocks[i]) { + + PCs.push_back((Constant *)IRB.CreatePointerCast(&F, IntptrPtrTy)); + PCs.push_back((Constant *)IRB.CreateIntToPtr( + ConstantInt::get(IntptrTy, 1), IntptrPtrTy)); + + } else { + + PCs.push_back((Constant *)IRB.CreatePointerCast( + BlockAddress::get(AllBlocks[i]), IntptrPtrTy)); + PCs.push_back((Constant *)IRB.CreateIntToPtr( + ConstantInt::get(IntptrTy, 0), IntptrPtrTy)); + + } + + } + + auto *PCArray = CreateFunctionLocalArrayInSection(N * 2, F, IntptrPtrTy, + SanCovPCsSectionName); + PCArray->setInitializer( + ConstantArray::get(ArrayType::get(IntptrPtrTy, N * 2), PCs)); + PCArray->setConstant(true); + + return PCArray; + +} + +void ModuleSanitizerCoverage::CreateFunctionLocalArrays( + Function &F, ArrayRef AllBlocks) { + + if (Options.TracePCGuard) + FunctionGuardArray = CreateFunctionLocalArrayInSection( + AllBlocks.size(), F, Int32Ty, SanCovGuardsSectionName); + + if (Options.Inline8bitCounters) + Function8bitCounterArray = CreateFunctionLocalArrayInSection( + AllBlocks.size(), F, Int8Ty, SanCovCountersSectionName); + /* + if (Options.InlineBoolFlag) + FunctionBoolArray = CreateFunctionLocalArrayInSection( + AllBlocks.size(), F, Int1Ty, SanCovBoolFlagSectionName); + */ + if (Options.PCTable) FunctionPCsArray = CreatePCArray(F, AllBlocks); + +} + +bool ModuleSanitizerCoverage::InjectCoverage(Function & F, + ArrayRef AllBlocks, + bool IsLeafFunc) { + + if (AllBlocks.empty()) return false; + CreateFunctionLocalArrays(F, AllBlocks); + for (size_t i = 0, N = AllBlocks.size(); i < N; i++) + InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc); + return true; + +} + +// On every indirect call we call a run-time function +// __sanitizer_cov_indir_call* with two parameters: +// - callee address, +// - global cache array that contains CacheSize pointers (zero-initialized). +// The cache is used to speed up recording the caller-callee pairs. +// The address of the caller is passed implicitly via caller PC. +// CacheSize is encoded in the name of the run-time function. +void ModuleSanitizerCoverage::InjectCoverageForIndirectCalls( + Function &F, ArrayRef IndirCalls) { + + if (IndirCalls.empty()) return; + assert(Options.TracePC || Options.TracePCGuard || + Options.Inline8bitCounters /*|| Options.InlineBoolFlag*/); + for (auto I : IndirCalls) { + + IRBuilder<> IRB(I); + CallBase & CB = cast(*I); + Value * Callee = CB.getCalledOperand(); + if (isa(Callee)) continue; + IRB.CreateCall(SanCovTracePCIndir, IRB.CreatePointerCast(Callee, IntptrTy)); + + } + +} + +// For every switch statement we insert a call: +// __sanitizer_cov_trace_switch(CondValue, +// {NumCases, ValueSizeInBits, Case0Value, Case1Value, Case2Value, ... }) + +void ModuleSanitizerCoverage::InjectTraceForSwitch( + Function &, ArrayRef SwitchTraceTargets) { + + for (auto I : SwitchTraceTargets) { + + if (SwitchInst *SI = dyn_cast(I)) { + + IRBuilder<> IRB(I); + SmallVector Initializers; + Value * Cond = SI->getCondition(); + if (Cond->getType()->getScalarSizeInBits() > + Int64Ty->getScalarSizeInBits()) + continue; + Initializers.push_back(ConstantInt::get(Int64Ty, SI->getNumCases())); + Initializers.push_back( + ConstantInt::get(Int64Ty, Cond->getType()->getScalarSizeInBits())); + if (Cond->getType()->getScalarSizeInBits() < + Int64Ty->getScalarSizeInBits()) + Cond = IRB.CreateIntCast(Cond, Int64Ty, false); + for (auto It : SI->cases()) { + + Constant *C = It.getCaseValue(); + if (C->getType()->getScalarSizeInBits() < + Int64Ty->getScalarSizeInBits()) + C = ConstantExpr::getCast(CastInst::ZExt, It.getCaseValue(), Int64Ty); + Initializers.push_back(C); + + } + + llvm::sort(Initializers.begin() + 2, Initializers.end(), + [](const Constant *A, const Constant *B) { + + return cast(A)->getLimitedValue() < + cast(B)->getLimitedValue(); + + }); + + ArrayType *ArrayOfInt64Ty = ArrayType::get(Int64Ty, Initializers.size()); + GlobalVariable *GV = new GlobalVariable( + *CurModule, ArrayOfInt64Ty, false, GlobalVariable::InternalLinkage, + ConstantArray::get(ArrayOfInt64Ty, Initializers), + "__sancov_gen_cov_switch_values"); + IRB.CreateCall(SanCovTraceSwitchFunction, + {Cond, IRB.CreatePointerCast(GV, Int64PtrTy)}); + + } + + } + +} + +void ModuleSanitizerCoverage::InjectTraceForDiv( + Function &, ArrayRef DivTraceTargets) { + + for (auto BO : DivTraceTargets) { + + IRBuilder<> IRB(BO); + Value * A1 = BO->getOperand(1); + if (isa(A1)) continue; + if (!A1->getType()->isIntegerTy()) continue; + uint64_t TypeSize = DL->getTypeStoreSizeInBits(A1->getType()); + int CallbackIdx = TypeSize == 32 ? 0 : TypeSize == 64 ? 1 : -1; + if (CallbackIdx < 0) continue; + auto Ty = Type::getIntNTy(*C, TypeSize); + IRB.CreateCall(SanCovTraceDivFunction[CallbackIdx], + {IRB.CreateIntCast(A1, Ty, true)}); + + } + +} + +void ModuleSanitizerCoverage::InjectTraceForGep( + Function &, ArrayRef GepTraceTargets) { + + for (auto GEP : GepTraceTargets) { + + IRBuilder<> IRB(GEP); + for (auto I = GEP->idx_begin(); I != GEP->idx_end(); ++I) + if (!isa(*I) && (*I)->getType()->isIntegerTy()) + IRB.CreateCall(SanCovTraceGepFunction, + {IRB.CreateIntCast(*I, IntptrTy, true)}); + + } + +} + +void ModuleSanitizerCoverage::InjectTraceForCmp( + Function &, ArrayRef CmpTraceTargets) { + + for (auto I : CmpTraceTargets) { + + if (ICmpInst *ICMP = dyn_cast(I)) { + + IRBuilder<> IRB(ICMP); + Value * A0 = ICMP->getOperand(0); + Value * A1 = ICMP->getOperand(1); + if (!A0->getType()->isIntegerTy()) continue; + uint64_t TypeSize = DL->getTypeStoreSizeInBits(A0->getType()); + int CallbackIdx = + TypeSize == 8 + ? 0 + : TypeSize == 16 ? 1 + : TypeSize == 32 ? 2 : TypeSize == 64 ? 3 : -1; + if (CallbackIdx < 0) continue; + // __sanitizer_cov_trace_cmp((type_size << 32) | predicate, A0, A1); + auto CallbackFunc = SanCovTraceCmpFunction[CallbackIdx]; + bool FirstIsConst = isa(A0); + bool SecondIsConst = isa(A1); + // If both are const, then we don't need such a comparison. + if (FirstIsConst && SecondIsConst) continue; + // If only one is const, then make it the first callback argument. + if (FirstIsConst || SecondIsConst) { + + CallbackFunc = SanCovTraceConstCmpFunction[CallbackIdx]; + if (SecondIsConst) std::swap(A0, A1); + + } + + auto Ty = Type::getIntNTy(*C, TypeSize); + IRB.CreateCall(CallbackFunc, {IRB.CreateIntCast(A0, Ty, true), + IRB.CreateIntCast(A1, Ty, true)}); + + } + + } + +} + +void ModuleSanitizerCoverage::InjectCoverageAtBlock(Function &F, BasicBlock &BB, + size_t Idx, + bool IsLeafFunc) { + + BasicBlock::iterator IP = BB.getFirstInsertionPt(); + bool IsEntryBB = &BB == &F.getEntryBlock(); + DebugLoc EntryLoc; + if (IsEntryBB) { + + if (auto SP = F.getSubprogram()) + EntryLoc = DebugLoc::get(SP->getScopeLine(), 0, SP); + // Keep static allocas and llvm.localescape calls in the entry block. Even + // if we aren't splitting the block, it's nice for allocas to be before + // calls. + IP = PrepareToSplitEntryBlock(BB, IP); + + } else { + + EntryLoc = IP->getDebugLoc(); + + } + + IRBuilder<> IRB(&*IP); + IRB.SetCurrentDebugLocation(EntryLoc); + if (Options.TracePC) { + + IRB.CreateCall(SanCovTracePC); + // ->setCannotMerge(); // gets the PC using GET_CALLER_PC. + + } + + if (Options.TracePCGuard) { + + /* Get CurLoc */ + + Value *GuardPtr = IRB.CreateIntToPtr( + IRB.CreateAdd(IRB.CreatePointerCast(FunctionGuardArray, IntptrTy), + ConstantInt::get(IntptrTy, Idx * 4)), + Int32PtrTy); + + LoadInst *CurLoc = IRB.CreateLoad(GuardPtr); + + /* Load SHM pointer */ + + LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); + + /* Load counter for CurLoc */ + + Value * MapPtrIdx = IRB.CreateGEP(MapPtr, CurLoc); + LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); + + /* Update bitmap */ + + Value *Incr = IRB.CreateAdd(Counter, One); + + if (skip_nozero == NULL) { + + auto cf = IRB.CreateICmpEQ(Incr, Zero); + auto carry = IRB.CreateZExt(cf, Int8Ty); + Incr = IRB.CreateAdd(Incr, carry); + + } + + IRB.CreateStore(Incr, MapPtrIdx); + + // done :) + + // IRB.CreateCall(SanCovTracePCGuard, Offset)->setCannotMerge(); + // IRB.CreateCall(SanCovTracePCGuard, GuardPtr)->setCannotMerge(); + ++instr; + + } + + if (Options.Inline8bitCounters) { + + auto CounterPtr = IRB.CreateGEP( + Function8bitCounterArray->getValueType(), Function8bitCounterArray, + {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)}); + auto Load = IRB.CreateLoad(Int8Ty, CounterPtr); + auto Inc = IRB.CreateAdd(Load, ConstantInt::get(Int8Ty, 1)); + auto Store = IRB.CreateStore(Inc, CounterPtr); + SetNoSanitizeMetadata(Load); + SetNoSanitizeMetadata(Store); + + } + + /* + if (Options.InlineBoolFlag) { + + auto FlagPtr = IRB.CreateGEP( + FunctionBoolArray->getValueType(), FunctionBoolArray, + {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)}); + auto Load = IRB.CreateLoad(Int1Ty, FlagPtr); + auto ThenTerm = + SplitBlockAndInsertIfThen(IRB.CreateIsNull(Load), &*IP, false); + IRBuilder<> ThenIRB(ThenTerm); + auto Store = ThenIRB.CreateStore(ConstantInt::getTrue(Int1Ty), FlagPtr); + SetNoSanitizeMetadata(Load); + SetNoSanitizeMetadata(Store); + + } + + */ + + if (Options.StackDepth && IsEntryBB && !IsLeafFunc) { + + // Check stack depth. If it's the deepest so far, record it. + Module * M = F.getParent(); + Function *GetFrameAddr = Intrinsic::getDeclaration( + M, Intrinsic::frameaddress, + IRB.getInt8PtrTy(M->getDataLayout().getAllocaAddrSpace())); + auto FrameAddrPtr = + IRB.CreateCall(GetFrameAddr, {Constant::getNullValue(Int32Ty)}); + auto FrameAddrInt = IRB.CreatePtrToInt(FrameAddrPtr, IntptrTy); + auto LowestStack = IRB.CreateLoad(IntptrTy, SanCovLowestStack); + auto IsStackLower = IRB.CreateICmpULT(FrameAddrInt, LowestStack); + auto ThenTerm = SplitBlockAndInsertIfThen(IsStackLower, &*IP, false); + IRBuilder<> ThenIRB(ThenTerm); + auto Store = ThenIRB.CreateStore(FrameAddrInt, SanCovLowestStack); + SetNoSanitizeMetadata(LowestStack); + SetNoSanitizeMetadata(Store); + + } + +} + +std::string ModuleSanitizerCoverage::getSectionName( + const std::string &Section) const { + + if (TargetTriple.isOSBinFormatCOFF()) { + + if (Section == SanCovCountersSectionName) return ".SCOV$CM"; + if (Section == SanCovBoolFlagSectionName) return ".SCOV$BM"; + if (Section == SanCovPCsSectionName) return ".SCOVP$M"; + return ".SCOV$GM"; // For SanCovGuardsSectionName. + + } + + if (TargetTriple.isOSBinFormatMachO()) return "__DATA,__" + Section; + return "__" + Section; + +} + +std::string ModuleSanitizerCoverage::getSectionStart( + const std::string &Section) const { + + if (TargetTriple.isOSBinFormatMachO()) + return "\1section$start$__DATA$__" + Section; + return "__start___" + Section; + +} + +std::string ModuleSanitizerCoverage::getSectionEnd( + const std::string &Section) const { + + if (TargetTriple.isOSBinFormatMachO()) + return "\1section$end$__DATA$__" + Section; + return "__stop___" + Section; + +} + +char ModuleSanitizerCoverageLegacyPass::ID = 0; +INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLegacyPass, "sancov", + "Pass for instrumenting coverage on functions", false, + false) +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) +INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass) +INITIALIZE_PASS_END(ModuleSanitizerCoverageLegacyPass, "sancov", + "Pass for instrumenting coverage on functions", false, + false) +ModulePass *llvm::createModuleSanitizerCoverageLegacyPassPass( + const SanitizerCoverageOptions &Options, + const std::vector &AllowlistFiles, + const std::vector &BlocklistFiles) { + + return new ModuleSanitizerCoverageLegacyPass(Options, AllowlistFiles, + BlocklistFiles); + +} + +static void registerPCGUARDPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + auto p = new ModuleSanitizerCoverageLegacyPass(); + PM.add(p); + +} + +static RegisterStandardPasses RegisterCompTransPass( + PassManagerBuilder::EP_OptimizerLast, registerPCGUARDPass); + +static RegisterStandardPasses RegisterCompTransPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerPCGUARDPass); + diff --git a/src/afl-cc.c b/src/afl-cc.c index c516dc4c..1a7a837f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -501,11 +501,22 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { -#if LLVM_MAJOR >= 4 +#if LLVM_MAJOR >= 10 + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = - "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default + alloc_printf("%s/SanitizerCoveragePCGUARD.so", obj_path); #else + #if LLVM_MAJOR >= 4 + if (!be_quiet) + SAYF( + "Using unoptimized trace-pc-guard, upgrade to llvm 10+ for " + "enhanced version.\n"); + cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; + #else FATAL("pcguard instrumentation requires llvm 4.0.1+"); + #endif #endif } else { -- cgit 1.4.1 From 20a8a93fd193f7526f5e3d0cd1dfa43df9d2c4f6 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Thu, 12 Nov 2020 01:13:57 +0300 Subject: Fixed symcc custom mutator --- custom_mutators/symcc/symcc.c | 67 +++++++++++++++++------- custom_mutators/symcc/test_examples/file_test.c | 27 ++++++++++ custom_mutators/symcc/test_examples/stdin_test.c | 22 ++++++++ src/afl-fuzz-bitmap.c | 10 ++-- 4 files changed, 101 insertions(+), 25 deletions(-) create mode 100644 custom_mutators/symcc/test_examples/file_test.c create mode 100644 custom_mutators/symcc/test_examples/stdin_test.c (limited to 'src') diff --git a/custom_mutators/symcc/symcc.c b/custom_mutators/symcc/symcc.c index 18b475b8..ab3d70ca 100644 --- a/custom_mutators/symcc/symcc.c +++ b/custom_mutators/symcc/symcc.c @@ -1,7 +1,10 @@ +#define _GNU_SOURCE #include #include #include #include +#include +#include #include "config.h" #include "debug.h" #include "afl-fuzz.h" @@ -95,40 +98,66 @@ void afl_custom_queue_new_entry(my_mutator_t * data, const uint8_t *filename_new_queue, const uint8_t *filename_orig_queue) { + int pipefd[2]; + struct stat st; + ACTF("Queueing to symcc: %s", filename_new_queue); + u8 *fn = alloc_printf("%s", filename_new_queue); + if (!(stat(fn, &st) == 0 && S_ISREG(st.st_mode) && st.st_size)) { + PFATAL("Couldn't find enqueued file: %s",fn); + } + + if (afl_struct->fsrv.use_stdin){ + if (pipe(pipefd)==-1) + { + exit(-1); + } + } int pid = fork(); if (pid == -1) return; + + if (pid){ - if (pid) pid = waitpid(pid, NULL, 0); - - if (pid == 0) { + if (afl_struct->fsrv.use_stdin){ - setenv("SYMCC_INPUT_FILE", afl_struct->fsrv.out_file, 1); - - if (afl_struct->fsrv.use_stdin) { - - u8 *fn = alloc_printf("%s/%s", afl_struct->out_dir, filename_new_queue); + close(pipefd[0]); int fd = open(fn, O_RDONLY); - + if (fd >= 0) { - + ssize_t r = read(fd, data->mutator_buf, MAX_FILE); - close(fd); DBG("fn=%s, fd=%d, size=%ld\n", fn, fd, r); if (r <= 0) return; - close(0); - ck_write(0, data->mutator_buf, r, fn); - ck_free(fn); - + close(fd); + if (r>fcntl(pipefd[1],F_GETPIPE_SZ)) fcntl(pipefd[1],F_SETPIPE_SZ,MAX_FILE); + ck_write(pipefd[1], data->mutator_buf, r, filename_new_queue); + } else { + PFATAL("Something happened to the enqueued file before sending its contents to symcc binary"); } + close(pipefd[1]); + ck_free(fn); } + pid = waitpid(pid,NULL, 0); + } + if (pid == 0) { + if (afl_struct->fsrv.use_stdin) { + unsetenv("SYMCC_INPUT_FILE"); + close(pipefd[1]); + dup2(pipefd[0],0); + } + else + { + setenv("SYMCC_INPUT_FILE", afl_struct->fsrv.out_file, 1); + } + DBG("exec=%s\n", data->target); close(1); close(2); dup2(afl_struct->fsrv.dev_null_fd, 1); dup2(afl_struct->fsrv.dev_null_fd, 2); + execvp(data->target, afl_struct->argv); DBG("exec=FAIL\n"); exit(-1); @@ -179,8 +208,8 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, struct dirent **nl; int32_t i, done = 0, items = scandir(data->out_dir, &nl, NULL, NULL); - size_t size = 0; - + ssize_t size = 0; + if (items <= 0) return 0; for (i = 0; i < (u32)items; ++i) { @@ -195,9 +224,9 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, int fd = open(fn, O_RDONLY); if (fd >= 0) { - size = read(fd, data->mutator_buf, max_size); *out_buf = data->mutator_buf; + close(fd); done = 1; @@ -216,7 +245,7 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, free(nl); DBG("FUZZ size=%lu\n", size); - return size; + return (uint32_t)size; } diff --git a/custom_mutators/symcc/test_examples/file_test.c b/custom_mutators/symcc/test_examples/file_test.c new file mode 100644 index 00000000..25271788 --- /dev/null +++ b/custom_mutators/symcc/test_examples/file_test.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include + +int main(int argc, char** argv){ + if (argc<2){ + printf("Need a file argument\n"); + return 1; + } + int fd=open(argv[1],O_RDONLY); + if (fd<0){ + printf("Couldn't open file\n"); + return 1; + } + uint32_t value = 0; + + read(fd,&value,sizeof(value)); + close(fd); + + value=value^0xffffffff; + if (value== 0x11223344) printf("Value one\n"); + if (value == 0x44332211) printf("Value two\n"); + if (value != 0x0) printf("Not zero\n"); + return 0; +} diff --git a/custom_mutators/symcc/test_examples/stdin_test.c b/custom_mutators/symcc/test_examples/stdin_test.c new file mode 100644 index 00000000..be87419b --- /dev/null +++ b/custom_mutators/symcc/test_examples/stdin_test.c @@ -0,0 +1,22 @@ +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + char input_buffer[16]; + uint32_t comparisonValue; + size_t bytesRead; + bytesRead=read(STDIN_FILENO,input_buffer, sizeof(input_buffer)); + if (bytesRead < 0) exit(-1); + comparisonValue=*(uint32_t*)input_buffer; + comparisonValue=comparisonValue^0xff112233; + if (comparisonValue==0x66554493){ + printf("First value\n"); + } + else{ + if (comparisonValue==0x84444415) printf("Second value\n"); + } + return 0; +} diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 735420c3..4b29672a 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -584,7 +584,10 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { alloc_printf("%s/queue/id_%06u", afl->out_dir, afl->queued_paths); #endif /* ^!SIMPLE_FILES */ - + fd = open(queue_fn, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (unlikely(fd < 0)) { PFATAL("Unable to create '%s'", queue_fn); } + ck_write(fd, mem, len, queue_fn); + close(fd); add_to_queue(afl, queue_fn, len, 0); #ifdef INTROSPECTION @@ -623,11 +626,6 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { } - fd = open(queue_fn, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (unlikely(fd < 0)) { PFATAL("Unable to create '%s'", queue_fn); } - ck_write(fd, mem, len, queue_fn); - close(fd); - if (likely(afl->q_testcase_max_cache_size)) { queue_testcase_store_mem(afl, afl->queue_top, mem); -- cgit 1.4.1 From bd313d4039d094c0caf657b4ed1a666da7eded1b Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 14 Nov 2020 11:31:18 +0100 Subject: no binary checking in noninstrumented mode --- src/afl-fuzz.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 269ce1bf..59772b3f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1338,11 +1338,11 @@ int main(int argc, char **argv_orig, char **envp) { } - if (!afl->fsrv.qemu_mode) { check_binary(afl, afl->cmplog_binary); } + if (!afl->fsrv.qemu_mode && !afl->non_instrumented_mode) { check_binary(afl, afl->cmplog_binary); } } - check_binary(afl, argv[optind]); + if (afl->non_instrumented_mode) check_binary(afl, argv[optind]); if (afl->shmem_testcase_mode) { setup_testcase_shmem(afl); } -- cgit 1.4.1 From 30cd8a8397419b3eedb6ee939e290b4c6b8c2cf1 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 14 Nov 2020 12:28:51 +0100 Subject: fix non instrumented mode, fix check_binary --- qemu_mode/qemuafl | 2 +- src/afl-fuzz-init.c | 6 ------ src/afl-fuzz.c | 10 +++++++++- unicorn_mode/unicornafl | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl index d66c9e26..21ff3438 160000 --- a/qemu_mode/qemuafl +++ b/qemu_mode/qemuafl @@ -1 +1 @@ -Subproject commit d66c9e2654efa8939f0fe6995d11a72b98a4da3e +Subproject commit 21ff34383764a8c6f66509b3b8d5282468c721e1 diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 19a8d77b..01929a0a 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2300,12 +2300,6 @@ void fix_up_sync(afl_state_t *afl) { u8 *x = afl->sync_id; - if (afl->non_instrumented_mode) { - - FATAL("-S / -M and -n are mutually exclusive"); - - } - while (*x) { if (!isalnum(*x) && *x != '_' && *x != '-') { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 59772b3f..f662b308 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -435,6 +435,7 @@ int main(int argc, char **argv_orig, char **envp) { u8 *c; + if (afl->non_instrumented_mode) { FATAL("-M is not supported in non-instrumented mode "); } if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } afl->sync_id = ck_strdup(optarg); afl->skip_deterministic = 0; // force determinsitic fuzzing @@ -464,6 +465,7 @@ int main(int argc, char **argv_orig, char **envp) { case 'S': /* secondary sync id */ + if (afl->non_instrumented_mode) { FATAL("-S is not supported in non-instrumented mode "); } if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } afl->sync_id = ck_strdup(optarg); afl->is_secondary_node = 1; @@ -620,6 +622,12 @@ int main(int argc, char **argv_orig, char **envp) { case 'n': /* dumb mode */ + if (afl->is_main_node || afl->is_secondary_node) { + + FATAL("Non instrumented mode is not supported with -M / -S"); + + } + if (afl->non_instrumented_mode) { FATAL("Multiple -n options not supported"); @@ -1342,7 +1350,7 @@ int main(int argc, char **argv_orig, char **envp) { } - if (afl->non_instrumented_mode) check_binary(afl, argv[optind]); + if (!afl->non_instrumented_mode) check_binary(afl, argv[optind]); if (afl->shmem_testcase_mode) { setup_testcase_shmem(afl); } diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl index c6d66471..f44ec48f 160000 --- a/unicorn_mode/unicornafl +++ b/unicorn_mode/unicornafl @@ -1 +1 @@ -Subproject commit c6d6647161a32bae88785a618fcd828d1711d9e6 +Subproject commit f44ec48f8d5929f243522c1152b5b3c0985a5548 -- cgit 1.4.1 From e750a5c856486bb89401f3555ca529bf743146f4 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 14 Nov 2020 12:36:28 +0100 Subject: add sanity check for -M/-S arguments --- src/afl-fuzz.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index f662b308..6b19d648 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -435,8 +435,12 @@ int main(int argc, char **argv_orig, char **envp) { u8 *c; - if (afl->non_instrumented_mode) { FATAL("-M is not supported in non-instrumented mode "); } + if (afl->non_instrumented_mode) { FATAL("-M is not supported in non-instrumented mode"); } if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } + + /* sanity check for argument: should not begin with '-' (possible option) */ + if (optarg && *optarg == '-') { FATAL("argument for -M started with a dash '-', which is used for options"); } + afl->sync_id = ck_strdup(optarg); afl->skip_deterministic = 0; // force determinsitic fuzzing afl->old_seed_selection = 1; // force old queue walking seed selection @@ -465,8 +469,12 @@ int main(int argc, char **argv_orig, char **envp) { case 'S': /* secondary sync id */ - if (afl->non_instrumented_mode) { FATAL("-S is not supported in non-instrumented mode "); } + if (afl->non_instrumented_mode) { FATAL("-S is not supported in non-instrumented mode"); } if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } + + /* sanity check for argument: should not begin with '-' (possible option) */ + if (optarg && *optarg == '-') { FATAL("argument for -M started with a dash '-', which is used for options"); } + afl->sync_id = ck_strdup(optarg); afl->is_secondary_node = 1; break; -- cgit 1.4.1 From 40e10895a2b7b69425ee03b2ec6e478184120ee2 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 14 Nov 2020 17:21:43 +0100 Subject: now really fix -n --- src/afl-forkserver.c | 25 +++++++++++++++++-------- src/afl-fuzz-init.c | 2 +- src/afl-fuzz.c | 2 +- 3 files changed, 19 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 45be2abd..266f021b 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -116,7 +116,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->use_fauxsrv = 0; fsrv_to->last_run_timed_out = 0; - fsrv_to->init_child_func = fsrv_exec_child; + fsrv_to->init_child_func = from->init_child_func; // Note: do not copy ->add_extra_func list_append(&fsrv_list, fsrv_to); @@ -220,7 +220,15 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) { } void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL); - +#if 0 + WARNF("targetpath=%s", fsrv->target_path); + if (argv) { + for (char *p = argv[0]; p; ++p) { + WARNF(" %s", p); + } + } + WARNF("\n"); +#endif while (1) { uint32_t was_killed; @@ -272,7 +280,8 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) { *(u32 *)fsrv->trace_bits = EXEC_FAIL_SIG; - PFATAL("Execv failed in fauxserver."); + WARNF("Execv failed in fauxserver."); + break; } @@ -286,13 +295,13 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) { if (waitpid(child_pid, &status, 0) < 0) { // Zombie Child could not be collected. Scary! - PFATAL("Fauxserver could not determin child's exit code. "); + WARNF("Fauxserver could not determine child's exit code. "); } /* Relay wait status to AFL pipe, then loop back. */ - if (write(FORKSRV_FD + 1, &status, 4) != 4) { exit(0); } + if (write(FORKSRV_FD + 1, &status, 4) != 4) { exit(1); } } @@ -330,7 +339,7 @@ static void report_error_and_exit(int error) { "memory failed."); break; default: - FATAL("unknown error code %u from fuzzing target!", error); + FATAL("unknown error code %d from fuzzing target!", error); } @@ -355,7 +364,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if (fsrv->use_fauxsrv) { - /* TODO: Come up with sone nice way to initialize this all */ + /* TODO: Come up with some nice way to initialize this all */ if (fsrv->init_child_func != fsrv_exec_child) { @@ -520,7 +529,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, *(u32 *)fsrv->trace_bits = EXEC_FAIL_SIG; fprintf(stderr, "Error: execv to target failed\n"); - exit(0); + exit(1); } diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 01929a0a..8b9b0a6f 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2497,7 +2497,7 @@ void check_binary(afl_state_t *afl, u8 *fname) { } - if (afl->afl_env.afl_skip_bin_check || afl->use_wine || afl->unicorn_mode) { + if (afl->afl_env.afl_skip_bin_check || afl->use_wine || afl->unicorn_mode || afl->non_instrumented_mode) { return; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 6b19d648..39af1e18 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1358,7 +1358,7 @@ int main(int argc, char **argv_orig, char **envp) { } - if (!afl->non_instrumented_mode) check_binary(afl, argv[optind]); + check_binary(afl, argv[optind]); if (afl->shmem_testcase_mode) { setup_testcase_shmem(afl); } -- cgit 1.4.1 From 76c5b8a3b420bfb74191cf4d3e44b067a268dc7f Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 14 Nov 2020 19:38:06 +0100 Subject: fix error handling in fauxserver --- src/afl-forkserver.c | 12 ++---------- src/afl-fuzz.c | 4 ++-- 2 files changed, 4 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 266f021b..3814a77e 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -220,15 +220,7 @@ static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) { } void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL); -#if 0 - WARNF("targetpath=%s", fsrv->target_path); - if (argv) { - for (char *p = argv[0]; p; ++p) { - WARNF(" %s", p); - } - } - WARNF("\n"); -#endif + while (1) { uint32_t was_killed; @@ -1146,7 +1138,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, } // Fauxserver should handle this now. - // if (tb4 == EXEC_FAIL_SIG) return FSRV_RUN_ERROR; + if (*(u32 *)fsrv->trace_bits == EXEC_FAIL_SIG) return FSRV_RUN_ERROR; return FSRV_RUN_OK; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 39af1e18..c1ddd413 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -442,7 +442,7 @@ int main(int argc, char **argv_orig, char **envp) { if (optarg && *optarg == '-') { FATAL("argument for -M started with a dash '-', which is used for options"); } afl->sync_id = ck_strdup(optarg); - afl->skip_deterministic = 0; // force determinsitic fuzzing + afl->skip_deterministic = 0; // force deterministic fuzzing afl->old_seed_selection = 1; // force old queue walking seed selection if ((c = strchr(afl->sync_id, ':'))) { @@ -922,7 +922,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->power_name = power_names[afl->schedule]; - if (!afl->sync_id) { + if (!afl->non_instrumented_mode && !afl->sync_id) { auto_sync = 1; afl->sync_id = ck_strdup("default"); -- cgit 1.4.1 From ea689076b330c46223ed1c36f9b02fc319c798f8 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sat, 14 Nov 2020 19:54:51 -0500 Subject: Actually make python 'fuzz' method optional At some point mutator->afl_custom_fuzz was allowed to be NULL, so do that instead of crashing --- src/afl-fuzz-python.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index 596b733e..cfaf055d 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -329,9 +329,7 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, if (py_functions[PY_FUNC_DEINIT]) { mutator->afl_custom_deinit = deinit_py; } - /* "afl_custom_fuzz" should not be NULL, but the interface of Python mutator - is quite different from the custom mutator. */ - mutator->afl_custom_fuzz = fuzz_py; + if (py_functions[PY_FUNC_FUZZ]) { mutator->afl_custom_fuzz = fuzz_py; } if (py_functions[PY_FUNC_POST_PROCESS]) { -- cgit 1.4.1 From 1cc637a0a05a043a223f69fb9661ecc3d5597d23 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 16 Nov 2020 10:59:09 +0100 Subject: support AFL_LLVM_INSTRUMENT env for our own PCGUARD --- docs/Changelog.md | 13 +-- instrumentation/SanitizerCoveragePCGUARD.so.cc | 8 +- src/afl-cc.c | 113 ++++++++++++++----------- src/afl-fuzz-init.c | 3 +- src/afl-fuzz.c | 42 +++++++-- 5 files changed, 116 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index a69f2ff4..baa2667b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -37,24 +37,27 @@ sending a mail to . - added NO_SPLICING compile option and makefile define - added INTROSPECTION make target that writes all mutations to out/NAME/introspection.txt - - added INTROSPECTION support for custom modules - print special compile time options used in help output + - somewhere we broke -n dumb fuzzing, fixed - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! - not overriding -Ox or -fno-unroll-loops anymore - we now have our own trace-pc-guard implementation. It is the same as -fsanitize-coverage=trace-pc-guard from llvm 12, but: it is a) inline - and b) works from llvm 10+ on :) + and b) works from llvm 10.0.1 + onwards :) - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz -x dictionary of string comparisons found during compilation - LTO autodict now also collects interesting cmp comparisons, std::string compare + find + ==, bcmp - fix crash in dict2file for integers > 64 bit + - custom mutators + - added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/ + - added a new custom mutator: libfuzzer that integrates libfuzzer mutations + - Our afl++ Grammar-Mutator is now better integrated into custom_mutators/ + - added INTROSPECTION support for custom modules + - python fuzz function was not optional, fixed - unicornafl synced with upstream (arm64 fix, better rust bindings) - - added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/ - - added a new custom mutator: libfuzzer that integrates libfuzzer mutations - - Our afl++ Grammar-Mutator is now better integrated into custom_mutators/ ### Version ++2.68c (release) diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index b3c55108..e85f9cd3 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -544,7 +544,9 @@ bool ModuleSanitizerCoverage::instrumentModule( be_quiet = 1; skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); - // scanForDangerousFunctions(&M); + + initInstrumentList(); + scanForDangerousFunctions(&M); if (debug) { @@ -819,6 +821,8 @@ void ModuleSanitizerCoverage::instrumentFunction( Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { if (F.empty()) return; + if (!isInInstrumentList(&F)) return; + if (F.getName().find(".module_ctor") != std::string::npos) return; // Should not instrument sanitizer init functions. if (F.getName().startswith("__sanitizer_")) @@ -1315,6 +1319,7 @@ std::string ModuleSanitizerCoverage::getSectionEnd( } char ModuleSanitizerCoverageLegacyPass::ID = 0; + INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLegacyPass, "sancov", "Pass for instrumenting coverage on functions", false, false) @@ -1323,6 +1328,7 @@ INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass) INITIALIZE_PASS_END(ModuleSanitizerCoverageLegacyPass, "sancov", "Pass for instrumenting coverage on functions", false, false) + ModulePass *llvm::createModuleSanitizerCoverageLegacyPassPass( const SanitizerCoverageOptions &Options, const std::vector &AllowlistFiles, diff --git a/src/afl-cc.c b/src/afl-cc.c index 771a58f5..5d8d33a5 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -49,14 +49,14 @@ static u8 * obj_path; /* Path to runtime libraries */ static u8 **cc_params; /* Parameters passed to the real CC */ static u32 cc_par_cnt = 1; /* Param count, including argv0 */ static u8 llvm_fullpath[PATH_MAX]; -static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode, - compiler_mode, plusplus_mode; -static u8 have_gcc, have_llvm, have_gcc_plugin, have_lto; -static u8 *lto_flag = AFL_CLANG_FLTO, *argvnull; -static u8 debug; -static u8 cwd[4096]; -static u8 cmplog_mode; -u8 use_stdin; /* dummy */ +static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode; +static u8 compiler_mode, plusplus_mode, have_instr_env = 0; +static u8 have_gcc, have_llvm, have_gcc_plugin, have_lto, have_instr_list = 0; +static u8 * lto_flag = AFL_CLANG_FLTO, *argvnull; +static u8 debug; +static u8 cwd[4096]; +static u8 cmplog_mode; +u8 use_stdin; /* dummy */ // static u8 *march_opt = CFLAGS_OPT; enum { @@ -354,19 +354,13 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode && plusplus_mode) cc_params[cc_par_cnt++] = "-lc++"; // needed by fuzzbench, early - if (lto_mode) { - - if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { + if (lto_mode && have_instr_env) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-lto-instrumentlist.so", obj_path); - - } + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-lto-instrumentlist.so", obj_path); } @@ -508,11 +502,25 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { #if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/SanitizerCoveragePCGUARD.so", obj_path); + if (have_instr_list) { + + if (!be_quiet) + SAYF( + "Using unoptimized trace-pc-guard, due usage of " + "-fsanitize-coverage-allow/denylist, you can use " + "AFL_LLVM_ALLOWLIST/AFL_LLMV_DENYLIST instead.\n"); + cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/SanitizerCoveragePCGUARD.so", obj_path); + + } + #else #if LLVM_MAJOR >= 4 if (!be_quiet) @@ -590,6 +598,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32; if (!strcmp(cur, "-m64")) bit_mode = 64; + if (!strncmp(cur, "-fsanitize-coverage-", 20) && strstr(cur, "list=")) + have_instr_list = 1; + if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory")) asan_set = 1; @@ -856,6 +867,14 @@ int main(int argc, char **argv, char **envp) { be_quiet = 1; + if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST") || + getenv("AFL_LLVM_ALLOWLIST") || getenv("AFL_LLVM_DENYLIST") || + getenv("AFL_LLVM_BLOCKLIST")) { + + have_instr_env = 1; + + } + if ((ptr = strrchr(callname, '/')) != NULL) callname = ptr + 1; argvnull = (u8 *)argv[0]; check_environment_vars(envp); @@ -1015,14 +1034,14 @@ int main(int argc, char **argv, char **envp) { } - if ((getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) && - getenv("AFL_DONT_OPTIMIZE")) + if (have_instr_env && getenv("AFL_DONT_OPTIMIZE")) { + WARNF( "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined " "for file matching, only function matching!"); + } + if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || getenv("INSTRIM_LIB")) { @@ -1426,22 +1445,20 @@ int main(int argc, char **argv, char **envp) { #if LLVM_MAJOR <= 6 instrument_mode = INSTRUMENT_AFL; #else - if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { + #if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) + if (have_instr_env) { instrument_mode = INSTRUMENT_AFL; - WARNF( - "switching to classic instrumentation because " - "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD. Use " - "-fsanitize-coverage-allowlist=allowlist.txt or " - "-fsanitize-coverage-blocklist=denylist.txt if you want to use " - "PCGUARD. Requires llvm 12+. See https://clang.llvm.org/docs/ " - "SanitizerCoverage.html#partially-disabling-instrumentation"); + if (!be_quiet) + WARNF( + "Switching to classic instrumentation because " + "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD < 10.0.1."); } else + #endif instrument_mode = INSTRUMENT_PCGUARD; + #endif } @@ -1487,18 +1504,16 @@ int main(int argc, char **argv, char **envp) { "AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set " "together"); - if (instrument_mode == INSTRUMENT_PCGUARD && - (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST"))) +#if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) + if (instrument_mode == INSTRUMENT_PCGUARD && have_instr_env) { + FATAL( "Instrumentation type PCGUARD does not support " - "AFL_LLVM_ALLOWLIST/DENYLIST! Use " - "-fsanitize-coverage-allowlist=allowlist.txt or " - "-fsanitize-coverage-blocklist=denylist.txt instead (requires llvm " - "12+), see " - "https://clang.llvm.org/docs/" - "SanitizerCoverage.html#partially-disabling-instrumentation"); + "AFL_LLVM_ALLOWLIST/DENYLIST! Use LLVM 10.0.1+ instead."); + + } + +#endif u8 *ptr2; diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 8b9b0a6f..6884bb1d 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2497,7 +2497,8 @@ void check_binary(afl_state_t *afl, u8 *fname) { } - if (afl->afl_env.afl_skip_bin_check || afl->use_wine || afl->unicorn_mode || afl->non_instrumented_mode) { + if (afl->afl_env.afl_skip_bin_check || afl->use_wine || afl->unicorn_mode || + afl->non_instrumented_mode) { return; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index c1ddd413..cedfdf8f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -435,11 +435,23 @@ int main(int argc, char **argv_orig, char **envp) { u8 *c; - if (afl->non_instrumented_mode) { FATAL("-M is not supported in non-instrumented mode"); } + if (afl->non_instrumented_mode) { + + FATAL("-M is not supported in non-instrumented mode"); + + } + if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } - /* sanity check for argument: should not begin with '-' (possible option) */ - if (optarg && *optarg == '-') { FATAL("argument for -M started with a dash '-', which is used for options"); } + /* sanity check for argument: should not begin with '-' (possible + * option) */ + if (optarg && *optarg == '-') { + + FATAL( + "argument for -M started with a dash '-', which is used for " + "options"); + + } afl->sync_id = ck_strdup(optarg); afl->skip_deterministic = 0; // force deterministic fuzzing @@ -469,11 +481,23 @@ int main(int argc, char **argv_orig, char **envp) { case 'S': /* secondary sync id */ - if (afl->non_instrumented_mode) { FATAL("-S is not supported in non-instrumented mode"); } + if (afl->non_instrumented_mode) { + + FATAL("-S is not supported in non-instrumented mode"); + + } + if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } - /* sanity check for argument: should not begin with '-' (possible option) */ - if (optarg && *optarg == '-') { FATAL("argument for -M started with a dash '-', which is used for options"); } + /* sanity check for argument: should not begin with '-' (possible + * option) */ + if (optarg && *optarg == '-') { + + FATAL( + "argument for -M started with a dash '-', which is used for " + "options"); + + } afl->sync_id = ck_strdup(optarg); afl->is_secondary_node = 1; @@ -1354,7 +1378,11 @@ int main(int argc, char **argv_orig, char **envp) { } - if (!afl->fsrv.qemu_mode && !afl->non_instrumented_mode) { check_binary(afl, afl->cmplog_binary); } + if (!afl->fsrv.qemu_mode && !afl->non_instrumented_mode) { + + check_binary(afl, afl->cmplog_binary); + + } } -- cgit 1.4.1 From c06b5a156480d70e5013a780bb41bd074637475c Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 17 Nov 2020 17:02:33 +0100 Subject: fix sync issue --- src/afl-fuzz-run.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index e969994d..b96fe71a 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -590,7 +590,7 @@ void sync_fuzzers(afl_state_t *afl) { while (m < n) { - if (strcmp(namelist[m]->d_name, entry)) { + if (strncmp(namelist[m]->d_name, entry, 9)) { m++; -- cgit 1.4.1 From 54fdec0e51d17ac47582ca23c32ce10da5591aa2 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 17 Nov 2020 21:07:29 +0100 Subject: fix: avoid preprocessor logic in macro arguments (not portable) --- src/afl-cc.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 5d8d33a5..ef4d2c74 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1326,15 +1326,18 @@ int main(int argc, char **argv, char **envp) { " AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by " "filename\n"); +#if LLVM_MAJOR < 9 +#define COUNTER_BEHAVIOUR " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" +#else +#define COUNTER_BEHAVIOUR " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" +#endif if (have_llvm) SAYF( "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " "variables:\n" -#if LLVM_MAJOR < 9 - " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" -#else - " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" -#endif + + COUNTER_BEHAVIOUR + " AFL_LLVM_DICT2FILE: generate an afl dictionary based on found " "comparisons\n" " AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n" -- cgit 1.4.1 From 23f37ff5054d77abf7baf7b6d01d660b435d81cd Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 18 Nov 2020 02:33:47 +0100 Subject: fixed alloc errors, code format --- examples/afl_network_proxy/afl-network-server.c | 8 ++++---- include/alloc-inl.h | 11 ++++++----- src/afl-cc.c | 6 ++++-- src/afl-fuzz-python.c | 6 +++--- src/afl-fuzz-queue.c | 7 ++++++- src/afl-fuzz.c | 2 ++ 6 files changed, 25 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/examples/afl_network_proxy/afl-network-server.c b/examples/afl_network_proxy/afl-network-server.c index 75eb3d20..3831f985 100644 --- a/examples/afl_network_proxy/afl-network-server.c +++ b/examples/afl_network_proxy/afl-network-server.c @@ -358,8 +358,8 @@ int recv_testcase(int s, void **buf) { if ((size & 0xff000000) != 0xff000000) { - *buf = afl_realloc((void **)&buf, size); - if (unlikely(!buf)) { PFATAL("Alloc"); } + *buf = afl_realloc(buf, size); + if (unlikely(!*buf)) { PFATAL("Alloc"); } received = 0; // fprintf(stderr, "unCOMPRESS (%u)\n", size); while (received < size && @@ -371,8 +371,8 @@ int recv_testcase(int s, void **buf) { #ifdef USE_DEFLATE u32 clen; size -= 0xff000000; - *buf = afl_realloc((void **)&buf, size); - if (unlikely(!buf)) { PFATAL("Alloc"); } + *buf = afl_realloc(buf, size); + if (unlikely(!*buf)) { PFATAL("Alloc"); } received = 0; while (received < 4 && (ret = recv(s, &clen + received, 4 - received, 0)) > 0) diff --git a/include/alloc-inl.h b/include/alloc-inl.h index a6194f86..68255fb6 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -694,10 +694,11 @@ static inline void *afl_realloc(void **buf, size_t size_needed) { } /* alloc */ - struct afl_alloc_buf *newer_buf = (struct afl_alloc_buf *)realloc(new_buf, next_size); + struct afl_alloc_buf *newer_buf = + (struct afl_alloc_buf *)realloc(new_buf, next_size); if (unlikely(!newer_buf)) { - free(new_buf); // avoid a leak + free(new_buf); // avoid a leak *buf = NULL; return NULL; @@ -707,7 +708,6 @@ static inline void *afl_realloc(void **buf, size_t size_needed) { } - new_buf->complete_size = next_size; *buf = (void *)(new_buf->buf); return *buf; @@ -736,10 +736,11 @@ static inline void *afl_realloc_exact(void **buf, size_t size_needed) { if (unlikely(current_size == size_needed)) { return *buf; } /* alloc */ - struct afl_alloc_buf *newer_buf = (struct afl_alloc_buf *)realloc(new_buf, size_needed); + struct afl_alloc_buf *newer_buf = + (struct afl_alloc_buf *)realloc(new_buf, size_needed); if (unlikely(!newer_buf)) { - free(new_buf); // avoid a leak + free(new_buf); // avoid a leak *buf = NULL; return NULL; diff --git a/src/afl-cc.c b/src/afl-cc.c index ef4d2c74..9c23c18b 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1327,9 +1327,11 @@ int main(int argc, char **argv, char **envp) { "filename\n"); #if LLVM_MAJOR < 9 -#define COUNTER_BEHAVIOUR " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" + #define COUNTER_BEHAVIOUR \ + " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" #else -#define COUNTER_BEHAVIOUR " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" + #define COUNTER_BEHAVIOUR \ + " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" #endif if (have_llvm) SAYF( diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index cfaf055d..80532774 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -96,7 +96,7 @@ static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, mutated_size = PyByteArray_Size(py_value); *out_buf = afl_realloc(BUF_PARAMS(fuzz), mutated_size); - if (unlikely(!out_buf)) { PFATAL("alloc"); } + if (unlikely(!*out_buf)) { PFATAL("alloc"); } memcpy(*out_buf, PyByteArray_AsString(py_value), mutated_size); Py_DECREF(py_value); @@ -579,7 +579,7 @@ size_t trim_py(void *py_mutator, u8 **out_buf) { ret = PyByteArray_Size(py_value); *out_buf = afl_realloc(BUF_PARAMS(trim), ret); - if (unlikely(!out_buf)) { PFATAL("alloc"); } + if (unlikely(!*out_buf)) { PFATAL("alloc"); } memcpy(*out_buf, PyByteArray_AsString(py_value), ret); Py_DECREF(py_value); @@ -645,7 +645,7 @@ size_t havoc_mutation_py(void *py_mutator, u8 *buf, size_t buf_size, /* A new buf is needed... */ *out_buf = afl_realloc(BUF_PARAMS(havoc), mutated_size); - if (unlikely(!out_buf)) { PFATAL("alloc"); } + if (unlikely(!*out_buf)) { PFATAL("alloc"); } } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index c78df8be..32bed06f 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -56,7 +56,12 @@ void create_alias_table(afl_state_t *afl) { int * S = (u32 *)afl_realloc(AFL_BUF_PARAM(out_scratch), n * sizeof(u32)); int * L = (u32 *)afl_realloc(AFL_BUF_PARAM(in_scratch), n * sizeof(u32)); - if (!P || !S || !L) { FATAL("could not aquire memory for alias table"); } + if (!P || !S || !L || !afl->alias_table || !afl->alias_probability) { + + FATAL("could not aquire memory for alias table"); + + } + memset((void *)afl->alias_table, 0, n * sizeof(u32)); memset((void *)afl->alias_probability, 0, n * sizeof(double)); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index cedfdf8f..ac77bb1f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -350,6 +350,7 @@ int main(int argc, char **argv_orig, char **envp) { case 's': { + if (optarg == NULL) { FATAL("No valid seed provided. Got NULL."); } rand_set_seed(afl, strtoul(optarg, 0L, 10)); afl->fixed_seed = 1; break; @@ -419,6 +420,7 @@ int main(int argc, char **argv_orig, char **envp) { case 'i': /* input dir */ if (afl->in_dir) { FATAL("Multiple -i options not supported"); } + if (afl->in_dir == NULL) { FATAL("Invalid -i option (got NULL)."); } afl->in_dir = optarg; if (!strcmp(afl->in_dir, "-")) { afl->in_place_resume = 1; } -- cgit 1.4.1 From f80f62f14bb5222344925a7ec51c81aa2f95d86e Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 18 Nov 2020 03:02:13 +0100 Subject: renamed env var to AFL_DEBUG_CHILD --- docs/Changelog.md | 1 + docs/env_variables.md | 2 +- examples/afl_network_proxy/afl-network-server.c | 7 +++++-- include/afl-fuzz.h | 4 ++-- include/envs.h | 3 ++- instrumentation/afl-compiler-rt.o.c | 2 +- src/afl-fuzz-run.c | 2 +- src/afl-fuzz-state.c | 6 ++++-- src/afl-fuzz.c | 4 ++-- src/afl-showmap.c | 6 +++++- src/afl-tmin.c | 7 +++++-- test/test-unicorn-mode.sh | 6 +++--- 12 files changed, 32 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index baa2667b..9426ed54 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -58,6 +58,7 @@ sending a mail to . - added INTROSPECTION support for custom modules - python fuzz function was not optional, fixed - unicornafl synced with upstream (arm64 fix, better rust bindings) + - renamed AFL_DEBUG_CHILD_OUTPUT to AFL_DEBUG_CHILD ### Version ++2.68c (release) diff --git a/docs/env_variables.md b/docs/env_variables.md index 469fc957..1ce6f206 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -388,7 +388,7 @@ checks or alter some of the more exotic semantics of the tool: processing the first queue entry; and `AFL_BENCH_UNTIL_CRASH` causes it to exit soon after the first crash is found. - - Setting `AFL_DEBUG_CHILD_OUTPUT` will not suppress the child output. + - Setting `AFL_DEBUG_CHILD` will not suppress the child output. Not pretty but good for debugging purposes. - Setting `AFL_NO_CPU_RED` will not display very high cpu usages in red color. diff --git a/examples/afl_network_proxy/afl-network-server.c b/examples/afl_network_proxy/afl-network-server.c index 3831f985..513dc8f2 100644 --- a/examples/afl_network_proxy/afl-network-server.c +++ b/examples/afl_network_proxy/afl-network-server.c @@ -636,8 +636,11 @@ int main(int argc, char **argv_orig, char **envp) { if (listen(sock, 1) < 0) { PFATAL("listen() failed"); } - afl_fsrv_start(fsrv, use_argv, &stop_soon, - get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0); + afl_fsrv_start( + fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); #ifdef USE_DEFLATE compressor = libdeflate_alloc_compressor(1); diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index c355263b..b484b93e 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -362,8 +362,8 @@ typedef struct afl_env_vars { u8 afl_skip_cpufreq, afl_exit_when_done, afl_no_affinity, afl_skip_bin_check, afl_dumb_forksrv, afl_import_first, afl_custom_mutator_only, afl_no_ui, afl_force_ui, afl_i_dont_care_about_missing_crashes, afl_bench_just_one, - afl_bench_until_crash, afl_debug_child_output, afl_autoresume, - afl_cal_fast, afl_cycle_schedules, afl_expand_havoc, afl_statsd; + afl_bench_until_crash, afl_debug_child, afl_autoresume, afl_cal_fast, + afl_cycle_schedules, afl_expand_havoc, afl_statsd; u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload, diff --git a/include/envs.h b/include/envs.h index b753d5f8..8255cf4f 100644 --- a/include/envs.h +++ b/include/envs.h @@ -6,6 +6,7 @@ static char *afl_environment_deprecated[] = { "AFL_LLVM_WHITELIST", "AFL_GCC_WHITELIST", + "AFL_DEBUG_CHILD_OUTPUT", "AFL_DEFER_FORKSRV", "AFL_POST_LIBRARY", "AFL_PERSISTENT", @@ -36,7 +37,7 @@ static char *afl_environment_variables[] = { "AFL_CXX", "AFL_CYCLE_SCHEDULES", "AFL_DEBUG", - "AFL_DEBUG_CHILD_OUTPUT", + "AFL_DEBUG_CHILD", "AFL_DEBUG_GDB", "AFL_DISABLE_TRIM", "AFL_DONT_OPTIMIZE", diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 18501b65..485f500c 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -992,7 +992,7 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { // For stability analysis, if you want to know to which function unstable // edge IDs belong - uncomment, recompile+install llvm_mode, recompile // the target. libunwind and libbacktrace are better solutions. - // Set AFL_DEBUG_CHILD_OUTPUT=1 and run afl-fuzz with 2>file to capture + // Set AFL_DEBUG_CHILD=1 and run afl-fuzz with 2>file to capture // the backtrace output /* uint32_t unstable[] = { ... unstable edge IDs }; diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index b96fe71a..95b3ee8a 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -332,7 +332,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child_output); + afl->afl_env.afl_debug_child); if (afl->fsrv.support_shmem_fuzz && !afl->fsrv.use_shmem_fuzz) { diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 61bd06b7..489d4e53 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -268,11 +268,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_bench_until_crash = get_afl_env(afl_environment_variables[i]) ? 1 : 0; - } else if (!strncmp(env, "AFL_DEBUG_CHILD_OUTPUT", + } else if (!strncmp(env, "AFL_DEBUG_CHILD", + afl_environment_variable_len) || + !strncmp(env, "AFL_DEBUG_CHILD_OUTPUT", afl_environment_variable_len)) { - afl->afl_env.afl_debug_child_output = + afl->afl_env.afl_debug_child = get_afl_env(afl_environment_variables[i]) ? 1 : 0; } else if (!strncmp(env, "AFL_AUTORESUME", diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index ac77bb1f..e04ae649 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -166,7 +166,7 @@ static void usage(u8 *argv0, int more_help) { "AFL_CUSTOM_MUTATOR_ONLY: avoid AFL++'s internal mutators\n" "AFL_CYCLE_SCHEDULES: after completing a cycle, switch to a different -p schedule\n" "AFL_DEBUG: extra debugging output for Python mode trimming\n" - "AFL_DEBUG_CHILD_OUTPUT: do not suppress stdout/stderr from target\n" + "AFL_DEBUG_CHILD: do not suppress stdout/stderr from target\n" "AFL_DISABLE_TRIM: disable the trimming of test cases\n" "AFL_DUMB_FORKSRV: use fork server without feedback from target\n" "AFL_EXIT_WHEN_DONE: exit when all inputs are run and no new finds are found\n" @@ -1426,7 +1426,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; afl->cmplog_fsrv.init_child_func = cmplog_exec_child; afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child_output); + afl->afl_env.afl_debug_child); OKF("Cmplog forkserver successfully started"); } diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 4b357254..69527007 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1091,7 +1091,11 @@ int main(int argc, char **argv_orig, char **envp) { } afl_fsrv_start(fsrv, use_argv, &stop_soon, - get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0); + (get_afl_env("AFL_DEBUG_CHILD") || + get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + map_size = fsrv->map_size; if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 06037d61..e4fb068d 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -1141,8 +1141,11 @@ int main(int argc, char **argv_orig, char **envp) { read_initial_file(); - afl_fsrv_start(fsrv, use_argv, &stop_soon, - get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0); + afl_fsrv_start( + fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); diff --git a/test/test-unicorn-mode.sh b/test/test-unicorn-mode.sh index 7ac4cdd2..b4c6eb3e 100755 --- a/test/test-unicorn-mode.sh +++ b/test/test-unicorn-mode.sh @@ -7,7 +7,7 @@ test -d ../unicorn_mode/unicornafl -a -e ../unicorn_mode/unicornafl/samples/shel test -e ../unicorn_mode/samples/simple/simple_target.bin -a -e ../unicorn_mode/samples/compcov_x64/compcov_target.bin && { { # We want to see python errors etc. in logs, in case something doesn't work - export AFL_DEBUG_CHILD_OUTPUT=1 + export AFL_DEBUG_CHILD=1 # some python version should be available now PYTHONS="`command -v python3` `command -v python` `command -v python2`" @@ -34,7 +34,7 @@ test -d ../unicorn_mode/unicornafl -a -e ../unicorn_mode/unicornafl/samples/shel cd ../unicorn_mode/samples/persistent make >>errors 2>&1 $ECHO "$GREY[*] running afl-fuzz for unicorn_mode (persistent), this will take approx 25 seconds" - AFL_DEBUG_CHILD_OUTPUT=1 ../../../afl-fuzz -m none -V25 -U -i sample_inputs -o out -d -- ./harness @@ >>errors 2>&1 + AFL_DEBUG_CHILD=1 ../../../afl-fuzz -m none -V25 -U -i sample_inputs -o out -d -- ./harness @@ >>errors 2>&1 test -n "$( ls out/default/queue/id:000002* 2>/dev/null )" && { $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode (persistent)" } || { @@ -96,7 +96,7 @@ test -d ../unicorn_mode/unicornafl -a -e ../unicorn_mode/unicornafl/samples/shel } fi - unset AFL_DEBUG_CHILD_OUTPUT + unset AFL_DEBUG_CHILD } } || { -- cgit 1.4.1 From 631b6d0187ae59409549c431c657def8c30da8cf Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 18 Nov 2020 03:03:49 +0100 Subject: fixed simple yet stupid bug --- src/afl-fuzz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e04ae649..1008f28c 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -420,7 +420,7 @@ int main(int argc, char **argv_orig, char **envp) { case 'i': /* input dir */ if (afl->in_dir) { FATAL("Multiple -i options not supported"); } - if (afl->in_dir == NULL) { FATAL("Invalid -i option (got NULL)."); } + if (optarg == NULL) { FATAL("Invalid -i option (got NULL)."); } afl->in_dir = optarg; if (!strcmp(afl->in_dir, "-")) { afl->in_place_resume = 1; } -- cgit 1.4.1 From 108a89b55959ed41fa3b050696148e698e6de2df Mon Sep 17 00:00:00 2001 From: hexcoder Date: Wed, 18 Nov 2020 08:33:06 +0100 Subject: typo --- src/afl-fuzz-queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 32bed06f..f35b4f57 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -58,7 +58,7 @@ void create_alias_table(afl_state_t *afl) { if (!P || !S || !L || !afl->alias_table || !afl->alias_probability) { - FATAL("could not aquire memory for alias table"); + FATAL("could not acquire memory for alias table"); } -- cgit 1.4.1 From b260204b728efcc4ba13dfa77692cfc5721db606 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Wed, 18 Nov 2020 18:13:03 +0000 Subject: Solaris/Illumos build fix. (#609) --- include/afl-fuzz.h | 1 + src/afl-cc.c | 2 +- src/afl-fuzz-init.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index b484b93e..423230f1 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -113,6 +113,7 @@ #include #include #include + #include #endif #endif /* __linux__ */ diff --git a/src/afl-cc.c b/src/afl-cc.c index 9c23c18b..19dc9a6a 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -837,7 +837,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - #ifndef __APPLE__ + #if !defined(__APPLE__) && !defined(__sun) if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 6884bb1d..0360cdb0 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -355,7 +355,7 @@ void bind_to_free_cpu(afl_state_t *afl) { if (ncpus > sizeof(cpu_used)) ncpus = sizeof(cpu_used); - for (i = 0; i < ncpus; i++) { + for (i = 0; i < (s32)ncpus; i++) { k = kstat_lookup(m, "cpu_stat", i, NULL); if (kstat_read(m, k, &cs)) { -- cgit 1.4.1 From cd0a25be5e9b05a2ab6a11592cd95e7f653bf42d Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Wed, 18 Nov 2020 14:29:17 -0500 Subject: Use buffer protocol to retrieve result from python post_process (#605) Saves an extra copy, gives post processing functions more flexibility --- docs/custom_mutators.md | 3 +++ include/afl-fuzz.h | 3 +-- src/afl-fuzz-python.c | 34 ++++++++++++++++++++++++---------- 3 files changed, 28 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index 2516e511..53f783fe 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -130,6 +130,9 @@ def introspection(): `post_process` function. This function is then transforming the data into the format expected by the API before executing the target. + This can return any python object that implements the buffer protocol and + supports PyBUF_SIMPLE. These include bytes, bytearray, etc. + - `queue_new_entry` (optional): This methods is called after adding a new test case to the queue. diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 423230f1..933af65d 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -326,8 +326,7 @@ typedef struct py_mutator { u8 * fuzz_buf; size_t fuzz_size; - u8 * post_process_buf; - size_t post_process_size; + Py_buffer post_process_buf; u8 * trim_buf; size_t trim_size; diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index 80532774..9ac4403b 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -134,6 +134,18 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { PyObject * py_module = py->py_module; PyObject **py_functions = py->py_functions; + // initialize the post process buffer; ensures it's always valid + PyObject *unused_bytes = PyByteArray_FromStringAndSize("OHAI", 4); + if (!unused_bytes) { FATAL("allocation failed!"); } + if (PyObject_GetBuffer(unused_bytes, &py->post_process_buf, PyBUF_SIMPLE) == + -1) { + + FATAL("buffer initialization failed"); + + } + + Py_DECREF(unused_bytes); + if (py_module != NULL) { u8 py_notrim = 0, py_idx; @@ -313,7 +325,6 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, struct custom_mutator *mutator; mutator = ck_alloc(sizeof(struct custom_mutator)); - mutator->post_process_buf = NULL; mutator->name = module_name; ACTF("Loading Python mutator library from '%s'...", module_name); @@ -403,10 +414,13 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, size_t post_process_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf) { - size_t py_out_buf_size; PyObject * py_args, *py_value; py_mutator_t *py = (py_mutator_t *)py_mutator; + // buffer returned previously must be released; initialized during init + // so we don't need to do comparisons + PyBuffer_Release(&py->post_process_buf); + py_args = PyTuple_New(1); py_value = PyByteArray_FromStringAndSize(buf, buf_size); if (!py_value) { @@ -426,20 +440,20 @@ size_t post_process_py(void *py_mutator, u8 *buf, size_t buf_size, if (py_value != NULL) { - py_out_buf_size = PyByteArray_Size(py_value); + if (PyObject_GetBuffer(py_value, &py->post_process_buf, PyBUF_SIMPLE) == + -1) { - if (unlikely(!afl_realloc(BUF_PARAMS(post_process), py_out_buf_size))) { - - PFATAL("alloc"); + PyErr_Print(); + FATAL( + "Python custom mutator: post_process call return value not a " + "bytes-like object"); } - memcpy(py->post_process_buf, PyByteArray_AsString(py_value), - py_out_buf_size); Py_DECREF(py_value); - *out_buf = py->post_process_buf; - return py_out_buf_size; + *out_buf = (u8 *)py->post_process_buf.buf; + return py->post_process_buf.len; } else { -- cgit 1.4.1 From e32b7eeb83c0571a2bdaadfd5b7b769fec1405cc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 19 Nov 2020 16:14:19 +0100 Subject: fixed child not killed with -c --- docs/Changelog.md | 1 + instrumentation/afl-compiler-rt.o.c | 22 ++++++++++++++++++++-- src/afl-fuzz.c | 9 ++++++--- 3 files changed, 27 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 9426ed54..3c20f8bd 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -38,6 +38,7 @@ sending a mail to . - added INTROSPECTION make target that writes all mutations to out/NAME/introspection.txt - print special compile time options used in help output + - when using -c cmplog, one of the childs was not killed, fixed - somewhere we broke -n dumb fuzzing, fixed - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 485f500c..b07aeb83 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -101,6 +101,11 @@ int __afl_sharedmem_fuzzing __attribute__((weak)); struct cmp_map *__afl_cmp_map; +/* Child pid? */ + +static s32 child_pid; +static void (*old_sigterm_handler)(int) = 0; + /* Running in persistent mode? */ static u8 is_persistent; @@ -109,6 +114,14 @@ static u8 is_persistent; static u8 _is_sancov; +/* ensure we kill the child on termination */ + +void at_exit(int signal) { + + if (child_pid > 0) { kill(child_pid, SIGKILL); } + +} + /* Uninspired gcc plugin instrumentation */ void __afl_trace(const u32 x) { @@ -432,7 +445,6 @@ static void __afl_map_shm(void) { static void __afl_start_snapshots(void) { static u8 tmp[4] = {0, 0, 0, 0}; - s32 child_pid; u32 status = 0; u32 already_read_first = 0; u32 was_killed; @@ -579,6 +591,7 @@ static void __afl_start_snapshots(void) { //(void)nice(-20); // does not seem to improve signal(SIGCHLD, old_sigchld_handler); + signal(SIGTERM, old_sigterm_handler); close(FORKSRV_FD); close(FORKSRV_FD + 1); @@ -633,6 +646,11 @@ static void __afl_start_snapshots(void) { static void __afl_start_forkserver(void) { + struct sigaction orig_action; + sigaction(SIGTERM, NULL, &orig_action); + old_sigterm_handler = orig_action.sa_handler; + signal(SIGTERM, at_exit); + #ifdef __linux__ if (/*!is_persistent &&*/ !__afl_cmp_map && !getenv("AFL_NO_SNAPSHOT") && afl_snapshot_init() >= 0) { @@ -645,7 +663,6 @@ static void __afl_start_forkserver(void) { #endif u8 tmp[4] = {0, 0, 0, 0}; - s32 child_pid; u32 status = 0; u32 already_read_first = 0; u32 was_killed; @@ -793,6 +810,7 @@ static void __afl_start_forkserver(void) { //(void)nice(-20); signal(SIGCHLD, old_sigchld_handler); + signal(SIGTERM, old_sigterm_handler); close(FORKSRV_FD); close(FORKSRV_FD + 1); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 1008f28c..b60908da 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -40,7 +40,7 @@ extern u64 time_spent_working; static void at_exit() { - int i; + s32 i, pid1 = 0, pid2 = 0; char *list[4] = {SHM_ENV_VAR, SHM_FUZZ_ENV_VAR, CMPLOG_SHM_ENV_VAR, NULL}; char *ptr; @@ -48,10 +48,10 @@ static void at_exit() { if (ptr && *ptr) unlink(ptr); ptr = getenv("__AFL_TARGET_PID1"); - if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL); + if (ptr && *ptr && (pid1 = atoi(ptr)) > 0) kill(pid1, SIGTERM); ptr = getenv("__AFL_TARGET_PID2"); - if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL); + if (ptr && *ptr && (pid2 = atoi(ptr)) > 0) kill(pid2, SIGTERM); i = 0; while (list[i] != NULL) { @@ -75,6 +75,9 @@ static void at_exit() { } + if (pid1 > 0) { kill(pid1, SIGKILL); } + if (pid2 > 0) { kill(pid2, SIGKILL); } + } /* Display usage hints. */ -- cgit 1.4.1 From ed2f82eaf40a49d76d7615370ea8749c2f1fefaa Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 24 Nov 2020 16:13:58 +0100 Subject: fix compiler warning turned error on NetBSD --- src/afl-fuzz-run.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 95b3ee8a..b716b8c8 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -484,7 +484,7 @@ void sync_fuzzers(afl_state_t *afl) { DIR * sd; struct dirent *sd_ent; u32 sync_cnt = 0, synced = 0, entries = 0; - u8 path[PATH_MAX + 256]; + u8 path[PATH_MAX + 1 + NAME_MAX]; sd = opendir(afl->sync_dir); if (!sd) { PFATAL("Unable to open '%s'", afl->sync_dir); } -- cgit 1.4.1 From 27c3423fb6f1cf568cf0e50b16a82c035945ae7c Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 24 Nov 2020 19:38:55 +0100 Subject: test-pre.sh: remove old uses of afl-clang, afl-cc.c: add missing env.var. AFL_LLVM_LAF_ALL --- src/afl-cc.c | 6 +++--- test/test-pre.sh | 11 ++--------- 2 files changed, 5 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 19dc9a6a..1ad43c43 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -686,7 +686,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || - getenv("LAF_TRANSFORM_COMPARES") || lto_mode) { + getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_ALL") || lto_mode) { cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; @@ -1030,7 +1030,7 @@ int main(int argc, char **argv, char **envp) { if (instrument_mode == 0) instrument_mode = INSTRUMENT_PCGUARD; else if (instrument_mode != INSTRUMENT_PCGUARD) - FATAL("you can not set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together"); + FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together"); } @@ -1049,7 +1049,7 @@ int main(int argc, char **argv, char **envp) { instrument_mode = INSTRUMENT_CFG; else if (instrument_mode != INSTRUMENT_CFG) FATAL( - "you can not set AFL_LLVM_INSTRUMENT and AFL_LLVM_INSTRIM together"); + "you cannot set AFL_LLVM_INSTRUMENT and AFL_LLVM_INSTRIM together"); } diff --git a/test/test-pre.sh b/test/test-pre.sh index fc14ee0b..fc84cb47 100755 --- a/test/test-pre.sh +++ b/test/test-pre.sh @@ -106,14 +106,7 @@ export AFL_LLVM_INSTRUMENT=AFL test -e /usr/local/bin/opt && { export PATH="/usr/local/bin:${PATH}" } -# on MacOS X we prefer afl-clang over afl-gcc, because -# afl-gcc does not work there -test `uname -s` = 'Darwin' -o `uname -s` = 'FreeBSD' && { - AFL_GCC=afl-clang -} || { - AFL_GCC=afl-gcc -} -command -v gcc >/dev/null 2>&1 || AFL_GCC=afl-clang +AFL_GCC=afl-gcc SYS=`uname -m` @@ -135,4 +128,4 @@ test -z "$SYS" && $ECHO "$YELLOW[-] uname -m did not succeed" CODE=0 INCOMPLETE=0 -fi \ No newline at end of file +fi -- cgit 1.4.1 From ded80870a933b83d8186a160f7717641ce441a81 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sun, 1 Nov 2020 06:22:18 +0100 Subject: reenable afl-clang(++) --- GNUmakefile | 4 +++- GNUmakefile.llvm | 2 ++ src/afl-cc.c | 43 ++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 45 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index 764c9baa..00753241 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -538,7 +538,7 @@ all_done: test_build .PHONY: clean clean: - rm -f $(PROGS) libradamsa.so afl-fuzz-document afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-g++-fast ld *.so *.8 test/unittests/*.o test/unittests/unit_maybe_alloc test/unittests/preallocable .afl-* afl-gcc afl-g++ test/unittests/unit_hash test/unittests/unit_rand + rm -f $(PROGS) libradamsa.so afl-fuzz-document afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-g++-fast ld *.so *.8 test/unittests/*.o test/unittests/unit_maybe_alloc test/unittests/preallocable .afl-* afl-gcc afl-g++ afl-clang afl-clang++ test/unittests/unit_hash test/unittests/unit_rand -$(MAKE) -f GNUmakefile.llvm clean -$(MAKE) -f GNUmakefile.gcc_plugin clean $(MAKE) -C libdislocator clean @@ -633,6 +633,8 @@ install: all $(MANPAGES) -$(MAKE) -f GNUmakefile.gcc_plugin install ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-gcc ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-g++ + ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang + ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang++ @mkdir -m 0755 -p ${DESTDIR}$(MAN_PATH) install -m0644 *.8 ${DESTDIR}$(MAN_PATH) install -m 755 afl-as $${DESTDIR}$(HELPER_PATH) diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index cc28695d..5ab998bb 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -361,6 +361,8 @@ instrumentation/afl-common.o: ./src/afl-common.c @ln -sf afl-cc ./afl-c++ @ln -sf afl-cc ./afl-gcc @ln -sf afl-cc ./afl-g++ + @ln -sf afl-cc ./afl-clang + @ln -sf afl-cc ./afl-clang++ @ln -sf afl-cc ./afl-clang-fast @ln -sf afl-cc ./afl-clang-fast++ ifneq "$(AFL_CLANG_FLTO)" "" diff --git a/src/afl-cc.c b/src/afl-cc.c index 1ad43c43..25ed923a 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -48,6 +48,7 @@ static u8 * obj_path; /* Path to runtime libraries */ static u8 **cc_params; /* Parameters passed to the real CC */ static u32 cc_par_cnt = 1; /* Param count, including argv0 */ +static u8 clang_mode; /* Invoked as afl-clang*? */ static u8 llvm_fullpath[PATH_MAX]; static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode; static u8 compiler_mode, plusplus_mode, have_instr_env = 0; @@ -288,7 +289,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode >= GCC_PLUGIN) { - alt_cxx = "g++"; + if (compiler_mode == GCC) { + + alt_cxx = clang_mode ? "clang++" : "g++"; + + } else { + + alt_cxx = "g++"; + + } } else { @@ -313,7 +322,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode >= GCC_PLUGIN) { - alt_cc = "gcc"; + if (compiler_mode == GCC) { + + alt_cc = clang_mode ? "clang" : "gcc"; + + } else { + + alt_cc = "gcc"; + + } } else { @@ -337,6 +354,11 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-B"; cc_params[cc_par_cnt++] = obj_path; + if (clang_mode) { + + cc_params[cc_par_cnt++] = "-no-integrated-as"; + + } } if (compiler_mode == GCC_PLUGIN) { @@ -934,7 +956,9 @@ int main(int argc, char **argv, char **envp) { } else if (strncmp(callname, "afl-gcc", 7) == 0 || - strncmp(callname, "afl-g++", 7) == 0) { + strncmp(callname, "afl-g++", 7) == 0 || + + strncmp(callname, "afl-clang", 9) == 0) { compiler_mode = GCC; @@ -978,6 +1002,19 @@ int main(int argc, char **argv, char **envp) { } + + if (strncmp(callname, "afl-clang", 9) == 0) { + + clang_mode = 1; + + if (strncmp(callname, "afl-clang++", 11) == 0) { + + plusplus_mode = 1; + + } + + } + for (i = 1; i < argc; i++) { if (strncmp(argv[i], "--afl", 5) == 0) { -- cgit 1.4.1 From 0fd98ae8b070b05a72b2c47a76f4ea145f9d51c2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 1 Nov 2020 21:34:08 +0100 Subject: added mutation introspection make target --- GNUmakefile | 6 + README.md | 1 + docs/Changelog.md | 2 + include/afl-fuzz.h | 6 + include/alloc-inl.h | 36 ++ instrumentation/SanitizerCoveragePCGUARD.so.cc | 14 +- src/afl-fuzz-bitmap.c | 11 + src/afl-fuzz-extras.c | 16 +- src/afl-fuzz-one.c | 503 ++++++++++++++++++++++++- src/afl-fuzz.c | 17 + 10 files changed, 592 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index c8d155e4..764c9baa 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -110,6 +110,11 @@ ifdef PROFILING LDFLAGS += -pg endif +ifdef INTROSPECTION + $(info Compiling with introspection documentation) + CFLAGS_OPT += -DINTROSPECTION=1 +endif + ifneq "$(shell uname -m)" "x86_64" ifneq "$(patsubst i%86,i386,$(shell uname -m))" "i386" ifneq "$(shell uname -m)" "amd64" @@ -348,6 +353,7 @@ help: @echo ASAN_BUILD - compiles with memory sanitizer for debug purposes @echo DEBUG - no optimization, -ggdb3, all warnings and -Werror @echo PROFILING - compile afl-fuzz with profiling information + @echo INTROSPECTION - compile afl-fuzz with mutation introspection @echo NO_PYTHON - disable python support @echo NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing @echo AFL_NO_X86 - if compiling on non-intel/amd platforms diff --git a/README.md b/README.md index 7c3b6ecf..d954a236 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,7 @@ These build options exist: * ASAN_BUILD - compiles with memory sanitizer for debug purposes * DEBUG - no optimization, -ggdb3, all warnings and -Werror * PROFILING - compile with profiling information (gprof) +* INTROSPECTION - compile afl-fuzz with mutation introspection * NO_PYTHON - disable python support * NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing * AFL_NO_X86 - if compiling on non-intel/amd platforms diff --git a/docs/Changelog.md b/docs/Changelog.md index 798a056f..f11a1178 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -35,6 +35,8 @@ sending a mail to . skipped. They are used for splicing though. - set the default power schedule to the superiour "seek" schedule - added NO_SPLICING compile option and makefile define + - added INTROSPECTION make target that writes all mutations to + out/NAME/introspection.txt - print special compile time options used in help output - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 85b31795..5ff7672b 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -717,6 +717,12 @@ typedef struct afl_state { * is too large) */ struct queue_entry **q_testcase_cache; +#ifdef INTROSPECTION + char mutation[8072]; + char m_tmp[4096]; + FILE *introspection_file; +#endif + } afl_state_t; struct custom_mutator { diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 36e47810..d7aa51a7 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -708,6 +708,42 @@ static inline void *afl_realloc(void **buf, size_t size_needed) { } +/* afl_realloc_exact uses afl alloc buffers but sets it to a specific size */ + +static inline void *afl_realloc_exact(void **buf, size_t size_needed) { + + struct afl_alloc_buf *new_buf = NULL; + + size_t current_size = 0; + + if (likely(*buf)) { + + /* the size is always stored at buf - 1*size_t */ + new_buf = (struct afl_alloc_buf *)afl_alloc_bufptr(*buf); + current_size = new_buf->complete_size; + + } + + size_needed += AFL_ALLOC_SIZE_OFFSET; + + /* No need to realloc */ + if (unlikely(current_size == size_needed)) { return *buf; } + + /* alloc */ + new_buf = (struct afl_alloc_buf *)realloc(new_buf, size_needed); + if (unlikely(!new_buf)) { + + *buf = NULL; + return NULL; + + } + + new_buf->complete_size = size_needed; + *buf = (void *)(new_buf->buf); + return *buf; + +} + static inline void afl_free(void *buf) { if (buf) { free(afl_alloc_bufptr(buf)); } diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index 97e8d32b..2f87e4f9 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -247,13 +247,13 @@ SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) { Options.CoverageType = SanitizerCoverageOptions::SCK_Edge; // std::max(Options.CoverageType, // CLOpts.CoverageType); - Options.IndirectCalls = true; // CLOpts.IndirectCalls; - Options.TraceCmp = false; //|= ClCMPTracing; - Options.TraceDiv = false; //|= ClDIVTracing; - Options.TraceGep = false; //|= ClGEPTracing; - Options.TracePC = false; //|= ClTracePC; - Options.TracePCGuard = true; // |= ClTracePCGuard; - Options.Inline8bitCounters = 0; //|= ClInline8bitCounters; + Options.IndirectCalls = true; // CLOpts.IndirectCalls; + Options.TraceCmp = false; //|= ClCMPTracing; + Options.TraceDiv = false; //|= ClDIVTracing; + Options.TraceGep = false; //|= ClGEPTracing; + Options.TracePC = false; //|= ClTracePC; + Options.TracePCGuard = true; // |= ClTracePCGuard; + Options.Inline8bitCounters = 0; //|= ClInline8bitCounters; // Options.InlineBoolFlag = 0; //|= ClInlineBoolFlag; Options.PCTable = false; //|= ClCreatePCTable; Options.NoPrune = false; //|= !ClPruneBlocks; diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 2653b9fd..735420c3 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -587,6 +587,11 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { add_to_queue(afl, queue_fn, len, 0); +#ifdef INTROSPECTION + fprintf(afl->introspection_file, "QUEUE %s = %s\n", afl->mutation, + afl->queue_top->fname); +#endif + if (hnb == 2) { afl->queue_top->has_new_cov = 1; @@ -659,6 +664,9 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { } ++afl->unique_tmouts; +#ifdef INTROSPECTION + fprintf(afl->introspection_file, "UNIQUE_TIMEOUT %s\n", afl->mutation); +#endif /* Before saving, we make sure that it's a genuine hang by re-running the target with a more generous timeout (unless the default timeout @@ -742,6 +750,9 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { #endif /* ^!SIMPLE_FILES */ ++afl->unique_crashes; +#ifdef INTROSPECTION + fprintf(afl->introspection_file, "UNIQUE_CRASH %s\n", afl->mutation); +#endif if (unlikely(afl->infoexec)) { // if the user wants to be informed on new crashes - do that diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index adec986e..171cce96 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -423,8 +423,8 @@ void dedup_extras(afl_state_t *afl) { } if (afl->extras_cnt != orig_cnt) - afl->extras = ck_realloc((void **)&afl->extras, - afl->extras_cnt * sizeof(struct extra_data)); + afl->extras = afl_realloc_exact( + (void **)&afl->extras, afl->extras_cnt * sizeof(struct extra_data)); } @@ -462,16 +462,8 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { } - if (afl->extras) { - - afl->extras = ck_realloc((void **)&afl->extras, - (afl->extras_cnt + 1) * sizeof(struct extra_data)); - - } else { - - afl->extras = ck_alloc((afl->extras_cnt + 1) * sizeof(struct extra_data)); - - } + afl->extras = afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); if (unlikely(!afl->extras)) { PFATAL("alloc"); } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 0f3393d2..5337b7f8 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -609,6 +609,11 @@ u8 fuzz_one_original(afl_state_t *afl) { FLIP_BIT(out_buf, afl->stage_cur); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT1 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -718,6 +723,11 @@ u8 fuzz_one_original(afl_state_t *afl) { FLIP_BIT(out_buf, afl->stage_cur); FLIP_BIT(out_buf, afl->stage_cur + 1); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT2 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -747,6 +757,11 @@ u8 fuzz_one_original(afl_state_t *afl) { FLIP_BIT(out_buf, afl->stage_cur + 2); FLIP_BIT(out_buf, afl->stage_cur + 3); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT4 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -802,6 +817,11 @@ u8 fuzz_one_original(afl_state_t *afl) { out_buf[afl->stage_cur] ^= 0xFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT8 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } /* We also use this stage to pull off a simple trick: we identify @@ -889,6 +909,11 @@ u8 fuzz_one_original(afl_state_t *afl) { *(u16 *)(out_buf + i) ^= 0xFFFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT16 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -927,6 +952,11 @@ u8 fuzz_one_original(afl_state_t *afl) { *(u32 *)(out_buf + i) ^= 0xFFFFFFFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT32 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -985,6 +1015,11 @@ skip_bitflip: afl->stage_cur_val = j; out_buf[i] = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH8+ %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1001,6 +1036,11 @@ skip_bitflip: afl->stage_cur_val = -j; out_buf[i] = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH8- %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1065,6 +1105,11 @@ skip_bitflip: afl->stage_cur_val = j; *(u16 *)(out_buf + i) = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16+ %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1079,6 +1124,11 @@ skip_bitflip: afl->stage_cur_val = -j; *(u16 *)(out_buf + i) = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16- %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1097,6 +1147,11 @@ skip_bitflip: afl->stage_cur_val = j; *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) + j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16+BE %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1111,6 +1166,11 @@ skip_bitflip: afl->stage_cur_val = -j; *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) - j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16-BE %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1174,6 +1234,11 @@ skip_bitflip: afl->stage_cur_val = j; *(u32 *)(out_buf + i) = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32+ %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1188,6 +1253,11 @@ skip_bitflip: afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32- %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1206,6 +1276,11 @@ skip_bitflip: afl->stage_cur_val = j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) + j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32+BE %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1220,6 +1295,11 @@ skip_bitflip: afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) - j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32-BE %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1287,6 +1367,11 @@ skip_arith: afl->stage_cur_val = interesting_8[j]; out_buf[i] = interesting_8[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING8 %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } out_buf[i] = orig; @@ -1342,6 +1427,11 @@ skip_arith: *(u16 *)(out_buf + i) = interesting_16[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING16 %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1358,6 +1448,11 @@ skip_arith: afl->stage_val_type = STAGE_VAL_BE; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s INTERESTING16BE %u %u", afl->queue_cur->fname, i, j); +#endif + *(u16 *)(out_buf + i) = SWAP16(interesting_16[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1421,6 +1516,11 @@ skip_arith: *(u32 *)(out_buf + i) = interesting_32[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING32 %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1437,6 +1537,11 @@ skip_arith: afl->stage_val_type = STAGE_VAL_BE; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s INTERESTING32BE %u %u", afl->queue_cur->fname, i, j); +#endif + *(u32 *)(out_buf + i) = SWAP32(interesting_32[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1510,6 +1615,12 @@ skip_interest: last_len = afl->extras[j].len; memcpy(out_buf + i, afl->extras[j].data, last_len); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, i, j, + afl->extras[j].data); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1557,6 +1668,12 @@ skip_interest: /* Copy tail */ memcpy(ex_tmp + i + afl->extras[j].len, out_buf + i, len - i); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s EXTRAS insert %u %u:%s", afl->queue_cur->fname, i, j, + afl->extras[j].data); +#endif + if (common_fuzz_stuff(afl, ex_tmp, len + afl->extras[j].len)) { goto abandon_entry; @@ -1614,6 +1731,12 @@ skip_user_extras: last_len = afl->a_extras[j].len; memcpy(out_buf + i, afl->a_extras[j].data, last_len); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s AUTO_EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, i, j, + afl->a_extras[j].data); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1675,7 +1798,7 @@ custom_mutator_stage: for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { - struct queue_entry *target; + struct queue_entry *target = NULL; u32 tid; u8 * new_buf = NULL; u32 target_len = 0; @@ -1717,6 +1840,12 @@ custom_mutator_stage: if (mutated_size > 0) { +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s CUSTOM %s", + afl->queue_cur->fname, + target != NULL ? (char *)target->fname : "none"); +#endif + if (common_fuzz_stuff(afl, mutated_buf, (u32)mutated_size)) { goto abandon_entry; @@ -1866,6 +1995,11 @@ havoc_stage: afl->stage_cur_val = use_stacking; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s HAVOC %u", + afl->queue_cur->fname, use_stacking); +#endif + for (i = 0; i < use_stacking; ++i) { if (afl->custom_mutators_count) { @@ -1909,6 +2043,10 @@ havoc_stage: /* Flip a single bit somewhere. Spooky! */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT1"); + strcat(afl->mutation, afl->m_tmp); +#endif FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); break; @@ -1916,6 +2054,10 @@ havoc_stage: /* Set byte to interesting value. */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING8"); + strcat(afl->mutation, afl->m_tmp); +#endif out_buf[rand_below(afl, temp_len)] = interesting_8[rand_below(afl, sizeof(interesting_8))]; break; @@ -1928,11 +2070,19 @@ havoc_stage: if (rand_below(afl, 2)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]; } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16BE"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = SWAP16( interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]); @@ -1948,11 +2098,19 @@ havoc_stage: if (rand_below(afl, 2)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]; } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32BE"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = SWAP32( interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]); @@ -1964,6 +2122,10 @@ havoc_stage: /* Randomly subtract from byte. */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8-"); + strcat(afl->mutation, afl->m_tmp); +#endif out_buf[rand_below(afl, temp_len)] -= 1 + rand_below(afl, ARITH_MAX); break; @@ -1971,6 +2133,10 @@ havoc_stage: /* Randomly add to byte. */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8+"); + strcat(afl->mutation, afl->m_tmp); +#endif out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); break; @@ -1984,6 +2150,10 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16-_%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { @@ -1991,6 +2161,11 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16-BE_%u_%u", pos, + num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) = SWAP16(SWAP16(*(u16 *)(out_buf + pos)) - num); @@ -2008,6 +2183,10 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+_%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { @@ -2015,6 +2194,11 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+BE_%u_%u", pos, + num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) = SWAP16(SWAP16(*(u16 *)(out_buf + pos)) + num); @@ -2032,6 +2216,10 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32-_%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { @@ -2039,6 +2227,11 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32-BE_%u_%u", pos, + num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) = SWAP32(SWAP32(*(u32 *)(out_buf + pos)) - num); @@ -2056,6 +2249,10 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+_%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { @@ -2063,6 +2260,11 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+BE_%u_%u", pos, + num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) = SWAP32(SWAP32(*(u32 *)(out_buf + pos)) + num); @@ -2076,6 +2278,10 @@ havoc_stage: why not. We use XOR with 1-255 to eliminate the possibility of a no-op. */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " RAND8"); + strcat(afl->mutation, afl->m_tmp); +#endif out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); break; @@ -2095,6 +2301,11 @@ havoc_stage: del_from = rand_below(afl, temp_len - del_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " DEL_%u_%u", del_from, + del_len); + strcat(afl->mutation, afl->m_tmp); +#endif memmove(out_buf + del_from, out_buf + del_from + del_len, temp_len - del_from - del_len); @@ -2128,6 +2339,12 @@ havoc_stage: clone_to = rand_below(afl, temp_len); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE_%s_%u_%u_%u", + actually_clone ? "clone" : "insert", clone_from, clone_to, + clone_len); + strcat(afl->mutation, afl->m_tmp); +#endif new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), temp_len + clone_len); if (unlikely(!new_buf)) { PFATAL("alloc"); } @@ -2181,12 +2398,23 @@ havoc_stage: if (copy_from != copy_to) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " OVERWRITE_COPY_%u_%u_%u", copy_from, copy_to, + copy_len); + strcat(afl->mutation, afl->m_tmp); +#endif memmove(out_buf + copy_to, out_buf + copy_from, copy_len); } } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " OVERWRITE_FIXED_%u_%u_%u", copy_from, copy_to, copy_len); + strcat(afl->mutation, afl->m_tmp); +#endif memset(out_buf + copy_to, rand_below(afl, 2) ? rand_below(afl, 256) : out_buf[rand_below(afl, temp_len)], @@ -2222,6 +2450,12 @@ havoc_stage: if ((s32)extra_len > temp_len) { break; } insert_at = rand_below(afl, temp_len - extra_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " AUTO_EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, + afl->a_extras[use_extra].data); + strcat(afl->mutation, afl->m_tmp); +#endif memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, extra_len); @@ -2236,6 +2470,12 @@ havoc_stage: if ((s32)extra_len > temp_len) { break; } insert_at = rand_below(afl, temp_len - extra_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, + afl->a_extras[use_extra].data); + strcat(afl->mutation, afl->m_tmp); +#endif memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len); @@ -2258,12 +2498,23 @@ havoc_stage: use_extra = rand_below(afl, afl->a_extras_cnt); extra_len = afl->a_extras[use_extra].len; ptr = afl->a_extras[use_extra].data; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " AUTO_EXTRA_INSERT_%u_%u_%s", insert_at, extra_len, + ptr); + strcat(afl->mutation, afl->m_tmp); +#endif } else { use_extra = rand_below(afl, afl->extras_cnt); extra_len = afl->extras[use_extra].len; ptr = afl->extras[use_extra].data; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " EXTRA_INSERT_%u_%u_%s", insert_at, extra_len, ptr); + strcat(afl->mutation, afl->m_tmp); +#endif } @@ -2324,6 +2575,12 @@ havoc_stage: copy_from = rand_below(afl, new_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " SPLICE_OVERWRITE_%u_%u_%u_%s", copy_from, copy_to, + copy_len, target->fname); + strcat(afl->mutation, afl->m_tmp); +#endif memmove(out_buf + copy_to, new_buf + copy_from, copy_len); } else { @@ -2340,6 +2597,12 @@ havoc_stage: temp_len + clone_len + 1); if (unlikely(!temp_buf)) { PFATAL("alloc"); } +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " SPLICE_INSERT_%u_%u_%u_%s", clone_from, clone_to, + clone_len, target->fname); + strcat(afl->mutation, afl->m_tmp); +#endif /* Head */ memcpy(temp_buf, out_buf, clone_to); @@ -2755,6 +3018,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { FLIP_BIT(out_buf, afl->stage_cur); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT1 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -2864,6 +3131,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { FLIP_BIT(out_buf, afl->stage_cur); FLIP_BIT(out_buf, afl->stage_cur + 1); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT2 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -2893,6 +3164,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { FLIP_BIT(out_buf, afl->stage_cur + 2); FLIP_BIT(out_buf, afl->stage_cur + 3); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT4 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -2948,6 +3223,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { out_buf[afl->stage_cur] ^= 0xFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT8 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } /* We also use this stage to pull off a simple trick: we identify @@ -3035,6 +3314,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { *(u16 *)(out_buf + i) ^= 0xFFFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT16 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3073,6 +3356,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { *(u32 *)(out_buf + i) ^= 0xFFFFFFFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT32 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3131,6 +3418,10 @@ skip_bitflip: afl->stage_cur_val = j; out_buf[i] = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH8+ %u %u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3147,6 +3438,10 @@ skip_bitflip: afl->stage_cur_val = -j; out_buf[i] = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH8- %u %u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3211,6 +3506,10 @@ skip_bitflip: afl->stage_cur_val = j; *(u16 *)(out_buf + i) = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH16+ %u %u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3225,6 +3524,10 @@ skip_bitflip: afl->stage_cur_val = -j; *(u16 *)(out_buf + i) = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH16- %u %u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3243,6 +3546,10 @@ skip_bitflip: afl->stage_cur_val = j; *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) + j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_ARITH16+BE %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3257,6 +3564,10 @@ skip_bitflip: afl->stage_cur_val = -j; *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) - j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_ARITH16-BE %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3320,6 +3631,10 @@ skip_bitflip: afl->stage_cur_val = j; *(u32 *)(out_buf + i) = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH32+ %u %u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3334,6 +3649,10 @@ skip_bitflip: afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH32- %u %u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3352,6 +3671,10 @@ skip_bitflip: afl->stage_cur_val = j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) + j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_ARITH32+BE %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3366,6 +3689,10 @@ skip_bitflip: afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) - j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_ARITH32-BE %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3433,6 +3760,10 @@ skip_arith: afl->stage_cur_val = interesting_8[j]; out_buf[i] = interesting_8[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING8 %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } out_buf[i] = orig; @@ -3488,6 +3819,10 @@ skip_arith: *(u16 *)(out_buf + i) = interesting_16[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING16 %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3504,6 +3839,10 @@ skip_arith: afl->stage_val_type = STAGE_VAL_BE; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING16BE %u %u", afl->queue_cur->fname, i, j); +#endif *(u16 *)(out_buf + i) = SWAP16(interesting_16[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3567,6 +3906,10 @@ skip_arith: *(u32 *)(out_buf + i) = interesting_32[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING32 %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3583,6 +3926,10 @@ skip_arith: afl->stage_val_type = STAGE_VAL_BE; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING32BE %u %u", afl->queue_cur->fname, i, j); +#endif *(u32 *)(out_buf + i) = SWAP32(interesting_32[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3656,6 +4003,11 @@ skip_interest: last_len = afl->extras[j].len; memcpy(out_buf + i, afl->extras[j].data, last_len); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, i, j, + afl->extras[j].data); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3703,6 +4055,11 @@ skip_interest: /* Copy tail */ memcpy(ex_tmp + i + afl->extras[j].len, out_buf + i, len - i); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_EXTRAS insert %u %u:%s", afl->queue_cur->fname, i, j, + afl->extras[j].data); +#endif if (common_fuzz_stuff(afl, ex_tmp, len + afl->extras[j].len)) { goto abandon_entry; @@ -3759,6 +4116,11 @@ skip_user_extras: last_len = afl->a_extras[j].len; memcpy(out_buf + i, afl->a_extras[j].data, last_len); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_AUTO_EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, + i, j, afl->a_extras[j].data); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3885,6 +4247,11 @@ pacemaker_fuzzing: } +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_HAVOC %u", + afl->queue_cur->fname, use_stacking); +#endif + for (i = 0; i < use_stacking; ++i) { switch (select_algorithm(afl)) { @@ -3893,6 +4260,10 @@ pacemaker_fuzzing: /* Flip a single bit somewhere. Spooky! */ FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); MOpt_globals.cycles_v2[STAGE_FLIP1] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT1"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 1: @@ -3901,6 +4272,10 @@ pacemaker_fuzzing: FLIP_BIT(out_buf, temp_len_puppet); FLIP_BIT(out_buf, temp_len_puppet + 1); MOpt_globals.cycles_v2[STAGE_FLIP2] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT2"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 2: @@ -3911,24 +4286,40 @@ pacemaker_fuzzing: FLIP_BIT(out_buf, temp_len_puppet + 2); FLIP_BIT(out_buf, temp_len_puppet + 3); MOpt_globals.cycles_v2[STAGE_FLIP4] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT4"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 3: if (temp_len < 4) { break; } out_buf[rand_below(afl, temp_len)] ^= 0xFF; MOpt_globals.cycles_v2[STAGE_FLIP8] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT8"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 4: if (temp_len < 8) { break; } *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) ^= 0xFFFF; MOpt_globals.cycles_v2[STAGE_FLIP16] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT16"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 5: if (temp_len < 8) { break; } *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) ^= 0xFFFFFFFF; MOpt_globals.cycles_v2[STAGE_FLIP32] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT32"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 6: @@ -3937,6 +4328,10 @@ pacemaker_fuzzing: out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); MOpt_globals.cycles_v2[STAGE_ARITH8] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH+-"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 7: @@ -3946,11 +4341,20 @@ pacemaker_fuzzing: u32 pos = rand_below(afl, temp_len - 1); *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16-%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif } else { u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16BE-%u-%u", + pos, num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) = SWAP16(SWAP16(*(u16 *)(out_buf + pos)) - num); @@ -3960,12 +4364,21 @@ pacemaker_fuzzing: if (rand_below(afl, 2)) { u32 pos = rand_below(afl, temp_len - 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16BE+%u-%u", + pos, num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) = SWAP16(SWAP16(*(u16 *)(out_buf + pos)) + num); @@ -3980,12 +4393,21 @@ pacemaker_fuzzing: if (rand_below(afl, 2)) { u32 pos = rand_below(afl, temp_len - 3); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32-%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32BE-%u-%u", + pos, num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) = SWAP32(SWAP32(*(u32 *)(out_buf + pos)) - num); @@ -3996,12 +4418,21 @@ pacemaker_fuzzing: if (rand_below(afl, 2)) { u32 pos = rand_below(afl, temp_len - 3); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32BE+%u-%u", + pos, num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) = SWAP32(SWAP32(*(u32 *)(out_buf + pos)) + num); @@ -4016,6 +4447,10 @@ pacemaker_fuzzing: out_buf[rand_below(afl, temp_len)] = interesting_8[rand_below(afl, sizeof(interesting_8))]; MOpt_globals.cycles_v2[STAGE_INTEREST8] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING8"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 10: @@ -4023,12 +4458,20 @@ pacemaker_fuzzing: if (temp_len < 8) { break; } if (rand_below(afl, 2)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]; } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16BE"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = SWAP16(interesting_16[rand_below( afl, sizeof(interesting_16) >> 1)]); @@ -4045,12 +4488,20 @@ pacemaker_fuzzing: if (rand_below(afl, 2)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]; } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32BE"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = SWAP32(interesting_32[rand_below( afl, sizeof(interesting_32) >> 2)]); @@ -4068,6 +4519,10 @@ pacemaker_fuzzing: out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); MOpt_globals.cycles_v2[STAGE_RANDOMBYTE] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " RAND8"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 13: { @@ -4091,6 +4546,11 @@ pacemaker_fuzzing: temp_len -= del_len; MOpt_globals.cycles_v2[STAGE_DELETEBYTE] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " DEL-%u%u", del_from, + del_len); + strcat(afl->mutation, afl->m_tmp); +#endif break; } @@ -4120,6 +4580,12 @@ pacemaker_fuzzing: clone_to = rand_below(afl, temp_len); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE_%s_%u_%u_%u", + actually_clone ? "clone" : "insert", clone_from, + clone_to, clone_len); + strcat(afl->mutation, afl->m_tmp); +#endif new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), temp_len + clone_len); if (unlikely(!new_buf)) { PFATAL("alloc"); } @@ -4175,12 +4641,24 @@ pacemaker_fuzzing: if (copy_from != copy_to) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " OVERWRITE_COPY_%u_%u_%u", copy_from, copy_to, + copy_len); + strcat(afl->mutation, afl->m_tmp); +#endif memmove(out_buf + copy_to, out_buf + copy_from, copy_len); } } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " OVERWRITE_FIXED_%u_%u_%u", copy_from, copy_to, + copy_len); + strcat(afl->mutation, afl->m_tmp); +#endif memset(out_buf + copy_to, rand_below(afl, 2) ? rand_below(afl, 256) : out_buf[rand_below(afl, temp_len)], @@ -4212,6 +4690,12 @@ pacemaker_fuzzing: if (extra_len > (u32)temp_len) break; u32 insert_at = rand_below(afl, temp_len - extra_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " AUTO_EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, + afl->a_extras[use_extra].data); + strcat(afl->mutation, afl->m_tmp); +#endif memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, extra_len); @@ -4225,6 +4709,12 @@ pacemaker_fuzzing: if (extra_len > (u32)temp_len) break; u32 insert_at = rand_below(afl, temp_len - extra_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, + afl->a_extras[use_extra].data); + strcat(afl->mutation, afl->m_tmp); +#endif memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len); @@ -4254,12 +4744,23 @@ pacemaker_fuzzing: use_extra = rand_below(afl, afl->a_extras_cnt); extra_len = afl->a_extras[use_extra].len; ptr = afl->a_extras[use_extra].data; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " AUTO_EXTRA_INSERT_%u_%u_%s", insert_at, extra_len, + ptr); + strcat(afl->mutation, afl->m_tmp); +#endif } else { use_extra = rand_below(afl, afl->extras_cnt); extra_len = afl->extras[use_extra].len; ptr = afl->extras[use_extra].data; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " EXTRA_INSERT_%u_%u_%s", insert_at, extra_len, ptr); + strcat(afl->mutation, afl->m_tmp); +#endif } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index cad26841..575e6b74 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -236,6 +236,10 @@ static void usage(u8 *argv0, int more_help) { SAYF("Compiled with PROFILING\n\n"); #endif +#ifdef INTROSPECTION + SAYF("Compiled with INTROSPECTION\n\n"); +#endif + #ifdef _DEBUG SAYF("Compiled with _DEBUG\n\n"); #endif @@ -1462,6 +1466,19 @@ int main(int argc, char **argv_orig, char **envp) { u32 prev_queued_paths = 0; u8 skipped_fuzz; + #ifdef INTROSPECTION + char ifn[4096]; + snprintf(ifn, sizeof(ifn), "%s/introspection.txt", afl->out_dir); + if ((afl->introspection_file = fopen(ifn, "w")) == NULL) { + + PFATAL("could not create '%s'", ifn); + + } + + setvbuf(afl->introspection_file, NULL, _IONBF, 0); + OKF("Writing mutation introspection to '%s'", ifn); + #endif + while (likely(!afl->stop_soon)) { cull_queue(afl); -- cgit 1.4.1 From 416e01d3c6277988ed39e67f9404bd7a6034820b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 2 Nov 2020 11:04:35 +0100 Subject: match mopt to havoc --- docs/Changelog.md | 6 +- include/afl-fuzz.h | 3 +- src/afl-fuzz-one.c | 209 ++++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 164 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index f11a1178..50c1d48a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -21,11 +21,11 @@ sending a mail to . a schedule performance score, which is much better that the previous walk the whole queue approach. Select the old mode with -Z (auto enabled with -M) - - statsd support by Edznux, thanks a lot! + - rpc.statsd support by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) - not specifying -M or -S will now auto-set "-S default" - reading testcases from -i now descends into subdirectories - - allow up to 4 -x command line options + - allow up to 4 times the -x command line option - loaded extras now have a duplicate protection - If test cases are too large we do a partial read on the maximum supported size @@ -33,7 +33,7 @@ sending a mail to . for fuzzing but still be used for splicing - crashing seeds are now not prohibiting a run anymore but are skipped. They are used for splicing though. - - set the default power schedule to the superiour "seek" schedule + - update MOpt for expanded havoc modes - added NO_SPLICING compile option and makefile define - added INTROSPECTION make target that writes all mutations to out/NAME/introspection.txt diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 5ff7672b..e59d5f90 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -231,7 +231,7 @@ enum { }; -#define operator_num 18 +#define operator_num 19 #define swarm_num 5 #define period_core 500000 @@ -247,6 +247,7 @@ enum { #define STAGE_OverWrite75 15 #define STAGE_OverWriteExtra 16 #define STAGE_InsertExtra 17 +#define STAGE_Splice 18 #define period_pilot 50000 enum { diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 5337b7f8..e4016773 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -29,11 +29,9 @@ /* MOpt */ -static int select_algorithm(afl_state_t *afl) { +static int select_algorithm(afl_state_t *afl, u32 max_algorithm) { - int i_puppet, j_puppet = 0, operator_number = operator_num; - - if (!afl->extras_cnt && !afl->a_extras_cnt) operator_number -= 2; + int i_puppet, j_puppet = 0, operator_number = max_algorithm; double range_sele = (double)afl->probability_now[afl->swarm_now][operator_number - 1]; @@ -502,6 +500,8 @@ u8 fuzz_one_original(afl_state_t *afl) { if (unlikely(!afl->non_instrumented_mode && !afl->queue_cur->trim_done && !afl->disable_trim)) { + u32 old_len = afl->queue_cur->len; + u8 res = trim_case(afl, afl->queue_cur, in_buf); orig_in = in_buf = queue_testcase_get(afl, afl->queue_cur); @@ -524,6 +524,9 @@ u8 fuzz_one_original(afl_state_t *afl) { len = afl->queue_cur->len; + /* maybe current entry is not ready for splicing anymore */ + if (unlikely(len <= 4 && old_len > 4)) afl->ready_for_splicing_count--; + } memcpy(out_buf, in_buf, len); @@ -537,7 +540,7 @@ u8 fuzz_one_original(afl_state_t *afl) { else orig_perf = perf_score = calculate_score(afl, afl->queue_cur); - if (unlikely(perf_score == 0)) { goto abandon_entry; } + if (unlikely(perf_score <= 0)) { goto abandon_entry; } if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { @@ -1976,7 +1979,7 @@ havoc_stage: /* add expensive havoc cases here, they are activated after a full cycle without finds happened */ - r_max += 1; + r_max++; } @@ -1985,7 +1988,7 @@ havoc_stage: /* add expensive havoc cases here if there is no findings in the last 5s */ - r_max += 1; + r_max++; } @@ -2396,7 +2399,7 @@ havoc_stage: if (likely(rand_below(afl, 4))) { - if (copy_from != copy_to) { + if (likely(copy_from != copy_to)) { #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), @@ -2445,11 +2448,10 @@ havoc_stage: u32 use_extra = rand_below(afl, afl->a_extras_cnt); u32 extra_len = afl->a_extras[use_extra].len; - u32 insert_at; if ((s32)extra_len > temp_len) { break; } - insert_at = rand_below(afl, temp_len - extra_len + 1); + u32 insert_at = rand_below(afl, temp_len - extra_len + 1); #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " AUTO_EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, @@ -2465,11 +2467,10 @@ havoc_stage: u32 use_extra = rand_below(afl, afl->extras_cnt); u32 extra_len = afl->extras[use_extra].len; - u32 insert_at; if ((s32)extra_len > temp_len) { break; } - insert_at = rand_below(afl, temp_len - extra_len + 1); + u32 insert_at = rand_below(afl, temp_len - extra_len + 1); #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, @@ -2816,7 +2817,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { possibly skip to them at the expense of already-fuzzed or non-favored cases. */ - if ((afl->queue_cur->was_fuzzed || !afl->queue_cur->favored) && + if (((afl->queue_cur->was_fuzzed > 0 || afl->queue_cur->fuzz_level > 0) || + !afl->queue_cur->favored) && rand_below(afl, 100) < SKIP_TO_NEW_PROB) { return 1; @@ -2831,13 +2833,14 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { The odds of skipping stuff are higher for already-fuzzed inputs and lower for never-fuzzed entries. */ - if (afl->queue_cycle > 1 && !afl->queue_cur->was_fuzzed) { + if (afl->queue_cycle > 1 && + (afl->queue_cur->fuzz_level == 0 || afl->queue_cur->was_fuzzed)) { - if (rand_below(afl, 100) < SKIP_NFAV_NEW_PROB) { return 1; } + if (likely(rand_below(afl, 100) < SKIP_NFAV_NEW_PROB)) { return 1; } } else { - if (rand_below(afl, 100) < SKIP_NFAV_OLD_PROB) { return 1; } + if (likely(rand_below(afl, 100) < SKIP_NFAV_OLD_PROB)) { return 1; } } @@ -2856,16 +2859,19 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { /* Map the test case into memory. */ orig_in = in_buf = queue_testcase_get(afl, afl->queue_cur); len = afl->queue_cur->len; + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); if (unlikely(!out_buf)) { PFATAL("alloc"); } + afl->subseq_tmouts = 0; + afl->cur_depth = afl->queue_cur->depth; /******************************************* * CALIBRATION (only if failed earlier on) * *******************************************/ - if (afl->queue_cur->cal_failed) { + if (unlikely(afl->queue_cur->cal_failed)) { u8 res = FSRV_RUN_TMOUT; @@ -2897,7 +2903,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { * TRIMMING * ************/ - if (!afl->non_instrumented_mode && !afl->queue_cur->trim_done) { + if (unlikely(!afl->non_instrumented_mode && !afl->queue_cur->trim_done && + !afl->disable_trim)) { u32 old_len = afl->queue_cur->len; @@ -2939,6 +2946,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { else orig_perf = perf_score = calculate_score(afl, afl->queue_cur); + if (unlikely(perf_score <= 0)) { goto abandon_entry; } + if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { if (input_to_state_stage(afl, in_buf, out_buf, len, @@ -2968,8 +2977,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { this entry ourselves (was_fuzzed), or if it has gone through deterministic testing in earlier, resumed runs (passed_det). */ - if (afl->skip_deterministic || afl->queue_cur->was_fuzzed || - afl->queue_cur->passed_det) { + if (likely(afl->skip_deterministic || afl->queue_cur->was_fuzzed || + afl->queue_cur->passed_det)) { goto havoc_stage; @@ -2978,8 +2987,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { /* Skip deterministic fuzzing if exec path checksum puts this out of scope for this main instance. */ - if (afl->main_node_max && (afl->queue_cur->exec_cksum % afl->main_node_max) != - afl->main_node_id - 1) { + if (unlikely(afl->main_node_max && + (afl->queue_cur->exec_cksum % afl->main_node_max) != + afl->main_node_id - 1)) { goto havoc_stage; @@ -4008,6 +4018,7 @@ skip_interest: "%s MOPT_EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, i, j, afl->extras[j].data); #endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -4060,6 +4071,7 @@ skip_interest: "%s MOPT_EXTRAS insert %u %u:%s", afl->queue_cur->fname, i, j, afl->extras[j].data); #endif + if (common_fuzz_stuff(afl, ex_tmp, len + afl->extras[j].len)) { goto abandon_entry; @@ -4099,7 +4111,8 @@ skip_user_extras: afl->stage_cur_byte = i; - for (j = 0; j < MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS); ++j) { + u32 min_extra_len = MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS); + for (j = 0; j < min_extra_len; ++j) { /* See the comment in the earlier code; extras are sorted by size. */ @@ -4121,6 +4134,7 @@ skip_user_extras: "%s MOPT_AUTO_EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, i, j, afl->a_extras[j].data); #endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -4234,6 +4248,19 @@ pacemaker_fuzzing: havoc_queued = afl->queued_paths; + u32 r_max; + + r_max = 15 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0); + + if (unlikely(afl->expand_havoc && afl->ready_for_splicing_count > 1)) { + + /* add expensive havoc cases here, they are activated after a full + cycle without finds happened */ + + ++r_max; + + } + for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { @@ -4254,12 +4281,12 @@ pacemaker_fuzzing: for (i = 0; i < use_stacking; ++i) { - switch (select_algorithm(afl)) { + switch (select_algorithm(afl, r_max)) { case 0: /* Flip a single bit somewhere. Spooky! */ FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); - MOpt_globals.cycles_v2[STAGE_FLIP1] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP1]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT1"); strcat(afl->mutation, afl->m_tmp); @@ -4271,7 +4298,7 @@ pacemaker_fuzzing: temp_len_puppet = rand_below(afl, (temp_len << 3) - 1); FLIP_BIT(out_buf, temp_len_puppet); FLIP_BIT(out_buf, temp_len_puppet + 1); - MOpt_globals.cycles_v2[STAGE_FLIP2] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP2]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT2"); strcat(afl->mutation, afl->m_tmp); @@ -4285,7 +4312,7 @@ pacemaker_fuzzing: FLIP_BIT(out_buf, temp_len_puppet + 1); FLIP_BIT(out_buf, temp_len_puppet + 2); FLIP_BIT(out_buf, temp_len_puppet + 3); - MOpt_globals.cycles_v2[STAGE_FLIP4] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP4]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT4"); strcat(afl->mutation, afl->m_tmp); @@ -4295,7 +4322,7 @@ pacemaker_fuzzing: case 3: if (temp_len < 4) { break; } out_buf[rand_below(afl, temp_len)] ^= 0xFF; - MOpt_globals.cycles_v2[STAGE_FLIP8] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP8]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT8"); strcat(afl->mutation, afl->m_tmp); @@ -4305,7 +4332,7 @@ pacemaker_fuzzing: case 4: if (temp_len < 8) { break; } *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) ^= 0xFFFF; - MOpt_globals.cycles_v2[STAGE_FLIP16] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP16]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT16"); strcat(afl->mutation, afl->m_tmp); @@ -4315,7 +4342,7 @@ pacemaker_fuzzing: case 5: if (temp_len < 8) { break; } *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) ^= 0xFFFFFFFF; - MOpt_globals.cycles_v2[STAGE_FLIP32] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP32]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT32"); strcat(afl->mutation, afl->m_tmp); @@ -4327,7 +4354,7 @@ pacemaker_fuzzing: 1 + rand_below(afl, ARITH_MAX); out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); - MOpt_globals.cycles_v2[STAGE_ARITH8] += 1; + MOpt_globals.cycles_v2[STAGE_ARITH8]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH+-"); strcat(afl->mutation, afl->m_tmp); @@ -4384,7 +4411,7 @@ pacemaker_fuzzing: } - MOpt_globals.cycles_v2[STAGE_ARITH16] += 1; + MOpt_globals.cycles_v2[STAGE_ARITH16]++; break; case 8: @@ -4438,7 +4465,7 @@ pacemaker_fuzzing: } - MOpt_globals.cycles_v2[STAGE_ARITH32] += 1; + MOpt_globals.cycles_v2[STAGE_ARITH32]++; break; case 9: @@ -4446,7 +4473,7 @@ pacemaker_fuzzing: if (temp_len < 4) { break; } out_buf[rand_below(afl, temp_len)] = interesting_8[rand_below(afl, sizeof(interesting_8))]; - MOpt_globals.cycles_v2[STAGE_INTEREST8] += 1; + MOpt_globals.cycles_v2[STAGE_INTEREST8]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING8"); strcat(afl->mutation, afl->m_tmp); @@ -4478,7 +4505,7 @@ pacemaker_fuzzing: } - MOpt_globals.cycles_v2[STAGE_INTEREST16] += 1; + MOpt_globals.cycles_v2[STAGE_INTEREST16]++; break; case 11: @@ -4508,7 +4535,7 @@ pacemaker_fuzzing: } - MOpt_globals.cycles_v2[STAGE_INTEREST32] += 1; + MOpt_globals.cycles_v2[STAGE_INTEREST32]++; break; case 12: @@ -4518,7 +4545,7 @@ pacemaker_fuzzing: possibility of a no-op. */ out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); - MOpt_globals.cycles_v2[STAGE_RANDOMBYTE] += 1; + MOpt_globals.cycles_v2[STAGE_RANDOMBYTE]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " RAND8"); strcat(afl->mutation, afl->m_tmp); @@ -4541,16 +4568,16 @@ pacemaker_fuzzing: del_from = rand_below(afl, temp_len - del_len + 1); - memmove(out_buf + del_from, out_buf + del_from + del_len, - temp_len - del_from - del_len); - - temp_len -= del_len; - MOpt_globals.cycles_v2[STAGE_DELETEBYTE] += 1; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " DEL-%u%u", del_from, del_len); strcat(afl->mutation, afl->m_tmp); #endif + memmove(out_buf + del_from, out_buf + del_from + del_len, + temp_len - del_from - del_len); + + temp_len -= del_len; + MOpt_globals.cycles_v2[STAGE_DELETEBYTE]++; break; } @@ -4566,7 +4593,7 @@ pacemaker_fuzzing: u32 clone_from, clone_to, clone_len; u8 *new_buf; - if (actually_clone) { + if (likely(actually_clone)) { clone_len = choose_block_len(afl, temp_len); clone_from = rand_below(afl, temp_len - clone_len + 1); @@ -4617,7 +4644,7 @@ pacemaker_fuzzing: out_buf = new_buf; afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); temp_len += clone_len; - MOpt_globals.cycles_v2[STAGE_Clone75] += 1; + MOpt_globals.cycles_v2[STAGE_Clone75]++; } @@ -4637,9 +4664,9 @@ pacemaker_fuzzing: copy_from = rand_below(afl, temp_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); - if (rand_below(afl, 4)) { + if (likely(rand_below(afl, 4))) { - if (copy_from != copy_to) { + if (likely(copy_from != copy_to)) { #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), @@ -4666,7 +4693,7 @@ pacemaker_fuzzing: } - MOpt_globals.cycles_v2[STAGE_OverWrite75] += 1; + MOpt_globals.cycles_v2[STAGE_OverWrite75]++; break; } /* case 15 */ @@ -4721,7 +4748,7 @@ pacemaker_fuzzing: } afl->stage_cycles_puppet_v2[afl->swarm_now] - [STAGE_OverWriteExtra] += 1; + [STAGE_OverWriteExtra]++; break; @@ -4783,11 +4810,93 @@ pacemaker_fuzzing: } + default: { + + if (unlikely(afl->ready_for_splicing_count < 2)) break; + + u32 tid; + do { + + tid = rand_below(afl, afl->queued_paths); + + } while (tid == afl->current_entry || + + afl->queue_buf[tid]->len < 4); + + /* Get the testcase for splicing. */ + struct queue_entry *target = afl->queue_buf[tid]; + u32 new_len = target->len; + u8 * new_buf = queue_testcase_get(afl, target); + + if ((temp_len >= 2 && rand_below(afl, 2)) || + temp_len + HAVOC_BLK_XL >= MAX_FILE) { + + /* overwrite mode */ + + u32 copy_from, copy_to, copy_len; + + copy_len = choose_block_len(afl, new_len - 1); + if ((s32)copy_len > temp_len) copy_len = temp_len; + + copy_from = rand_below(afl, new_len - copy_len + 1); + copy_to = rand_below(afl, temp_len - copy_len + 1); + +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " SPLICE_OVERWRITE_%u_%u_%u_%s", copy_from, copy_to, + copy_len, target->fname); + strcat(afl->mutation, afl->m_tmp); +#endif + memmove(out_buf + copy_to, new_buf + copy_from, copy_len); + + } else { + + /* insert mode */ + + u32 clone_from, clone_to, clone_len; + + clone_len = choose_block_len(afl, new_len); + clone_from = rand_below(afl, new_len - clone_len + 1); + clone_to = rand_below(afl, temp_len + 1); + + u8 *temp_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), + temp_len + clone_len + 1); + if (unlikely(!temp_buf)) { PFATAL("alloc"); } + +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " SPLICE_INSERT_%u_%u_%u_%s", clone_from, clone_to, + clone_len, target->fname); + strcat(afl->mutation, afl->m_tmp); +#endif + /* Head */ + + memcpy(temp_buf, out_buf, clone_to); + + /* Inserted part */ + + memcpy(temp_buf + clone_to, new_buf + clone_from, clone_len); + + /* Tail */ + memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to, + temp_len - clone_to); + + out_buf = temp_buf; + afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); + temp_len += clone_len; + + } + + afl->stage_cycles_puppet_v2[afl->swarm_now][STAGE_Splice]++; + break; + + } // end of default: + } /* switch select_algorithm() */ } /* for i=0; i < use_stacking */ - *MOpt_globals.pTime += 1; + ++*MOpt_globals.pTime; u64 temp_total_found = afl->queued_paths + afl->unique_crashes; @@ -5138,7 +5247,7 @@ u8 pilot_fuzzing(afl_state_t *afl) { void pso_updating(afl_state_t *afl) { - afl->g_now += 1; + afl->g_now++; if (afl->g_now > afl->g_max) { afl->g_now = 0; } afl->w_now = (afl->w_init - afl->w_end) * (afl->g_max - afl->g_now) / (afl->g_max) + -- cgit 1.4.1 From 350c3b323a59c99891635a233c3f82f83653947c Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Mon, 2 Nov 2020 21:17:37 +0100 Subject: fix for #562 --- src/afl-tmin.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/afl-tmin.c b/src/afl-tmin.c index efc12d4f..06037d61 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -656,6 +656,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { unlink(out_file); + fsrv->out_file = out_file; fsrv->out_fd = open(out_file, O_RDWR | O_CREAT | O_EXCL, 0600); if (fsrv->out_fd < 0) { PFATAL("Unable to create '%s'", out_file); } -- cgit 1.4.1 From d795ec0451bfb6f93485c4ec6436ae1af3840070 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 3 Nov 2020 13:41:06 +0100 Subject: added better error handling to forkserver fd --- .gitignore | 5 +++++ include/debug.h | 16 +++++++++------- src/afl-forkserver.c | 9 ++++++--- 3 files changed, 20 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/.gitignore b/.gitignore index fb6a94c1..8107b448 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,8 @@ examples/afl_frida/afl-frida examples/afl_frida/libtestinstr.so examples/afl_frida/frida-gum-example.c examples/afl_frida/frida-gum.h +examples/aflpp_driver/libAFLDriver.a +examples/aflpp_driver/libAFLQemuDriver.a +libAFLDriver.a +libAFLQemuDriver.a +test/.afl_performance \ No newline at end of file diff --git a/include/debug.h b/include/debug.h index f9ebce58..e6d3c3fc 100644 --- a/include/debug.h +++ b/include/debug.h @@ -273,13 +273,15 @@ /* Error-checking versions of read() and write() that call RPFATAL() as appropriate. */ -#define ck_write(fd, buf, len, fn) \ - do { \ - \ - s32 _len = (s32)(len); \ - s32 _res = write(fd, buf, _len); \ - if (_res != _len) RPFATAL(_res, "Short write to %s", fn); \ - \ +#define ck_write(fd, buf, len, fn) \ + do { \ + \ + int _fd = (fd); \ + \ + s32 _len = (s32)(len); \ + s32 _res = write(_fd, (buf), _len); \ + if (_res != _len) RPFATAL(_res, "Short write to %s, fd %d", fn, _fd); \ + \ } while (0) #define ck_read(fd, buf, len, fn) \ diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index d23cf6eb..714be24e 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -983,10 +983,13 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { if (fd < 0) { PFATAL("Unable to create '%s'", fsrv->out_file); } - } else if (unlikely(!fd)) { + } else if (unlikely(fd <= 0)) { - // We should never have stdin as fd here, 0 is likely unset. - FATAL("Nowhere to write output to (neither out_fd nor out_file set)"); + // We should have a (non-stdin) fd at this point, else we got a problem. + FATAL( + "Nowhere to write output to (neither out_fd nor out_file set (fd is " + "%d))", + fd); } else { -- cgit 1.4.1 From 7ed0bfb6f51ff9c331c09315d72baad7b14259e5 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 3 Nov 2020 15:09:57 +0100 Subject: copy fsrv: copy out_file ptr --- src/afl-forkserver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 714be24e..b717c1ad 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -114,7 +114,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->child_pid = -1; fsrv_to->use_fauxsrv = 0; fsrv_to->last_run_timed_out = 0; - fsrv_to->out_file = NULL; + fsrv_to->out_file = from->out_file; fsrv_to->init_child_func = fsrv_exec_child; // Note: do not copy ->add_extra_func -- cgit 1.4.1 From 8bccf5655354540db5f6f208731d261dcfe05e2d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 3 Nov 2020 15:13:04 +0100 Subject: fsrv copy: out_fd = -1 --- src/afl-forkserver.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index b717c1ad..313aa363 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -99,14 +99,13 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->use_stdin = from->use_stdin; - fsrv_to->out_fd = from->out_fd; fsrv_to->dev_null_fd = from->dev_null_fd; fsrv_to->exec_tmout = from->exec_tmout; fsrv_to->init_tmout = from->init_tmout; fsrv_to->mem_limit = from->mem_limit; fsrv_to->map_size = from->map_size; fsrv_to->support_shmem_fuzz = from->support_shmem_fuzz; - + fsrv_to->out_file = from->out_file; fsrv_to->dev_urandom_fd = from->dev_urandom_fd; // These are forkserver specific. @@ -114,7 +113,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->child_pid = -1; fsrv_to->use_fauxsrv = 0; fsrv_to->last_run_timed_out = 0; - fsrv_to->out_file = from->out_file; + fsrv_to->out_fd = -1; fsrv_to->init_child_func = fsrv_exec_child; // Note: do not copy ->add_extra_func -- cgit 1.4.1 From 245f511a1d3432927ba101e270d7c6e4157e8e63 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 3 Nov 2020 15:15:58 +0100 Subject: fsrv copy: out_fd = 0 on stdin --- src/afl-forkserver.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 313aa363..d294f7fc 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -113,7 +113,16 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->child_pid = -1; fsrv_to->use_fauxsrv = 0; fsrv_to->last_run_timed_out = 0; - fsrv_to->out_fd = -1; + + if (fsrv_to->use_stdin) { + + fsrv_to->out_fd = 0; + + } else { + + fsrv_to->out_fd = -1; + + } fsrv_to->init_child_func = fsrv_exec_child; // Note: do not copy ->add_extra_func -- cgit 1.4.1 From 7b4d1c408909dd27d81967eced963f3b661511c8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 3 Nov 2020 15:52:19 +0100 Subject: revert out_fd copy --- src/afl-forkserver.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index d294f7fc..a8c486b6 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -107,6 +107,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->support_shmem_fuzz = from->support_shmem_fuzz; fsrv_to->out_file = from->out_file; fsrv_to->dev_urandom_fd = from->dev_urandom_fd; + fsrv_to->out_fd = from->out_fd; // not sure this is a good idea // These are forkserver specific. fsrv_to->out_dir_fd = -1; @@ -114,16 +115,6 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->use_fauxsrv = 0; fsrv_to->last_run_timed_out = 0; - if (fsrv_to->use_stdin) { - - fsrv_to->out_fd = 0; - - } else { - - fsrv_to->out_fd = -1; - - } - fsrv_to->init_child_func = fsrv_exec_child; // Note: do not copy ->add_extra_func -- cgit 1.4.1 From 3cfc0174f7fac85f232052dffdb1eb83ea3ef24d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 3 Nov 2020 16:00:29 +0100 Subject: fix -N for forkserver --- src/afl-forkserver.c | 3 ++- src/afl-fuzz-run.c | 4 ++-- src/afl-fuzz.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index a8c486b6..45be2abd 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -108,6 +108,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->out_file = from->out_file; fsrv_to->dev_urandom_fd = from->dev_urandom_fd; fsrv_to->out_fd = from->out_fd; // not sure this is a good idea + fsrv_to->no_unlink = from->no_unlink; // These are forkserver specific. fsrv_to->out_dir_fd = -1; @@ -969,7 +970,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { if (!fsrv->use_stdin && fsrv->out_file) { - if (fsrv->no_unlink) { + if (unlikely(fsrv->no_unlink)) { fd = open(fsrv->out_file, O_WRONLY | O_CREAT | O_TRUNC, 0600); diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index fb259b5d..e969994d 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -243,7 +243,7 @@ static void write_with_gap(afl_state_t *afl, u8 *mem, u32 len, u32 skip_at, } else if (afl->fsrv.out_file) { - if (afl->no_unlink) { + if (unlikely(afl->no_unlink)) { fd = open(afl->fsrv.out_file, O_WRONLY | O_CREAT | O_TRUNC, 0600); @@ -824,7 +824,7 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { s32 fd; - if (afl->no_unlink) { + if (unlikely(afl->no_unlink)) { fd = open(q->fname, O_WRONLY | O_CREAT | O_TRUNC, 0600); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 575e6b74..67cde96a 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -656,7 +656,7 @@ int main(int argc, char **argv_orig, char **envp) { case 'N': /* Unicorn mode */ if (afl->no_unlink) { FATAL("Multiple -N options not supported"); } - afl->no_unlink = 1; + afl->fsrv.no_unlink = afl->no_unlink = 1; break; -- cgit 1.4.1 From 6abe4d124ec23d3ab9fc5f7bec4ffa1c0a1eba22 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 5 Nov 2020 08:53:16 +0100 Subject: require llvm 10.0.1+ for enhanced pcguard, correct 1MB checks --- src/afl-cc.c | 8 ++++---- src/afl-fuzz.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 1a7a837f..46468dda 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -501,7 +501,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { -#if LLVM_MAJOR >= 10 +#if LLVM_MAJOR >= 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; cc_params[cc_par_cnt++] = "-Xclang"; @@ -511,7 +511,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { #if LLVM_MAJOR >= 4 if (!be_quiet) SAYF( - "Using unoptimized trace-pc-guard, upgrade to llvm 10+ for " + "Using unoptimized trace-pc-guard, upgrade to llvm 10.0.1+ for " "enhanced version.\n"); cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; #else @@ -715,14 +715,14 @@ static void edit_params(u32 argc, char **argv, char **envp) { "int __afl_sharedmem_fuzzing = 1;" "extern unsigned int *__afl_fuzz_len;" "extern unsigned char *__afl_fuzz_ptr;" - "unsigned char __afl_fuzz_alt[1024000];" + "unsigned char __afl_fuzz_alt[1048576];" "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; cc_params[cc_par_cnt++] = "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " "__afl_fuzz_alt_ptr)"; cc_params[cc_par_cnt++] = "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : " - "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1024000)) == 0xffffffff " + "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff " "? 0 : *__afl_fuzz_len)"; cc_params[cc_par_cnt++] = diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 67cde96a..269ce1bf 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1064,13 +1064,13 @@ int main(int argc, char **argv_orig, char **envp) { } else if (afl->q_testcase_max_cache_size < 2 * MAX_FILE) { FATAL("AFL_TESTCACHE_SIZE must be set to %u or more, or 0 to disable", - (2 * MAX_FILE) % 1024000 == 0 ? (2 * MAX_FILE) / 1048576 + (2 * MAX_FILE) % 1048576 == 0 ? (2 * MAX_FILE) / 1048576 : 1 + ((2 * MAX_FILE) / 1048576)); } else { OKF("Enabled testcache with %llu MB", - afl->q_testcase_max_cache_size / 1024000); + afl->q_testcase_max_cache_size / 1048576); } -- cgit 1.4.1 From a728e8f9a5518017cde12bdb6ba795a6bcb47b0a Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 6 Nov 2020 16:42:02 +0100 Subject: better scriptable output from introspection --- README.md | 2 +- src/afl-fuzz-one.c | 191 +++++++++++++++++++++++++---------------------------- 2 files changed, 91 insertions(+), 102 deletions(-) (limited to 'src') diff --git a/README.md b/README.md index f955aedd..d7c5694e 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ behaviours and defaults: ## Important features of afl++ - afl++ supports llvm up to version 12, very fast binary fuzzing with QEMU 3.1 + afl++ supports llvm up to version 12, very fast binary fuzzing with QEMU 5.1 with laf-intel and redqueen, unicorn mode, gcc plugin, full *BSD, Solaris and Android support and much, much, much more. diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index e4016773..91bbced6 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -613,7 +613,7 @@ u8 fuzz_one_original(afl_state_t *afl) { FLIP_BIT(out_buf, afl->stage_cur); #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT1 %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT1-%u", afl->queue_cur->fname, afl->stage_cur); #endif @@ -727,7 +727,7 @@ u8 fuzz_one_original(afl_state_t *afl) { FLIP_BIT(out_buf, afl->stage_cur + 1); #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT2 %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT2-%u", afl->queue_cur->fname, afl->stage_cur); #endif @@ -761,7 +761,7 @@ u8 fuzz_one_original(afl_state_t *afl) { FLIP_BIT(out_buf, afl->stage_cur + 3); #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT4 %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT4-%u", afl->queue_cur->fname, afl->stage_cur); #endif @@ -821,7 +821,7 @@ u8 fuzz_one_original(afl_state_t *afl) { out_buf[afl->stage_cur] ^= 0xFF; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT8 %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT8-%u", afl->queue_cur->fname, afl->stage_cur); #endif @@ -913,7 +913,7 @@ u8 fuzz_one_original(afl_state_t *afl) { *(u16 *)(out_buf + i) ^= 0xFFFF; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT16 %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT16-%u", afl->queue_cur->fname, afl->stage_cur); #endif @@ -956,7 +956,7 @@ u8 fuzz_one_original(afl_state_t *afl) { *(u32 *)(out_buf + i) ^= 0xFFFFFFFF; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT32 %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT32-%u", afl->queue_cur->fname, afl->stage_cur); #endif @@ -1019,7 +1019,7 @@ skip_bitflip: out_buf[i] = orig + j; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH8+ %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH8+-%u-%u", afl->queue_cur->fname, i, j); #endif @@ -1040,7 +1040,7 @@ skip_bitflip: out_buf[i] = orig - j; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH8- %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH8--%u-%u", afl->queue_cur->fname, i, j); #endif @@ -1109,7 +1109,7 @@ skip_bitflip: *(u16 *)(out_buf + i) = orig + j; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16+ %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16+-%u-%u", afl->queue_cur->fname, i, j); #endif @@ -1128,7 +1128,7 @@ skip_bitflip: *(u16 *)(out_buf + i) = orig - j; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16- %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16--%u-%u", afl->queue_cur->fname, i, j); #endif @@ -1151,7 +1151,7 @@ skip_bitflip: *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) + j); #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16+BE %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16+BE-%u-%u", afl->queue_cur->fname, i, j); #endif @@ -1170,7 +1170,7 @@ skip_bitflip: *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) - j); #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16-BE %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16_BE-%u-%u", afl->queue_cur->fname, i, j); #endif @@ -1238,7 +1238,7 @@ skip_bitflip: *(u32 *)(out_buf + i) = orig + j; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32+ %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32+-%u-%u", afl->queue_cur->fname, i, j); #endif @@ -1257,7 +1257,7 @@ skip_bitflip: *(u32 *)(out_buf + i) = orig - j; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32- %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32_-%u-%u", afl->queue_cur->fname, i, j); #endif @@ -1280,7 +1280,7 @@ skip_bitflip: *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) + j); #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32+BE %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32+BE-%u-%u", afl->queue_cur->fname, i, j); #endif @@ -1299,7 +1299,7 @@ skip_bitflip: *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) - j); #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32-BE %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32_BE-%u-%u", afl->queue_cur->fname, i, j); #endif @@ -1371,7 +1371,7 @@ skip_arith: out_buf[i] = interesting_8[j]; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING8 %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING8_%u_%u", afl->queue_cur->fname, i, j); #endif @@ -1431,7 +1431,7 @@ skip_arith: *(u16 *)(out_buf + i) = interesting_16[j]; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING16 %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING16_%u_%u", afl->queue_cur->fname, i, j); #endif @@ -1453,7 +1453,7 @@ skip_arith: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s INTERESTING16BE %u %u", afl->queue_cur->fname, i, j); + "%s INTERESTING16BE_%u_%u", afl->queue_cur->fname, i, j); #endif *(u16 *)(out_buf + i) = SWAP16(interesting_16[j]); @@ -1520,7 +1520,7 @@ skip_arith: *(u32 *)(out_buf + i) = interesting_32[j]; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING32 %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING32_%u_%u", afl->queue_cur->fname, i, j); #endif @@ -1542,7 +1542,7 @@ skip_arith: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s INTERESTING32BE %u %u", afl->queue_cur->fname, i, j); + "%s INTERESTING32BE_%u_%u", afl->queue_cur->fname, i, j); #endif *(u32 *)(out_buf + i) = SWAP32(interesting_32[j]); @@ -1620,8 +1620,7 @@ skip_interest: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, i, j, - afl->extras[j].data); + "%s EXTRAS_overwrite-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -1672,9 +1671,8 @@ skip_interest: memcpy(ex_tmp + i + afl->extras[j].len, out_buf + i, len - i); #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), - "%s EXTRAS insert %u %u:%s", afl->queue_cur->fname, i, j, - afl->extras[j].data); + snprintf(afl->mutation, sizeof(afl->mutation), "%s EXTRAS_insert-%u-%u", + afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, ex_tmp, len + afl->extras[j].len)) { @@ -1736,8 +1734,7 @@ skip_user_extras: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s AUTO_EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, i, j, - afl->a_extras[j].data); + "%s AUTO_EXTRAS_overwrite-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -1844,7 +1841,7 @@ custom_mutator_stage: if (mutated_size > 0) { #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s CUSTOM %s", + snprintf(afl->mutation, sizeof(afl->mutation), "%s CUSTOM-%s", afl->queue_cur->fname, target != NULL ? (char *)target->fname : "none"); #endif @@ -1999,7 +1996,7 @@ havoc_stage: afl->stage_cur_val = use_stacking; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s HAVOC %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s HAVOC-%u", afl->queue_cur->fname, use_stacking); #endif @@ -2126,7 +2123,7 @@ havoc_stage: /* Randomly subtract from byte. */ #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8-"); + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8_"); strcat(afl->mutation, afl->m_tmp); #endif out_buf[rand_below(afl, temp_len)] -= 1 + rand_below(afl, ARITH_MAX); @@ -2154,7 +2151,7 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16-_%u", pos); + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16_-%u", pos); strcat(afl->mutation, afl->m_tmp); #endif *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); @@ -2165,7 +2162,7 @@ havoc_stage: u16 num = 1 + rand_below(afl, ARITH_MAX); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16-BE_%u_%u", pos, + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16_BE-%u_%u", pos, num); strcat(afl->mutation, afl->m_tmp); #endif @@ -2187,7 +2184,7 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+_%u", pos); + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+-%u", pos); strcat(afl->mutation, afl->m_tmp); #endif *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); @@ -2198,7 +2195,7 @@ havoc_stage: u16 num = 1 + rand_below(afl, ARITH_MAX); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+BE_%u_%u", pos, + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+BE-%u_%u", pos, num); strcat(afl->mutation, afl->m_tmp); #endif @@ -2220,7 +2217,7 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32-_%u", pos); + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32_-%u", pos); strcat(afl->mutation, afl->m_tmp); #endif *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); @@ -2231,7 +2228,7 @@ havoc_stage: u32 num = 1 + rand_below(afl, ARITH_MAX); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32-BE_%u_%u", pos, + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32_BE-%u-%u", pos, num); strcat(afl->mutation, afl->m_tmp); #endif @@ -2253,7 +2250,7 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+_%u", pos); + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+-%u", pos); strcat(afl->mutation, afl->m_tmp); #endif *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); @@ -2264,7 +2261,7 @@ havoc_stage: u32 num = 1 + rand_below(afl, ARITH_MAX); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+BE_%u_%u", pos, + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+BE-%u-%u", pos, num); strcat(afl->mutation, afl->m_tmp); #endif @@ -2305,7 +2302,7 @@ havoc_stage: del_from = rand_below(afl, temp_len - del_len + 1); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " DEL_%u_%u", del_from, + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " DEL-%u-%u", del_from, del_len); strcat(afl->mutation, afl->m_tmp); #endif @@ -2343,7 +2340,7 @@ havoc_stage: clone_to = rand_below(afl, temp_len); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE_%s_%u_%u_%u", + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE-%s-%u-%u-%u", actually_clone ? "clone" : "insert", clone_from, clone_to, clone_len); strcat(afl->mutation, afl->m_tmp); @@ -2403,7 +2400,7 @@ havoc_stage: #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " OVERWRITE_COPY_%u_%u_%u", copy_from, copy_to, + " OVERWRITE_COPY-%u-%u-%u", copy_from, copy_to, copy_len); strcat(afl->mutation, afl->m_tmp); #endif @@ -2415,7 +2412,7 @@ havoc_stage: #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " OVERWRITE_FIXED_%u_%u_%u", copy_from, copy_to, copy_len); + " OVERWRITE_FIXED-%u-%u-%u", copy_from, copy_to, copy_len); strcat(afl->mutation, afl->m_tmp); #endif memset(out_buf + copy_to, @@ -2454,8 +2451,7 @@ havoc_stage: u32 insert_at = rand_below(afl, temp_len - extra_len + 1); #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " AUTO_EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, - afl->a_extras[use_extra].data); + " AUTO_EXTRA_OVERWRITE-%u-%u", insert_at, extra_len); strcat(afl->mutation, afl->m_tmp); #endif memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, @@ -2473,8 +2469,7 @@ havoc_stage: u32 insert_at = rand_below(afl, temp_len - extra_len + 1); #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, - afl->a_extras[use_extra].data); + " EXTRA_OVERWRITE-%u-%u", insert_at, extra_len); strcat(afl->mutation, afl->m_tmp); #endif memcpy(out_buf + insert_at, afl->extras[use_extra].data, @@ -2501,8 +2496,7 @@ havoc_stage: ptr = afl->a_extras[use_extra].data; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " AUTO_EXTRA_INSERT_%u_%u_%s", insert_at, extra_len, - ptr); + " AUTO_EXTRA_INSERT-%u-%u", insert_at, extra_len); strcat(afl->mutation, afl->m_tmp); #endif @@ -2512,8 +2506,8 @@ havoc_stage: extra_len = afl->extras[use_extra].len; ptr = afl->extras[use_extra].data; #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " EXTRA_INSERT_%u_%u_%s", insert_at, extra_len, ptr); + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " EXTRA_INSERT-%u-%u", + insert_at, extra_len); strcat(afl->mutation, afl->m_tmp); #endif @@ -2578,7 +2572,7 @@ havoc_stage: #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " SPLICE_OVERWRITE_%u_%u_%u_%s", copy_from, copy_to, + " SPLICE_OVERWRITE-%u-%u-%u-%s", copy_from, copy_to, copy_len, target->fname); strcat(afl->mutation, afl->m_tmp); #endif @@ -2600,7 +2594,7 @@ havoc_stage: #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " SPLICE_INSERT_%u_%u_%u_%s", clone_from, clone_to, + " SPLICE_INSERT-%u-%u-%u-%s", clone_from, clone_to, clone_len, target->fname); strcat(afl->mutation, afl->m_tmp); #endif @@ -3029,7 +3023,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { FLIP_BIT(out_buf, afl->stage_cur); #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT1 %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT1-%u", afl->queue_cur->fname, afl->stage_cur); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3142,7 +3136,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { FLIP_BIT(out_buf, afl->stage_cur + 1); #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT2 %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT2-%u", afl->queue_cur->fname, afl->stage_cur); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3175,7 +3169,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { FLIP_BIT(out_buf, afl->stage_cur + 3); #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT4 %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT4-%u", afl->queue_cur->fname, afl->stage_cur); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3234,7 +3228,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { out_buf[afl->stage_cur] ^= 0xFF; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT8 %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT8-%u", afl->queue_cur->fname, afl->stage_cur); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3325,7 +3319,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { *(u16 *)(out_buf + i) ^= 0xFFFF; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT16 %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT16-%u", afl->queue_cur->fname, afl->stage_cur); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3367,7 +3361,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { *(u32 *)(out_buf + i) ^= 0xFFFFFFFF; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT32 %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT32-%u", afl->queue_cur->fname, afl->stage_cur); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3429,7 +3423,7 @@ skip_bitflip: out_buf[i] = orig + j; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH8+ %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH8+-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3449,7 +3443,7 @@ skip_bitflip: out_buf[i] = orig - j; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH8- %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH8_-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3517,7 +3511,7 @@ skip_bitflip: *(u16 *)(out_buf + i) = orig + j; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH16+ %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH16+-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3535,7 +3529,7 @@ skip_bitflip: *(u16 *)(out_buf + i) = orig - j; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH16- %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH16_-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3558,7 +3552,7 @@ skip_bitflip: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s MOPT_ARITH16+BE %u %u", afl->queue_cur->fname, i, j); + "%s MOPT_ARITH16+BE-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3576,7 +3570,7 @@ skip_bitflip: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s MOPT_ARITH16-BE %u %u", afl->queue_cur->fname, i, j); + "%s MOPT_ARITH16_BE+%u+%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3642,7 +3636,7 @@ skip_bitflip: *(u32 *)(out_buf + i) = orig + j; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH32+ %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH32+-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3660,7 +3654,7 @@ skip_bitflip: *(u32 *)(out_buf + i) = orig - j; #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH32- %u %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH32_-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3683,7 +3677,7 @@ skip_bitflip: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s MOPT_ARITH32+BE %u %u", afl->queue_cur->fname, i, j); + "%s MOPT_ARITH32+BE-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3701,7 +3695,7 @@ skip_bitflip: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s MOPT_ARITH32-BE %u %u", afl->queue_cur->fname, i, j); + "%s MOPT_ARITH32_BE-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3772,7 +3766,7 @@ skip_arith: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s MOPT_INTERESTING8 %u %u", afl->queue_cur->fname, i, j); + "%s MOPT_INTERESTING8-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3831,7 +3825,7 @@ skip_arith: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s MOPT_INTERESTING16 %u %u", afl->queue_cur->fname, i, j); + "%s MOPT_INTERESTING16-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3851,7 +3845,7 @@ skip_arith: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s MOPT_INTERESTING16BE %u %u", afl->queue_cur->fname, i, j); + "%s MOPT_INTERESTING16BE-%u-%u", afl->queue_cur->fname, i, j); #endif *(u16 *)(out_buf + i) = SWAP16(interesting_16[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -3918,7 +3912,7 @@ skip_arith: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s MOPT_INTERESTING32 %u %u", afl->queue_cur->fname, i, j); + "%s MOPT_INTERESTING32-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3938,7 +3932,7 @@ skip_arith: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s MOPT_INTERESTING32BE %u %u", afl->queue_cur->fname, i, j); + "%s MOPT_INTERESTING32BE-%u-%u", afl->queue_cur->fname, i, j); #endif *(u32 *)(out_buf + i) = SWAP32(interesting_32[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -4015,8 +4009,7 @@ skip_interest: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s MOPT_EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, i, j, - afl->extras[j].data); + "%s MOPT_EXTRAS_overwrite-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -4068,8 +4061,7 @@ skip_interest: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s MOPT_EXTRAS insert %u %u:%s", afl->queue_cur->fname, i, j, - afl->extras[j].data); + "%s MOPT_EXTRAS_insert-%u-%u", afl->queue_cur->fname, i, j); #endif if (common_fuzz_stuff(afl, ex_tmp, len + afl->extras[j].len)) { @@ -4131,8 +4123,8 @@ skip_user_extras: #ifdef INTROSPECTION snprintf(afl->mutation, sizeof(afl->mutation), - "%s MOPT_AUTO_EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, - i, j, afl->a_extras[j].data); + "%s MOPT_AUTO_EXTRAS_overwrite-%u-%u", afl->queue_cur->fname, i, + j); #endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } @@ -4275,7 +4267,7 @@ pacemaker_fuzzing: } #ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_HAVOC %u", + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_HAVOC-%u", afl->queue_cur->fname, use_stacking); #endif @@ -4356,7 +4348,7 @@ pacemaker_fuzzing: 1 + rand_below(afl, ARITH_MAX); MOpt_globals.cycles_v2[STAGE_ARITH8]++; #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH+-"); + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8"); strcat(afl->mutation, afl->m_tmp); #endif break; @@ -4392,7 +4384,7 @@ pacemaker_fuzzing: u32 pos = rand_below(afl, temp_len - 1); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+%u", pos); + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+-%u", pos); strcat(afl->mutation, afl->m_tmp); #endif *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); @@ -4402,7 +4394,7 @@ pacemaker_fuzzing: u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16BE+%u-%u", + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16BE+-%u-%u", pos, num); strcat(afl->mutation, afl->m_tmp); #endif @@ -4421,7 +4413,7 @@ pacemaker_fuzzing: u32 pos = rand_below(afl, temp_len - 3); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32-%u", pos); + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32_-%u", pos); strcat(afl->mutation, afl->m_tmp); #endif *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); @@ -4431,7 +4423,7 @@ pacemaker_fuzzing: u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32BE-%u-%u", + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32BE_-%u-%u", pos, num); strcat(afl->mutation, afl->m_tmp); #endif @@ -4446,7 +4438,7 @@ pacemaker_fuzzing: u32 pos = rand_below(afl, temp_len - 3); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+%u", pos); + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+-%u", pos); strcat(afl->mutation, afl->m_tmp); #endif *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); @@ -4456,7 +4448,7 @@ pacemaker_fuzzing: u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32BE+%u-%u", + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32BE+-%u-%u", pos, num); strcat(afl->mutation, afl->m_tmp); #endif @@ -4608,7 +4600,7 @@ pacemaker_fuzzing: clone_to = rand_below(afl, temp_len); #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE_%s_%u_%u_%u", + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE_%s-%u-%u-%u", actually_clone ? "clone" : "insert", clone_from, clone_to, clone_len); strcat(afl->mutation, afl->m_tmp); @@ -4670,7 +4662,7 @@ pacemaker_fuzzing: #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " OVERWRITE_COPY_%u_%u_%u", copy_from, copy_to, + " OVERWRITE_COPY-%u-%u-%u", copy_from, copy_to, copy_len); strcat(afl->mutation, afl->m_tmp); #endif @@ -4682,7 +4674,7 @@ pacemaker_fuzzing: #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " OVERWRITE_FIXED_%u_%u_%u", copy_from, copy_to, + " OVERWRITE_FIXED-%u-%u-%u", copy_from, copy_to, copy_len); strcat(afl->mutation, afl->m_tmp); #endif @@ -4719,8 +4711,7 @@ pacemaker_fuzzing: u32 insert_at = rand_below(afl, temp_len - extra_len + 1); #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " AUTO_EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, - afl->a_extras[use_extra].data); + " AUTO_EXTRA_OVERWRITE-%u-%u", insert_at, extra_len); strcat(afl->mutation, afl->m_tmp); #endif memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, @@ -4738,8 +4729,7 @@ pacemaker_fuzzing: u32 insert_at = rand_below(afl, temp_len - extra_len + 1); #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, - afl->a_extras[use_extra].data); + " EXTRA_OVERWRITE-%u-%u", insert_at, extra_len); strcat(afl->mutation, afl->m_tmp); #endif memcpy(out_buf + insert_at, afl->extras[use_extra].data, @@ -4773,8 +4763,7 @@ pacemaker_fuzzing: ptr = afl->a_extras[use_extra].data; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " AUTO_EXTRA_INSERT_%u_%u_%s", insert_at, extra_len, - ptr); + " AUTO_EXTRA_INSERT-%u-%u", insert_at, extra_len); strcat(afl->mutation, afl->m_tmp); #endif @@ -4784,8 +4773,8 @@ pacemaker_fuzzing: extra_len = afl->extras[use_extra].len; ptr = afl->extras[use_extra].data; #ifdef INTROSPECTION - snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " EXTRA_INSERT_%u_%u_%s", insert_at, extra_len, ptr); + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " EXTRA_INSERT-%u-%u", + insert_at, extra_len); strcat(afl->mutation, afl->m_tmp); #endif @@ -4843,7 +4832,7 @@ pacemaker_fuzzing: #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " SPLICE_OVERWRITE_%u_%u_%u_%s", copy_from, copy_to, + " SPLICE_OVERWRITE-%u-%u-%u-%s", copy_from, copy_to, copy_len, target->fname); strcat(afl->mutation, afl->m_tmp); #endif @@ -4865,7 +4854,7 @@ pacemaker_fuzzing: #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), - " SPLICE_INSERT_%u_%u_%u_%s", clone_from, clone_to, + " SPLICE_INSERT-%u-%u-%u-%s", clone_from, clone_to, clone_len, target->fname); strcat(afl->mutation, afl->m_tmp); #endif -- cgit 1.4.1 From 585ba4c1dda6d8706db122e15718b867fd5489cd Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 8 Nov 2020 16:33:25 +0100 Subject: fix for llvm 10.0.0 --- GNUmakefile.llvm | 2 +- src/afl-cc.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 2bb0263b..cc28695d 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -357,7 +357,7 @@ instrumentation/afl-common.o: ./src/afl-common.c $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(LDFLAGS) ./afl-cc: src/afl-cc.c instrumentation/afl-common.o - $(CC) $(CLANG_CFL) $(CFLAGS) $(CPPFLAGS) $< instrumentation/afl-common.o -o $@ -DLLVM_MAJOR=$(LLVM_MAJOR) $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\" + $(CC) $(CLANG_CFL) $(CFLAGS) $(CPPFLAGS) $< instrumentation/afl-common.o -o $@ -DLLVM_MINOR=$(LLVM_MINOR) -DLLVM_MAJOR=$(LLVM_MAJOR) $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\" @ln -sf afl-cc ./afl-c++ @ln -sf afl-cc ./afl-gcc @ln -sf afl-cc ./afl-g++ diff --git a/src/afl-cc.c b/src/afl-cc.c index 46468dda..a1c1d676 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -38,6 +38,12 @@ #if !defined(LLVM_MAJOR) #define LLVM_MAJOR 0 #endif +#if (LLVM_MINOR - 0 == 0) + #undef LLVM_MINOR +#endif +#if !defined(LLVM_MINOR) + #define LLVM_MINOR 0 +#endif static u8 * obj_path; /* Path to runtime libraries */ static u8 **cc_params; /* Parameters passed to the real CC */ -- cgit 1.4.1 From 82d1c3e18dd1b90fa15f7c056f94dc1a06ee345d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 8 Nov 2020 17:08:30 +0100 Subject: fix wrong llvm version comparison --- src/afl-cc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index a1c1d676..771a58f5 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -507,7 +507,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { -#if LLVM_MAJOR >= 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) +#if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; cc_params[cc_par_cnt++] = "-Xclang"; -- cgit 1.4.1 From 8e1047f5efaece663bba9b8ef86d181198db5101 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 10 Nov 2020 14:08:21 +0100 Subject: support custom mutator introspection --- docs/Changelog.md | 1 + docs/custom_mutators.md | 10 ++++++ include/afl-fuzz.h | 33 ++++++++++++++------ src/afl-fuzz-bitmap.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++--- src/afl-fuzz-mutators.c | 7 +++++ src/afl-fuzz-one.c | 8 +++++ src/afl-fuzz-python.c | 33 ++++++++++++++++++++ 7 files changed, 159 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 50c1d48a..a69f2ff4 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -37,6 +37,7 @@ sending a mail to . - added NO_SPLICING compile option and makefile define - added INTROSPECTION make target that writes all mutations to out/NAME/introspection.txt + - added INTROSPECTION support for custom modules - print special compile time options used in help output - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index 81ee9de4..2516e511 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -42,6 +42,7 @@ size_t afl_custom_havoc_mutation(void *data, unsigned char *buf, size_t buf_size unsigned char afl_custom_havoc_mutation_probability(void *data); unsigned char afl_custom_queue_get(void *data, const unsigned char *filename); void afl_custom_queue_new_entry(void *data, const unsigned char *filename_new_queue, const unsigned int *filename_orig_queue); +const char* afl_custom_introspection(my_mutator_t *data); void afl_custom_deinit(void *data); ``` @@ -81,6 +82,9 @@ def queue_new_entry(filename_new_queue, filename_orig_queue): pass ``` +def introspection(): + return string + ### Custom Mutation - `init`: @@ -130,6 +134,12 @@ def queue_new_entry(filename_new_queue, filename_orig_queue): This methods is called after adding a new test case to the queue. +- `introspection` (optional): + + This method is called after a new queue entry, crash or timeout is + discovered if compiled with INTROSPECTION. The custom mutator can then + return a string (const char *) that reports the exact mutations used. + - `deinit`: The last method to be called, deinitializing the state. diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index e59d5f90..c355263b 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -310,6 +310,7 @@ enum { /* 09 */ PY_FUNC_HAVOC_MUTATION_PROBABILITY, /* 10 */ PY_FUNC_QUEUE_GET, /* 11 */ PY_FUNC_QUEUE_NEW_ENTRY, + /* 12 */ PY_FUNC_INTROSPECTION, PY_FUNC_COUNT }; @@ -684,6 +685,8 @@ typedef struct afl_state { u32 custom_mutators_count; + struct custom_mutator *current_custom_fuzz; + list_t custom_mutator_list; /* this is a fixed buffer of size map_size that can be used by any function if @@ -747,6 +750,15 @@ struct custom_mutator { */ void *(*afl_custom_init)(afl_state_t *afl, unsigned int seed); + /** + * When afl-fuzz was compiled with INTROSPECTION=1 then custom mutators can + * also give introspection information back with this function. + * + * @param data pointer returned in afl_custom_init for this fuzz case + * @return pointer to a text string (const char*) + */ + const char *(*afl_custom_introspection)(void *data); + /** * This method is called just before fuzzing a queue entry with the custom * mutator, and receives the initial buffer. It should return the number of @@ -953,16 +965,17 @@ u8 trim_case_custom(afl_state_t *, struct queue_entry *q, u8 *in_buf, struct custom_mutator *load_custom_mutator_py(afl_state_t *, char *); void finalize_py_module(void *); -u32 fuzz_count_py(void *, const u8 *, size_t); -size_t post_process_py(void *, u8 *, size_t, u8 **); -s32 init_trim_py(void *, u8 *, size_t); -s32 post_trim_py(void *, u8); -size_t trim_py(void *, u8 **); -size_t havoc_mutation_py(void *, u8 *, size_t, u8 **, size_t); -u8 havoc_mutation_probability_py(void *); -u8 queue_get_py(void *, const u8 *); -void queue_new_entry_py(void *, const u8 *, const u8 *); -void deinit_py(void *); +u32 fuzz_count_py(void *, const u8 *, size_t); +size_t post_process_py(void *, u8 *, size_t, u8 **); +s32 init_trim_py(void *, u8 *, size_t); +s32 post_trim_py(void *, u8); +size_t trim_py(void *, u8 **); +size_t havoc_mutation_py(void *, u8 *, size_t, u8 **, size_t); +u8 havoc_mutation_probability_py(void *); +u8 queue_get_py(void *, const u8 *); +const char *introspection_py(void *); +void queue_new_entry_py(void *, const u8 *, const u8 *); +void deinit_py(void *); #endif diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 735420c3..132499d6 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -588,8 +588,32 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { add_to_queue(afl, queue_fn, len, 0); #ifdef INTROSPECTION - fprintf(afl->introspection_file, "QUEUE %s = %s\n", afl->mutation, - afl->queue_top->fname); + if (afl->mutation[0] != 0) { + + fprintf(afl->introspection_file, "QUEUE %s = %s\n", afl->mutation, + afl->queue_top->fname); + + } else if (afl->custom_mutators_count && afl->current_custom_fuzz) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (afl->current_custom_fuzz == el && el->afl_custom_introspection) { + + const char *ptr = el->afl_custom_introspection(el->data); + + if (ptr != NULL && *ptr != 0) { + + fprintf(afl->introspection_file, "QUEUE CUSTOM %s = %s\n", ptr, + afl->queue_top->fname); + + } + + } + + }); + + } + #endif if (hnb == 2) { @@ -665,7 +689,32 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { ++afl->unique_tmouts; #ifdef INTROSPECTION - fprintf(afl->introspection_file, "UNIQUE_TIMEOUT %s\n", afl->mutation); + if (afl->mutation[0] != 0) { + + fprintf(afl->introspection_file, "UNIQUE_TIMEOUT %s\n", afl->mutation); + + } else if (afl->custom_mutators_count && afl->current_custom_fuzz) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (afl->current_custom_fuzz == el && el->afl_custom_introspection) { + + const char *ptr = el->afl_custom_introspection(el->data); + + if (ptr != NULL && *ptr != 0) { + + fprintf(afl->introspection_file, + "UNIQUE_TIMEOUT CUSTOM %s = %s\n", ptr, + afl->queue_top->fname); + + } + + } + + }); + + } + #endif /* Before saving, we make sure that it's a genuine hang by re-running @@ -751,7 +800,31 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { ++afl->unique_crashes; #ifdef INTROSPECTION - fprintf(afl->introspection_file, "UNIQUE_CRASH %s\n", afl->mutation); + if (afl->mutation[0] != 0) { + + fprintf(afl->introspection_file, "UNIQUE_CRASH %s\n", afl->mutation); + + } else if (afl->custom_mutators_count && afl->current_custom_fuzz) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (afl->current_custom_fuzz == el && el->afl_custom_introspection) { + + const char *ptr = el->afl_custom_introspection(el->data); + + if (ptr != NULL && *ptr != 0) { + + fprintf(afl->introspection_file, "UNIQUE_CRASH CUSTOM %s = %s\n", + ptr, afl->queue_top->fname); + + } + + } + + }); + + } + #endif if (unlikely(afl->infoexec)) { diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index c4d7233c..1d14f657 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -166,6 +166,13 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { } + /* "afl_custom_introspection", optional */ +#ifdef INTROSPECTION + mutator->afl_custom_introspection = dlsym(dh, "afl_custom_introspection"); + if (!mutator->afl_custom_introspection) + ACTF("optional symbol 'afl_custom_introspection' not found."); +#endif + /* "afl_custom_fuzz_count", optional */ mutator->afl_custom_fuzz_count = dlsym(dh, "afl_custom_fuzz_count"); if (!mutator->afl_custom_fuzz_count) diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 91bbced6..64365ebb 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1780,10 +1780,16 @@ custom_mutator_stage: orig_hit_cnt = afl->queued_paths + afl->unique_crashes; +#ifdef INTROSPECTION + afl->mutation[0] = 0; +#endif + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { if (el->afl_custom_fuzz) { + afl->current_custom_fuzz = el; + if (el->afl_custom_fuzz_count) afl->stage_max = el->afl_custom_fuzz_count(el->data, out_buf, len); else @@ -1889,6 +1895,8 @@ custom_mutator_stage: }); + afl->current_custom_fuzz = NULL; + if (!has_custom_fuzz) goto havoc_stage; new_hit_cnt = afl->queued_paths + afl->unique_crashes; diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index adb92649..fe16bc46 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -163,6 +163,8 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { PyObject_GetAttrString(py_module, "queue_get"); py_functions[PY_FUNC_QUEUE_NEW_ENTRY] = PyObject_GetAttrString(py_module, "queue_new_entry"); + py_functions[PY_FUNC_INTROSPECTION] = + PyObject_GetAttrString(py_module, "introspection"); py_functions[PY_FUNC_DEINIT] = PyObject_GetAttrString(py_module, "deinit"); if (!py_functions[PY_FUNC_DEINIT]) FATAL("deinit function not found in python module"); @@ -381,6 +383,15 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, } + #ifdef INTROSPECTION + if (py_functions[PY_FUNC_INTROSPECTION]) { + + mutator->afl_custom_introspection = introspection_py; + + } + + #endif + OKF("Python mutator '%s' installed successfully.", module_name); /* Initialize the custom mutator */ @@ -679,6 +690,28 @@ u8 havoc_mutation_probability_py(void *py_mutator) { } +const char *introspection_py(void *py_mutator) { + + PyObject *py_args, *py_value; + + py_args = PyTuple_New(0); + py_value = PyObject_CallObject( + ((py_mutator_t *)py_mutator)->py_functions[PY_FUNC_INTROSPECTION], + py_args); + Py_DECREF(py_args); + + if (py_value == NULL) { + + return NULL; + + } else { + + return PyByteArray_AsString(py_value); + + } + +} + u8 queue_get_py(void *py_mutator, const u8 *filename) { PyObject *py_args, *py_value; -- cgit 1.4.1 From 1dfd7df7c0e28b6f356bda714bedfc612bf2db75 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 10 Nov 2020 14:16:36 +0100 Subject: small fix to actually document custom mutator introspection --- src/afl-fuzz-one.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 64365ebb..0adc3719 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1846,12 +1846,6 @@ custom_mutator_stage: if (mutated_size > 0) { -#ifdef INTROSPECTION - snprintf(afl->mutation, sizeof(afl->mutation), "%s CUSTOM-%s", - afl->queue_cur->fname, - target != NULL ? (char *)target->fname : "none"); -#endif - if (common_fuzz_stuff(afl, mutated_buf, (u32)mutated_size)) { goto abandon_entry; -- cgit 1.4.1 From a3928e5a62c3ed4af321cc6030efc250b6a585c0 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 10 Nov 2020 14:20:41 +0100 Subject: small fix to actually document custom mutator introspection --- src/afl-fuzz-bitmap.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 132499d6..a4407af7 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -588,12 +588,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { add_to_queue(afl, queue_fn, len, 0); #ifdef INTROSPECTION - if (afl->mutation[0] != 0) { - - fprintf(afl->introspection_file, "QUEUE %s = %s\n", afl->mutation, - afl->queue_top->fname); - - } else if (afl->custom_mutators_count && afl->current_custom_fuzz) { + if (afl->custom_mutators_count && afl->current_custom_fuzz) { LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { @@ -612,6 +607,11 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { }); + } else if (afl->mutation[0] != 0) { + + fprintf(afl->introspection_file, "QUEUE %s = %s\n", afl->mutation, + afl->queue_top->fname); + } #endif @@ -689,11 +689,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { ++afl->unique_tmouts; #ifdef INTROSPECTION - if (afl->mutation[0] != 0) { - - fprintf(afl->introspection_file, "UNIQUE_TIMEOUT %s\n", afl->mutation); - - } else if (afl->custom_mutators_count && afl->current_custom_fuzz) { + if (afl->custom_mutators_count && afl->current_custom_fuzz) { LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { @@ -713,6 +709,10 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { }); + } else if (afl->mutation[0] != 0) { + + fprintf(afl->introspection_file, "UNIQUE_TIMEOUT %s\n", afl->mutation); + } #endif @@ -800,11 +800,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { ++afl->unique_crashes; #ifdef INTROSPECTION - if (afl->mutation[0] != 0) { - - fprintf(afl->introspection_file, "UNIQUE_CRASH %s\n", afl->mutation); - - } else if (afl->custom_mutators_count && afl->current_custom_fuzz) { + if (afl->custom_mutators_count && afl->current_custom_fuzz) { LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { @@ -823,6 +819,10 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { }); + } else if (afl->mutation[0] != 0) { + + fprintf(afl->introspection_file, "UNIQUE_CRASH %s\n", afl->mutation); + } #endif -- cgit 1.4.1 From 5357ae5f918cb358a541d4d8a4cbf678d7682242 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 10 Nov 2020 19:20:13 +0100 Subject: remove duplicate include --- src/afl-gotcpu.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/afl-gotcpu.c b/src/afl-gotcpu.c index bd0f7de6..1aea3e40 100644 --- a/src/afl-gotcpu.c +++ b/src/afl-gotcpu.c @@ -65,7 +65,6 @@ #define cpu_set_t cpuset_t #elif defined(__NetBSD__) #include - #include #elif defined(__APPLE__) #include #include -- cgit 1.4.1 From 54a312a5fe7d83fcee8b420868bc918622e9b3a6 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Fri, 13 Nov 2020 00:44:08 +0100 Subject: more small fixes --- qemu_mode/libcompcov/pmparser.h | 2 +- qemu_mode/qemuafl | 1 - src/afl-common.c | 2 +- src/afl-fuzz-python.c | 1 + .../samples/persistent/simple_target_noncrashing.c | 16 ++++++++++------ 5 files changed, 13 insertions(+), 9 deletions(-) delete mode 160000 qemu_mode/qemuafl (limited to 'src') diff --git a/qemu_mode/libcompcov/pmparser.h b/qemu_mode/libcompcov/pmparser.h index 9421d47e..1a3d8834 100644 --- a/qemu_mode/libcompcov/pmparser.h +++ b/qemu_mode/libcompcov/pmparser.h @@ -108,7 +108,6 @@ void pmparser_print(procmaps_struct *map, int order); procmaps_iterator *pmparser_parse(int pid) { - procmaps_iterator *maps_it = malloc(sizeof(procmaps_iterator)); char maps_path[500]; if (pid >= 0) { @@ -129,6 +128,7 @@ procmaps_iterator *pmparser_parse(int pid) { } + procmaps_iterator *maps_it = malloc(sizeof(procmaps_iterator)); int ind = 0; char buf[PROCMAPS_LINE_MAX_LENGTH]; // int c; diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl deleted file mode 160000 index d66c9e26..00000000 --- a/qemu_mode/qemuafl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d66c9e2654efa8939f0fe6995d11a72b98a4da3e diff --git a/src/afl-common.c b/src/afl-common.c index 19c9419b..8cf1a444 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -342,7 +342,7 @@ u8 *find_binary(u8 *fname) { if (stat(target_path, &st) || !S_ISREG(st.st_mode) || !(st.st_mode & 0111) || st.st_size < 4) { - free(target_path); + ck_free(target_path); FATAL("Program '%s' not found or not executable", fname); } diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index fe16bc46..596b733e 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -214,6 +214,7 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { PyErr_Print(); fprintf(stderr, "Failed to load \"%s\"\n", module_name); + free(py); return NULL; } diff --git a/unicorn_mode/samples/persistent/simple_target_noncrashing.c b/unicorn_mode/samples/persistent/simple_target_noncrashing.c index 00764473..9257643b 100644 --- a/unicorn_mode/samples/persistent/simple_target_noncrashing.c +++ b/unicorn_mode/samples/persistent/simple_target_noncrashing.c @@ -10,7 +10,7 @@ * Written by Nathan Voss * Adapted by Lukas Seidel */ - +#include int main(int argc, char** argv) { if(argc < 2){ @@ -19,15 +19,19 @@ int main(int argc, char** argv) { char *data_buf = argv[1]; - if len(data_buf < 20) { - if (data_buf[20] != 0) { + if (strlen(data_buf) >= 21 && data_buf[20] != 0) { printf("Not crashing"); - } else if (data_buf[0] > 0x10 && data_buf[0] < 0x20 && data_buf[1] > data_buf[2]) { + } else if (strlen(data_buf) > 1 + && data_buf[0] > 0x10 && data_buf[0] < 0x20 && data_buf[1] > data_buf[2]) { printf("Also not crashing with databuf[0] == %c", data_buf[0]) - } else if (data_buf[9] == 0x00 && data_buf[10] != 0x00 && data_buf[11] == 0x00) { + } +#if 0 + // not possible with argv (zero terminated strings) (hexcoder-) + // do not try to access data_buf[10] and beyond + else if (data_buf[9] == 0x00 && data_buf[10] != 0x00 && data_buf[11] == 0x00) { // Cause a crash if data[10] is not zero, but [9] and [11] are zero unsigned char invalid_read = *(unsigned char *) 0x00000000; } - +#endif return 0; } -- cgit 1.4.1 From 1b75cc9f742ca6f5c95788269c66c346036b7233 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 30 Nov 2020 21:38:15 +0100 Subject: add DEBUGF --- src/afl-cc.c | 4 ++-- src/afl-ld-lto.c | 10 ++++------ src/afl-showmap.c | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 25ed923a..6d39b890 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1582,7 +1582,7 @@ int main(int argc, char **argv, char **envp) { if (debug) { - SAYF(cMGN "[D]" cRST " cd '%s';", getthecwd()); + DEBUGF("cd '%s';", getthecwd()); for (i = 0; i < argc; i++) SAYF(" '%s'", argv[i]); SAYF("\n"); @@ -1610,7 +1610,7 @@ int main(int argc, char **argv, char **envp) { if (debug) { - SAYF(cMGN "[D]" cRST " cd '%s';", getthecwd()); + DEBUGF("cd '%s';", getthecwd()); for (i = 0; i < (s32)cc_par_cnt; i++) SAYF(" '%s'", cc_params[i]); SAYF("\n"); diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index 771e2d0d..e6ea1f1d 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -182,9 +182,7 @@ static void edit_params(int argc, char **argv) { instrim = 1; if (debug) - SAYF(cMGN "[D] " cRST - "passthrough=%s instrim=%d, gold_pos=%d, gold_present=%s " - "inst_present=%s rt_present=%s rt_lto_present=%s\n", + DEBUGF("passthrough=%s instrim=%d, gold_pos=%d, gold_present=%s inst_present=%s rt_present=%s rt_lto_present=%s\n", passthrough ? "true" : "false", instrim, gold_pos, gold_present ? "true" : "false", inst_present ? "true" : "false", rt_present ? "true" : "false", rt_lto_present ? "true" : "false"); @@ -280,7 +278,7 @@ int main(int argc, char **argv) { if (getcwd(thecwd, sizeof(thecwd)) != 0) strcpy(thecwd, "."); - SAYF(cMGN "[D] " cRST "cd \"%s\";", thecwd); + DEBUGF("cd \"%s\";", thecwd); for (i = 0; i < argc; i++) SAYF(" \"%s\"", argv[i]); SAYF("\n"); @@ -315,7 +313,7 @@ int main(int argc, char **argv) { if (debug) { - SAYF(cMGN "[D]" cRST " cd \"%s\";", thecwd); + DEBUGF("cd \"%s\";", thecwd); for (i = 0; i < ld_param_cnt; i++) SAYF(" \"%s\"", ld_params[i]); SAYF("\n"); @@ -333,7 +331,7 @@ int main(int argc, char **argv) { if (pid < 0) PFATAL("fork() failed"); if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed"); - if (debug) SAYF(cMGN "[D] " cRST "linker result: %d\n", status); + if (debug) DEBUGF("linker result: %d\n", status); if (!just_version) { diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 69527007..a8e7d3f9 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -904,7 +904,7 @@ int main(int argc, char **argv_orig, char **envp) { if (getenv("AFL_DEBUG")) { - SAYF(cMGN "[D]" cRST); + DEBUGF(""); for (i = 0; i < argc; i++) SAYF(" %s", argv[i]); SAYF("\n"); @@ -1066,7 +1066,7 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_DEBUG")) { int i = optind; - SAYF(cMGN "[D]" cRST " %s:", fsrv->target_path); + DEBUGF("%s:", fsrv->target_path); while (argv[i] != NULL) { SAYF(" \"%s\"", argv[i++]); -- cgit 1.4.1 From e769102491a4a5aa90afe57cce48211338133d3f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 30 Nov 2020 21:54:18 +0100 Subject: more DEBUGF --- include/debug.h | 10 +++++++ instrumentation/LLVMInsTrim.so.cc | 4 +-- instrumentation/afl-gcc-pass.so.cc | 36 +++++++++++------------ instrumentation/afl-llvm-common.cc | 34 ++++++++++----------- instrumentation/afl-llvm-lto-instrumentlist.so.cc | 9 +++--- src/afl-cc.c | 18 ++++-------- src/afl-ld-lto.c | 10 ++++--- 7 files changed, 60 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/include/debug.h b/include/debug.h index e6d3c3fc..5512023c 100644 --- a/include/debug.h +++ b/include/debug.h @@ -270,6 +270,16 @@ \ } while (0) +/* Show a prefixed debug output. */ + +#define DEBUGF(x...) \ + do { \ + \ + SAYF(cMGN "[D] " cBRI "DEBUG: " cRST x); \ + SAYF(cRST ""); \ + \ + } while (0) + /* Error-checking versions of read() and write() that call RPFATAL() as appropriate. */ diff --git a/instrumentation/LLVMInsTrim.so.cc b/instrumentation/LLVMInsTrim.so.cc index 61a420ba..6b3231e6 100644 --- a/instrumentation/LLVMInsTrim.so.cc +++ b/instrumentation/LLVMInsTrim.so.cc @@ -268,8 +268,8 @@ struct InsTrim : public ModulePass { for (auto &BB : F) if (BB.size() > 0) ++bb_cnt; - SAYF(cMGN "[D] " cRST "Function %s size %zu %u\n", - F.getName().str().c_str(), F.size(), bb_cnt); + DEBUGF("Function %s size %zu %u\n", F.getName().str().c_str(), F.size(), + bb_cnt); } diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index f94bb57f..e116e7d1 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -627,9 +627,8 @@ struct afl_pass : gimple_opt_pass { } if (debug) - SAYF(cMGN "[D] " cRST - "loaded allowlist with %zu file and %zu function entries\n", - allowListFiles.size(), allowListFunctions.size()); + DEBUGF("loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); } @@ -702,9 +701,8 @@ struct afl_pass : gimple_opt_pass { } if (debug) - SAYF(cMGN "[D] " cRST - "loaded denylist with %zu file and %zu function entries\n", - denyListFiles.size(), denyListFunctions.size()); + DEBUGF("loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); } @@ -745,10 +743,10 @@ struct afl_pass : gimple_opt_pass { if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the deny function list, " - "not instrumenting ... \n", - instFunction.c_str()); + DEBUGF( + "Function %s is in the deny function list, not " + "instrumenting ... \n", + instFunction.c_str()); return false; } @@ -825,10 +823,10 @@ struct afl_pass : gimple_opt_pass { if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allow function list, " - "instrumenting ... \n", - instFunction.c_str()); + DEBUGF( + "Function %s is in the allow function list, instrumenting " + "... \n", + instFunction.c_str()); return true; } @@ -859,11 +857,11 @@ struct afl_pass : gimple_opt_pass { if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allowlist (%s), " - "instrumenting ... \n", - IDENTIFIER_POINTER(DECL_NAME(F->decl)), - source_file.c_str()); + DEBUGF( + "Function %s is in the allowlist (%s), instrumenting ... " + "\n", + IDENTIFIER_POINTER(DECL_NAME(F->decl)), + source_file.c_str()); return true; } diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc index 189b4ec6..21c4d204 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -173,9 +173,8 @@ void initInstrumentList() { } if (debug) - SAYF(cMGN "[D] " cRST - "loaded allowlist with %zu file and %zu function entries\n", - allowListFiles.size(), allowListFunctions.size()); + DEBUGF("loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); } @@ -248,9 +247,8 @@ void initInstrumentList() { } if (debug) - SAYF(cMGN "[D] " cRST - "loaded denylist with %zu file and %zu function entries\n", - denyListFiles.size(), denyListFunctions.size()); + DEBUGF("loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); } @@ -409,10 +407,10 @@ bool isInInstrumentList(llvm::Function *F) { if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the deny function list, " - "not instrumenting ... \n", - instFunction.c_str()); + DEBUGF( + "Function %s is in the deny function list, not instrumenting " + "... \n", + instFunction.c_str()); return false; } @@ -489,10 +487,10 @@ bool isInInstrumentList(llvm::Function *F) { if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allow function list, " - "instrumenting ... \n", - instFunction.c_str()); + DEBUGF( + "Function %s is in the allow function list, instrumenting " + "... \n", + instFunction.c_str()); return true; } @@ -523,10 +521,10 @@ bool isInInstrumentList(llvm::Function *F) { if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allowlist (%s), " - "instrumenting ... \n", - F->getName().str().c_str(), source_file.c_str()); + DEBUGF( + "Function %s is in the allowlist (%s), instrumenting ... " + "\n", + F->getName().str().c_str(), source_file.c_str()); return true; } diff --git a/instrumentation/afl-llvm-lto-instrumentlist.so.cc b/instrumentation/afl-llvm-lto-instrumentlist.so.cc index a7331444..416dbb88 100644 --- a/instrumentation/afl-llvm-lto-instrumentlist.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentlist.so.cc @@ -105,15 +105,14 @@ bool AFLcheckIfInstrument::runOnModule(Module &M) { if (isInInstrumentList(&F)) { if (debug) - SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n", - F.getName().str().c_str()); + DEBUGF("function %s is in the instrument file list\n", + F.getName().str().c_str()); } else { if (debug) - SAYF(cMGN "[D] " cRST - "function %s is NOT in the instrument file list\n", - F.getName().str().c_str()); + DEBUGF("function %s is NOT in the instrument file list\n", + F.getName().str().c_str()); auto & Ctx = F.getContext(); AttributeList Attrs = F.getAttributes(); diff --git a/src/afl-cc.c b/src/afl-cc.c index 6d39b890..cc9854b6 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -354,11 +354,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-B"; cc_params[cc_par_cnt++] = obj_path; - if (clang_mode) { + if (clang_mode) { cc_params[cc_par_cnt++] = "-no-integrated-as"; } - cc_params[cc_par_cnt++] = "-no-integrated-as"; - - } } if (compiler_mode == GCC_PLUGIN) { @@ -708,7 +705,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { } if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || - getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_ALL") || lto_mode) { + getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_ALL") || + lto_mode) { cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; @@ -1002,16 +1000,11 @@ int main(int argc, char **argv, char **envp) { } - if (strncmp(callname, "afl-clang", 9) == 0) { clang_mode = 1; - if (strncmp(callname, "afl-clang++", 11) == 0) { - - plusplus_mode = 1; - - } + if (strncmp(callname, "afl-clang++", 11) == 0) { plusplus_mode = 1; } } @@ -1085,8 +1078,7 @@ int main(int argc, char **argv, char **envp) { if (instrument_mode == 0) instrument_mode = INSTRUMENT_CFG; else if (instrument_mode != INSTRUMENT_CFG) - FATAL( - "you cannot set AFL_LLVM_INSTRUMENT and AFL_LLVM_INSTRIM together"); + FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_LLVM_INSTRIM together"); } diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index e6ea1f1d..16feaa80 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -182,10 +182,12 @@ static void edit_params(int argc, char **argv) { instrim = 1; if (debug) - DEBUGF("passthrough=%s instrim=%d, gold_pos=%d, gold_present=%s inst_present=%s rt_present=%s rt_lto_present=%s\n", - passthrough ? "true" : "false", instrim, gold_pos, - gold_present ? "true" : "false", inst_present ? "true" : "false", - rt_present ? "true" : "false", rt_lto_present ? "true" : "false"); + DEBUGF( + "passthrough=%s instrim=%d, gold_pos=%d, gold_present=%s " + "inst_present=%s rt_present=%s rt_lto_present=%s\n", + passthrough ? "true" : "false", instrim, gold_pos, + gold_present ? "true" : "false", inst_present ? "true" : "false", + rt_present ? "true" : "false", rt_lto_present ? "true" : "false"); for (i = 1; i < argc; i++) { -- cgit 1.4.1 From 8584f9d2b5de9687c518c672e471f4f8cd9166fa Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 1 Dec 2020 13:13:11 +0100 Subject: added AFL_NO_AUTODICT --- docs/Changelog.md | 1 + docs/env_variables.md | 3 + include/envs.h | 1 + instrumentation/README.lto.md | 33 +++++++---- src/afl-forkserver.c | 125 ++++++++++++++++++++++-------------------- src/afl-fuzz.c | 1 + 6 files changed, 95 insertions(+), 69 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 3c20f8bd..7fa7ff53 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -34,6 +34,7 @@ sending a mail to . - crashing seeds are now not prohibiting a run anymore but are skipped. They are used for splicing though. - update MOpt for expanded havoc modes + - setting the env var AFL_NO_AUTODICT will not load an LTO autodictionary - added NO_SPLICING compile option and makefile define - added INTROSPECTION make target that writes all mutations to out/NAME/introspection.txt diff --git a/docs/env_variables.md b/docs/env_variables.md index 04ba032a..f7b4c994 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -294,6 +294,9 @@ checks or alter some of the more exotic semantics of the tool: on Linux systems. This slows things down, but lets you run more instances of afl-fuzz than would be prudent (if you really want to). + - Setting `AFL_NO_AUTODICT` will not load an LTO generated auto dictionary + that is compiled into the target. + - `AFL_SKIP_CRASHES` causes AFL++ to tolerate crashing files in the input queue. This can help with rare situations where a program crashes only intermittently, but it's not really recommended under normal operating diff --git a/include/envs.h b/include/envs.h index 8255cf4f..3aa05cb5 100644 --- a/include/envs.h +++ b/include/envs.h @@ -100,6 +100,7 @@ static char *afl_environment_variables[] = { "AFL_LLVM_LTO_STARTID", "AFL_LLVM_LTO_DONTWRITEID", "AFL_NO_ARITH", + "AFL_NO_AUTODICT", "AFL_NO_BUILTIN", "AFL_NO_CPU_RED", "AFL_NO_FORKSRV", diff --git a/instrumentation/README.lto.md b/instrumentation/README.lto.md index abdbd2ac..62e98902 100644 --- a/instrumentation/README.lto.md +++ b/instrumentation/README.lto.md @@ -60,7 +60,12 @@ AUTODICTIONARY: 11 strings found ## Getting llvm 11+ -### Installing llvm from the llvm repository (version 11) +### Installing llvm version 11 + +llvm 11 should be available in all current Linux repository. +If you use an outdated Linux distribution read the next section. + +### Installing llvm from the llvm repository (version 12) Installing the llvm snapshot builds is easy and mostly painless: @@ -73,11 +78,11 @@ then add the pgp key of llvm and install the packages: ``` wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - apt-get update && apt-get upgrade -y -apt-get install -y clang-11 clang-tools-11 libc++1-11 libc++-11-dev \ - libc++abi1-11 libc++abi-11-dev libclang1-11 libclang-11-dev \ - libclang-common-11-dev libclang-cpp11 libclang-cpp11-dev liblld-11 \ - liblld-11-dev liblldb-11 liblldb-11-dev libllvm11 libomp-11-dev \ - libomp5-11 lld-11 lldb-11 llvm-11 llvm-11-dev llvm-11-runtime llvm-11-tools +apt-get install -y clang-12 clang-tools-12 libc++1-12 libc++-12-dev \ + libc++abi1-12 libc++abi-12-dev libclang1-12 libclang-12-dev \ + libclang-common-12-dev libclang-cpp11 libclang-cpp11-dev liblld-12 \ + liblld-12-dev liblldb-12 liblldb-12-dev libllvm11 libomp-12-dev \ + libomp5-12 lld-12 lldb-12 llvm-12 llvm-12-dev llvm-12-runtime llvm-12-tools ``` ### Building llvm yourself (version 12) @@ -120,16 +125,22 @@ While compiling, a dictionary based on string comparisons is automatically generated and put into the target binary. This dictionary is transfered to afl-fuzz on start. This improves coverage statistically by 5-10% :) +Note that if for any reason you do not want to use the autodictionary feature +then just set the environment variable `AFL_NO_AUTODICT` when starting afl-fuzz. + ## Fixed memory map -To speed up fuzzing, it is possible to set a fixed shared memory map. +To speed up fuzzing a little bit more, it is possible to set a fixed shared +memory map. Recommended is the value 0x10000. + In most cases this will work without any problems. However if a target uses early constructors, ifuncs or a deferred forkserver this can crash the target. -On unusual operating systems/processors/kernels or weird libraries this might -fail so to change the fixed address at compile time set -AFL_LLVM_MAP_ADDR with a better value (a value of 0 or empty sets the map address -to be dynamic - the original afl way, which is slower). + +Also on unusual operating systems/processors/kernels or weird libraries the +recommended 0x10000 address might not work, so then change the fixed address. + +To enable this feature set AFL_LLVM_MAP_ADDR with the address. ## Document edge IDs diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 3814a77e..01ef1d9e 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -348,9 +348,10 @@ static void report_error_and_exit(int error) { void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, u8 debug_child_output) { - int st_pipe[2], ctl_pipe[2]; - s32 status; - s32 rlen; + int st_pipe[2], ctl_pipe[2]; + s32 status; + s32 rlen; + char *ignore_autodict = getenv("AFL_NO_AUTODICT"); if (!be_quiet) { ACTF("Spinning up the fork server..."); } @@ -607,7 +608,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, fsrv->use_shmem_fuzz = 1; if (!be_quiet) { ACTF("Using SHARED MEMORY FUZZING feature."); } - if ((status & FS_OPT_AUTODICT) == 0) { + if ((status & FS_OPT_AUTODICT) == 0 || ignore_autodict) { u32 send_status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); if (write(fsrv->fsrv_ctl_fd, &send_status, 4) != 4) { @@ -660,101 +661,109 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if ((status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT) { - if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) { + if (ignore_autodict) { - // this is not afl-fuzz - or it is cmplog - we deny and return - if (fsrv->use_shmem_fuzz) { + if (!be_quiet) { WARNF("Ignoring offered AUTODICT feature."); } - status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); + } else { - } else { + if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) { - status = (FS_OPT_ENABLED); + // this is not afl-fuzz - or it is cmplog - we deny and return + if (fsrv->use_shmem_fuzz) { - } + status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); - if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { + } else { - FATAL("Writing to forkserver failed."); + status = (FS_OPT_ENABLED); - } + } - return; + if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { - } + FATAL("Writing to forkserver failed."); - if (!be_quiet) { ACTF("Using AUTODICT feature."); } + } - if (fsrv->use_shmem_fuzz) { + return; - status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ); + } - } else { + if (!be_quiet) { ACTF("Using AUTODICT feature."); } - status = (FS_OPT_ENABLED | FS_OPT_AUTODICT); + if (fsrv->use_shmem_fuzz) { - } + status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ); - if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { + } else { - FATAL("Writing to forkserver failed."); + status = (FS_OPT_ENABLED | FS_OPT_AUTODICT); - } + } - if (read(fsrv->fsrv_st_fd, &status, 4) != 4) { + if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { - FATAL("Reading from forkserver failed."); + FATAL("Writing to forkserver failed."); - } + } - if (status < 2 || (u32)status > 0xffffff) { + if (read(fsrv->fsrv_st_fd, &status, 4) != 4) { - FATAL("Dictionary has an illegal size: %d", status); + FATAL("Reading from forkserver failed."); - } + } - u32 offset = 0, count = 0; - u32 len = status; - u8 *dict = ck_alloc(len); - if (dict == NULL) { + if (status < 2 || (u32)status > 0xffffff) { - FATAL("Could not allocate %u bytes of autodictionary memory", len); + FATAL("Dictionary has an illegal size: %d", status); - } + } - while (len != 0) { + u32 offset = 0, count = 0; + u32 len = status; + u8 *dict = ck_alloc(len); + if (dict == NULL) { - rlen = read(fsrv->fsrv_st_fd, dict + offset, len); - if (rlen > 0) { + FATAL("Could not allocate %u bytes of autodictionary memory", len); - len -= rlen; - offset += rlen; + } - } else { + while (len != 0) { - FATAL( - "Reading autodictionary fail at position %u with %u bytes " - "left.", - offset, len); + rlen = read(fsrv->fsrv_st_fd, dict + offset, len); + if (rlen > 0) { + + len -= rlen; + offset += rlen; + + } else { + + FATAL( + "Reading autodictionary fail at position %u with %u bytes " + "left.", + offset, len); + + } } - } + offset = 0; + while (offset < (u32)status && + (u8)dict[offset] + offset < (u32)status) { + + fsrv->add_extra_func(fsrv->afl_ptr, dict + offset + 1, + (u8)dict[offset]); + offset += (1 + dict[offset]); + count++; - offset = 0; - while (offset < (u32)status && - (u8)dict[offset] + offset < (u32)status) { + } - fsrv->add_extra_func(fsrv->afl_ptr, dict + offset + 1, - (u8)dict[offset]); - offset += (1 + dict[offset]); - count++; + if (!be_quiet) { ACTF("Loaded %u autodictionary entries", count); } + ck_free(dict); } - if (!be_quiet) { ACTF("Loaded %u autodictionary entries", count); } - ck_free(dict); - } } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index b60908da..b91d862d 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -187,6 +187,7 @@ static void usage(u8 *argv0, int more_help) { " used. Defaults to 200.\n" "AFL_NO_AFFINITY: do not check for an unused cpu core to use for fuzzing\n" "AFL_NO_ARITH: skip arithmetic mutations in deterministic stage\n" + "AFL_NO_AUTODICT: do not load an offered auto dictionary compiled into a target\n" "AFL_NO_CPU_RED: avoid red color for showing very high cpu usage\n" "AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n" "AFL_NO_SNAPSHOT: do not use the snapshot feature (if the snapshot lkm is loaded)\n" -- cgit 1.4.1 From c05e4efbe9b4e7d1ff078b7a392621f2ca7572e6 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 1 Dec 2020 14:40:30 +0100 Subject: renamed examples/ to utils/ --- GNUmakefile | 36 +- README.md | 7 +- docs/Changelog.md | 1 + docs/FAQ.md | 2 +- docs/binaryonly_fuzzing.md | 6 +- docs/custom_mutators.md | 4 +- docs/env_variables.md | 2 +- docs/life_pro_tips.md | 4 +- docs/notes_for_asan.md | 4 +- docs/parallel_fuzzing.md | 2 +- examples/README.md | 54 -- examples/afl_frida/GNUmakefile | 23 - examples/afl_frida/Makefile | 2 - examples/afl_frida/README.md | 34 - examples/afl_frida/afl-frida.c | 542 --------------- examples/afl_frida/afl-frida.h | 53 -- examples/afl_frida/libtestinstr.c | 35 - examples/afl_network_proxy/GNUmakefile | 43 -- examples/afl_network_proxy/Makefile | 2 - examples/afl_network_proxy/README.md | 61 -- examples/afl_network_proxy/afl-network-client.c | 415 ------------ examples/afl_network_proxy/afl-network-server.c | 720 -------------------- examples/afl_proxy/Makefile | 7 - examples/afl_proxy/README.md | 9 - examples/afl_proxy/afl-proxy.c | 238 ------- examples/afl_untracer/Makefile | 16 - examples/afl_untracer/README.md | 60 -- examples/afl_untracer/TODO | 2 - examples/afl_untracer/afl-untracer.c | 768 ---------------------- examples/afl_untracer/ghidra_get_patchpoints.java | 84 --- examples/afl_untracer/ida_get_patchpoints.py | 62 -- examples/afl_untracer/libtestinstr.c | 35 - examples/afl_untracer/libtestinstr.so | Bin 17152 -> 0 bytes examples/afl_untracer/patches.txt | 34 - examples/aflpp_driver/GNUmakefile | 46 -- examples/aflpp_driver/Makefile | 2 - examples/aflpp_driver/aflpp_driver.c | 326 --------- examples/aflpp_driver/aflpp_driver_test.c | 32 - examples/aflpp_driver/aflpp_qemu_driver.c | 38 -- examples/aflpp_driver/aflpp_qemu_driver_hook.c | 22 - examples/analysis_scripts/queue2csv.sh | 122 ---- examples/argv_fuzzing/Makefile | 58 -- examples/argv_fuzzing/README.md | 16 - examples/argv_fuzzing/argv-fuzz-inl.h | 90 --- examples/argv_fuzzing/argvfuzz.c | 49 -- examples/asan_cgroups/limit_memory.sh | 157 ----- examples/bash_shellshock/shellshock-fuzz.diff | 59 -- examples/canvas_harness/canvas_harness.html | 170 ----- examples/clang_asm_normalize/as | 75 --- examples/crash_triage/triage_crashes.sh | 115 ---- examples/custom_mutators/Makefile | 7 - examples/custom_mutators/README.md | 35 - examples/custom_mutators/XmlMutatorMin.py | 332 ---------- examples/custom_mutators/common.py | 40 -- examples/custom_mutators/custom_mutator_helpers.h | 342 ---------- examples/custom_mutators/example.c | 376 ----------- examples/custom_mutators/example.py | 186 ------ examples/custom_mutators/post_library_gif.so.c | 165 ----- examples/custom_mutators/post_library_png.so.c | 163 ----- examples/custom_mutators/simple-chunk-replace.py | 64 -- examples/custom_mutators/simple_example.c | 74 --- examples/custom_mutators/wrapper_afl_min.py | 118 ---- examples/defork/Makefile | 64 -- examples/defork/README.md | 11 - examples/defork/defork.c | 50 -- examples/defork/forking_target.c | 49 -- examples/distributed_fuzzing/sync_script.sh | 97 --- examples/libpng_no_checksum/libpng-nocrc.patch | 15 - examples/persistent_mode/Makefile | 10 - examples/persistent_mode/persistent_demo.c | 112 ---- examples/persistent_mode/persistent_demo_new.c | 117 ---- examples/persistent_mode/test-instr.c | 69 -- examples/qemu_persistent_hook/Makefile | 6 - examples/qemu_persistent_hook/README.md | 19 - examples/qemu_persistent_hook/read_into_rdi.c | 34 - examples/qemu_persistent_hook/test.c | 35 - examples/socket_fuzzing/Makefile | 61 -- examples/socket_fuzzing/README.md | 11 - examples/socket_fuzzing/socketfuzz.c | 110 ---- instrumentation/README.gcc_plugin.md | 2 +- instrumentation/README.persistent_mode.md | 4 +- qemu_mode/README.md | 2 +- qemu_mode/README.persistent.md | 2 +- src/afl-as.c | 2 +- test/test-custom-mutators.sh | 8 +- test/test-gcc-plugin.sh | 2 +- test/test-llvm-lto.sh | 2 +- test/test-llvm.sh | 2 +- utils/README.md | 54 ++ utils/afl_frida/GNUmakefile | 23 + utils/afl_frida/Makefile | 2 + utils/afl_frida/README.md | 34 + utils/afl_frida/afl-frida.c | 542 +++++++++++++++ utils/afl_frida/afl-frida.h | 53 ++ utils/afl_frida/libtestinstr.c | 35 + utils/afl_network_proxy/GNUmakefile | 43 ++ utils/afl_network_proxy/Makefile | 2 + utils/afl_network_proxy/README.md | 61 ++ utils/afl_network_proxy/afl-network-client.c | 415 ++++++++++++ utils/afl_network_proxy/afl-network-server.c | 720 ++++++++++++++++++++ utils/afl_proxy/Makefile | 7 + utils/afl_proxy/README.md | 9 + utils/afl_proxy/afl-proxy.c | 238 +++++++ utils/afl_untracer/Makefile | 16 + utils/afl_untracer/README.md | 60 ++ utils/afl_untracer/TODO | 2 + utils/afl_untracer/afl-untracer.c | 768 ++++++++++++++++++++++ utils/afl_untracer/ghidra_get_patchpoints.java | 84 +++ utils/afl_untracer/ida_get_patchpoints.py | 62 ++ utils/afl_untracer/libtestinstr.c | 35 + utils/afl_untracer/patches.txt | 34 + utils/aflpp_driver/GNUmakefile | 46 ++ utils/aflpp_driver/Makefile | 2 + utils/aflpp_driver/aflpp_driver.c | 326 +++++++++ utils/aflpp_driver/aflpp_driver_test.c | 32 + utils/aflpp_driver/aflpp_qemu_driver.c | 38 ++ utils/aflpp_driver/aflpp_qemu_driver_hook.c | 22 + utils/analysis_scripts/queue2csv.sh | 122 ++++ utils/argv_fuzzing/Makefile | 58 ++ utils/argv_fuzzing/README.md | 16 + utils/argv_fuzzing/argv-fuzz-inl.h | 90 +++ utils/argv_fuzzing/argvfuzz.c | 49 ++ utils/asan_cgroups/limit_memory.sh | 157 +++++ utils/bash_shellshock/shellshock-fuzz.diff | 59 ++ utils/canvas_harness/canvas_harness.html | 170 +++++ utils/clang_asm_normalize/as | 75 +++ utils/crash_triage/triage_crashes.sh | 115 ++++ utils/custom_mutators/Makefile | 7 + utils/custom_mutators/README.md | 35 + utils/custom_mutators/XmlMutatorMin.py | 332 ++++++++++ utils/custom_mutators/common.py | 40 ++ utils/custom_mutators/custom_mutator_helpers.h | 342 ++++++++++ utils/custom_mutators/example.c | 376 +++++++++++ utils/custom_mutators/example.py | 186 ++++++ utils/custom_mutators/post_library_gif.so.c | 165 +++++ utils/custom_mutators/post_library_png.so.c | 163 +++++ utils/custom_mutators/simple-chunk-replace.py | 64 ++ utils/custom_mutators/simple_example.c | 74 +++ utils/custom_mutators/wrapper_afl_min.py | 118 ++++ utils/defork/Makefile | 64 ++ utils/defork/README.md | 11 + utils/defork/defork.c | 50 ++ utils/defork/forking_target.c | 49 ++ utils/distributed_fuzzing/sync_script.sh | 97 +++ utils/libpng_no_checksum/libpng-nocrc.patch | 15 + utils/persistent_mode/Makefile | 10 + utils/persistent_mode/persistent_demo.c | 112 ++++ utils/persistent_mode/persistent_demo_new.c | 117 ++++ utils/persistent_mode/test-instr.c | 69 ++ utils/qemu_persistent_hook/Makefile | 6 + utils/qemu_persistent_hook/README.md | 19 + utils/qemu_persistent_hook/read_into_rdi.c | 34 + utils/qemu_persistent_hook/test.c | 35 + utils/socket_fuzzing/Makefile | 61 ++ utils/socket_fuzzing/README.md | 11 + utils/socket_fuzzing/socketfuzz.c | 110 ++++ 156 files changed, 7496 insertions(+), 7494 deletions(-) delete mode 100644 examples/README.md delete mode 100644 examples/afl_frida/GNUmakefile delete mode 100644 examples/afl_frida/Makefile delete mode 100644 examples/afl_frida/README.md delete mode 100644 examples/afl_frida/afl-frida.c delete mode 100644 examples/afl_frida/afl-frida.h delete mode 100644 examples/afl_frida/libtestinstr.c delete mode 100644 examples/afl_network_proxy/GNUmakefile delete mode 100644 examples/afl_network_proxy/Makefile delete mode 100644 examples/afl_network_proxy/README.md delete mode 100644 examples/afl_network_proxy/afl-network-client.c delete mode 100644 examples/afl_network_proxy/afl-network-server.c delete mode 100644 examples/afl_proxy/Makefile delete mode 100644 examples/afl_proxy/README.md delete mode 100644 examples/afl_proxy/afl-proxy.c delete mode 100644 examples/afl_untracer/Makefile delete mode 100644 examples/afl_untracer/README.md delete mode 100644 examples/afl_untracer/TODO delete mode 100644 examples/afl_untracer/afl-untracer.c delete mode 100644 examples/afl_untracer/ghidra_get_patchpoints.java delete mode 100644 examples/afl_untracer/ida_get_patchpoints.py delete mode 100644 examples/afl_untracer/libtestinstr.c delete mode 100755 examples/afl_untracer/libtestinstr.so delete mode 100644 examples/afl_untracer/patches.txt delete mode 100644 examples/aflpp_driver/GNUmakefile delete mode 100644 examples/aflpp_driver/Makefile delete mode 100644 examples/aflpp_driver/aflpp_driver.c delete mode 100644 examples/aflpp_driver/aflpp_driver_test.c delete mode 100644 examples/aflpp_driver/aflpp_qemu_driver.c delete mode 100644 examples/aflpp_driver/aflpp_qemu_driver_hook.c delete mode 100755 examples/analysis_scripts/queue2csv.sh delete mode 100644 examples/argv_fuzzing/Makefile delete mode 100644 examples/argv_fuzzing/README.md delete mode 100644 examples/argv_fuzzing/argv-fuzz-inl.h delete mode 100644 examples/argv_fuzzing/argvfuzz.c delete mode 100755 examples/asan_cgroups/limit_memory.sh delete mode 100644 examples/bash_shellshock/shellshock-fuzz.diff delete mode 100644 examples/canvas_harness/canvas_harness.html delete mode 100755 examples/clang_asm_normalize/as delete mode 100755 examples/crash_triage/triage_crashes.sh delete mode 100644 examples/custom_mutators/Makefile delete mode 100644 examples/custom_mutators/README.md delete mode 100644 examples/custom_mutators/XmlMutatorMin.py delete mode 100644 examples/custom_mutators/common.py delete mode 100644 examples/custom_mutators/custom_mutator_helpers.h delete mode 100644 examples/custom_mutators/example.c delete mode 100644 examples/custom_mutators/example.py delete mode 100644 examples/custom_mutators/post_library_gif.so.c delete mode 100644 examples/custom_mutators/post_library_png.so.c delete mode 100644 examples/custom_mutators/simple-chunk-replace.py delete mode 100644 examples/custom_mutators/simple_example.c delete mode 100644 examples/custom_mutators/wrapper_afl_min.py delete mode 100644 examples/defork/Makefile delete mode 100644 examples/defork/README.md delete mode 100644 examples/defork/defork.c delete mode 100644 examples/defork/forking_target.c delete mode 100755 examples/distributed_fuzzing/sync_script.sh delete mode 100644 examples/libpng_no_checksum/libpng-nocrc.patch delete mode 100644 examples/persistent_mode/Makefile delete mode 100644 examples/persistent_mode/persistent_demo.c delete mode 100644 examples/persistent_mode/persistent_demo_new.c delete mode 100644 examples/persistent_mode/test-instr.c delete mode 100644 examples/qemu_persistent_hook/Makefile delete mode 100644 examples/qemu_persistent_hook/README.md delete mode 100644 examples/qemu_persistent_hook/read_into_rdi.c delete mode 100644 examples/qemu_persistent_hook/test.c delete mode 100644 examples/socket_fuzzing/Makefile delete mode 100644 examples/socket_fuzzing/README.md delete mode 100644 examples/socket_fuzzing/socketfuzz.c create mode 100644 utils/README.md create mode 100644 utils/afl_frida/GNUmakefile create mode 100644 utils/afl_frida/Makefile create mode 100644 utils/afl_frida/README.md create mode 100644 utils/afl_frida/afl-frida.c create mode 100644 utils/afl_frida/afl-frida.h create mode 100644 utils/afl_frida/libtestinstr.c create mode 100644 utils/afl_network_proxy/GNUmakefile create mode 100644 utils/afl_network_proxy/Makefile create mode 100644 utils/afl_network_proxy/README.md create mode 100644 utils/afl_network_proxy/afl-network-client.c create mode 100644 utils/afl_network_proxy/afl-network-server.c create mode 100644 utils/afl_proxy/Makefile create mode 100644 utils/afl_proxy/README.md create mode 100644 utils/afl_proxy/afl-proxy.c create mode 100644 utils/afl_untracer/Makefile create mode 100644 utils/afl_untracer/README.md create mode 100644 utils/afl_untracer/TODO create mode 100644 utils/afl_untracer/afl-untracer.c create mode 100644 utils/afl_untracer/ghidra_get_patchpoints.java create mode 100644 utils/afl_untracer/ida_get_patchpoints.py create mode 100644 utils/afl_untracer/libtestinstr.c create mode 100644 utils/afl_untracer/patches.txt create mode 100644 utils/aflpp_driver/GNUmakefile create mode 100644 utils/aflpp_driver/Makefile create mode 100644 utils/aflpp_driver/aflpp_driver.c create mode 100644 utils/aflpp_driver/aflpp_driver_test.c create mode 100644 utils/aflpp_driver/aflpp_qemu_driver.c create mode 100644 utils/aflpp_driver/aflpp_qemu_driver_hook.c create mode 100755 utils/analysis_scripts/queue2csv.sh create mode 100644 utils/argv_fuzzing/Makefile create mode 100644 utils/argv_fuzzing/README.md create mode 100644 utils/argv_fuzzing/argv-fuzz-inl.h create mode 100644 utils/argv_fuzzing/argvfuzz.c create mode 100755 utils/asan_cgroups/limit_memory.sh create mode 100644 utils/bash_shellshock/shellshock-fuzz.diff create mode 100644 utils/canvas_harness/canvas_harness.html create mode 100755 utils/clang_asm_normalize/as create mode 100755 utils/crash_triage/triage_crashes.sh create mode 100644 utils/custom_mutators/Makefile create mode 100644 utils/custom_mutators/README.md create mode 100644 utils/custom_mutators/XmlMutatorMin.py create mode 100644 utils/custom_mutators/common.py create mode 100644 utils/custom_mutators/custom_mutator_helpers.h create mode 100644 utils/custom_mutators/example.c create mode 100644 utils/custom_mutators/example.py create mode 100644 utils/custom_mutators/post_library_gif.so.c create mode 100644 utils/custom_mutators/post_library_png.so.c create mode 100644 utils/custom_mutators/simple-chunk-replace.py create mode 100644 utils/custom_mutators/simple_example.c create mode 100644 utils/custom_mutators/wrapper_afl_min.py create mode 100644 utils/defork/Makefile create mode 100644 utils/defork/README.md create mode 100644 utils/defork/defork.c create mode 100644 utils/defork/forking_target.c create mode 100755 utils/distributed_fuzzing/sync_script.sh create mode 100644 utils/libpng_no_checksum/libpng-nocrc.patch create mode 100644 utils/persistent_mode/Makefile create mode 100644 utils/persistent_mode/persistent_demo.c create mode 100644 utils/persistent_mode/persistent_demo_new.c create mode 100644 utils/persistent_mode/test-instr.c create mode 100644 utils/qemu_persistent_hook/Makefile create mode 100644 utils/qemu_persistent_hook/README.md create mode 100644 utils/qemu_persistent_hook/read_into_rdi.c create mode 100644 utils/qemu_persistent_hook/test.c create mode 100644 utils/socket_fuzzing/Makefile create mode 100644 utils/socket_fuzzing/README.md create mode 100644 utils/socket_fuzzing/socketfuzz.c (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index 521ab683..309a7d4c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -500,8 +500,8 @@ code-format: ./.custom-format.py -i instrumentation/*.c @#./.custom-format.py -i custom_mutators/*/*.c* # destroys libfuzzer :-( @#./.custom-format.py -i custom_mutators/*/*.h # destroys honggfuzz :-( - ./.custom-format.py -i examples/*/*.c* - ./.custom-format.py -i examples/*/*.h + ./.custom-format.py -i utils/*/*.c* + ./.custom-format.py -i utils/*/*.h ./.custom-format.py -i test/*.c ./.custom-format.py -i qemu_mode/libcompcov/*.c ./.custom-format.py -i qemu_mode/libcompcov/*.cc @@ -547,9 +547,9 @@ clean: -$(MAKE) -f GNUmakefile.gcc_plugin clean $(MAKE) -C libdislocator clean $(MAKE) -C libtokencap clean - $(MAKE) -C examples/afl_network_proxy clean - $(MAKE) -C examples/socket_fuzzing clean - $(MAKE) -C examples/argv_fuzzing clean + $(MAKE) -C utils/afl_network_proxy clean + $(MAKE) -C utils/socket_fuzzing clean + $(MAKE) -C utils/argv_fuzzing clean $(MAKE) -C qemu_mode/unsigaction clean $(MAKE) -C qemu_mode/libcompcov clean ifeq "$(IN_REPO)" "1" @@ -572,10 +572,10 @@ distrib: all -$(MAKE) -f GNUmakefile.gcc_plugin $(MAKE) -C libdislocator $(MAKE) -C libtokencap - $(MAKE) -C examples/aflpp_driver - $(MAKE) -C examples/afl_network_proxy - $(MAKE) -C examples/socket_fuzzing - $(MAKE) -C examples/argv_fuzzing + $(MAKE) -C utils/aflpp_driver + $(MAKE) -C utils/afl_network_proxy + $(MAKE) -C utils/socket_fuzzing + $(MAKE) -C utils/argv_fuzzing -cd qemu_mode && sh ./build_qemu_support.sh -cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh @@ -583,9 +583,9 @@ distrib: all binary-only: all $(MAKE) -C libdislocator $(MAKE) -C libtokencap - $(MAKE) -C examples/afl_network_proxy - $(MAKE) -C examples/socket_fuzzing - $(MAKE) -C examples/argv_fuzzing + $(MAKE) -C utils/afl_network_proxy + $(MAKE) -C utils/socket_fuzzing + $(MAKE) -C utils/argv_fuzzing -cd qemu_mode && sh ./build_qemu_support.sh -cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh @@ -595,7 +595,7 @@ source-only: all -$(MAKE) -f GNUmakefile.gcc_plugin $(MAKE) -C libdislocator $(MAKE) -C libtokencap - $(MAKE) -C examples/aflpp_driver + $(MAKE) -C utils/aflpp_driver %.8: % @echo .TH $* 8 $(BUILD_DATE) "afl++" > $@ @@ -628,11 +628,11 @@ install: all $(MANPAGES) @if [ -f libtokencap.so ]; then set -e; install -m 755 libtokencap.so $${DESTDIR}$(HELPER_PATH); fi @if [ -f libcompcov.so ]; then set -e; install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH); fi @if [ -f afl-fuzz-document ]; then set -e; install -m 755 afl-fuzz-document $${DESTDIR}$(BIN_PATH); fi - @if [ -f socketfuzz32.so -o -f socketfuzz64.so ]; then $(MAKE) -C examples/socket_fuzzing install; fi - @if [ -f argvfuzz32.so -o -f argvfuzz64.so ]; then $(MAKE) -C examples/argv_fuzzing install; fi - @if [ -f examples/afl_network_proxy/afl-network-server ]; then $(MAKE) -C examples/afl_network_proxy install; fi - @if [ -f examples/aflpp_driver/libAFLDriver.a ]; then set -e; install -m 644 examples/aflpp_driver/libAFLDriver.a $${DESTDIR}$(HELPER_PATH); fi - @if [ -f examples/aflpp_driver/libAFLQemuDriver.a ]; then set -e; install -m 644 examples/aflpp_driver/libAFLQemuDriver.a $${DESTDIR}$(HELPER_PATH); fi + @if [ -f socketfuzz32.so -o -f socketfuzz64.so ]; then $(MAKE) -C utils/socket_fuzzing install; fi + @if [ -f argvfuzz32.so -o -f argvfuzz64.so ]; then $(MAKE) -C utils/argv_fuzzing install; fi + @if [ -f utils/afl_network_proxy/afl-network-server ]; then $(MAKE) -C utils/afl_network_proxy install; fi + @if [ -f utils/aflpp_driver/libAFLDriver.a ]; then set -e; install -m 644 utils/aflpp_driver/libAFLDriver.a $${DESTDIR}$(HELPER_PATH); fi + @if [ -f utils/aflpp_driver/libAFLQemuDriver.a ]; then set -e; install -m 644 utils/aflpp_driver/libAFLQemuDriver.a $${DESTDIR}$(HELPER_PATH); fi -$(MAKE) -f GNUmakefile.llvm install -$(MAKE) -f GNUmakefile.gcc_plugin install ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-gcc diff --git a/README.md b/README.md index d7cad092..b00e5d00 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ behaviours and defaults: * a caching of testcases can now be performed and can be modified by editing config.h for TESTCASE_CACHE or by specifying the env variable `AFL_TESTCACHE_SIZE` (in MB). Good values are between 50-500 (default: 50). + * utils/ got renamed to utils/ ## Contents @@ -760,10 +761,10 @@ cd unicorn_mode If the goal is to fuzz a dynamic library then there are two options available. For both you need to write a small hardness that loads and calls the library. -Faster is the frida solution: [examples/afl_frida/README.md](examples/afl_frida/README.md) +Faster is the frida solution: [utils/afl_frida/README.md](utils/afl_frida/README.md) Another, less precise and slower option is using ptrace with debugger interrupt -instrumentation: [examples/afl_untracer/README.md](examples/afl_untracer/README.md) +instrumentation: [utils/afl_untracer/README.md](utils/afl_untracer/README.md) ### More @@ -1037,7 +1038,7 @@ Here are some of the most important caveats for AFL: wholly wrap the actual data format to be tested. To work around this, you can comment out the relevant checks (see - examples/libpng_no_checksum/ for inspiration); if this is not possible, + utils/libpng_no_checksum/ for inspiration); if this is not possible, you can also write a postprocessor, one of the hooks of custom mutators. See [docs/custom_mutators.md](docs/custom_mutators.md) on how to use `AFL_CUSTOM_MUTATOR_LIBRARY` diff --git a/docs/Changelog.md b/docs/Changelog.md index 7fa7ff53..fd30c7b0 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -11,6 +11,7 @@ sending a mail to . ### Version ++3.00a (develop) - llvm_mode/ and gcc_plugin/ moved to instrumentation/ + - examples/ renamed to utils/ - all compilers combined to afl-cc which emulates the previous ones - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o - afl-fuzz diff --git a/docs/FAQ.md b/docs/FAQ.md index 064638f4..714d50eb 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -63,7 +63,7 @@ x10 - that is a x100 difference!). If modifying the source is not an option (e.g. because you only have a binary and perform binary fuzzing) you can also use a shared library with AFL_PRELOAD to emulate the network. This is also much faster than the real network would be. -See [examples/socket_fuzzing/](../examples/socket_fuzzing/). +See [utils/socket_fuzzing/](../utils/socket_fuzzing/). There is an outdated afl++ branch that implements networking if you are desperate though: [https://github.com/AFLplusplus/AFLplusplus/tree/networking](https://github.com/AFLplusplus/AFLplusplus/tree/networking) - diff --git a/docs/binaryonly_fuzzing.md b/docs/binaryonly_fuzzing.md index cb1288ef..66734452 100644 --- a/docs/binaryonly_fuzzing.md +++ b/docs/binaryonly_fuzzing.md @@ -15,7 +15,7 @@ high enough. Otherwise try retrowrite, afl-dyninst and if these fail too then try standard qemu_mode with AFL_ENTRYPOINT to where you need it. - If your target is a library use examples/afl_frida/. + If your target is a library use utils/afl_frida/. If your target is non-linux then use unicorn_mode/. @@ -65,14 +65,14 @@ ## AFL FRIDA If you want to fuzz a binary-only shared library then you can fuzz it with - frida-gum via examples/afl_frida/, you will have to write a harness to + frida-gum via utils/afl_frida/, you will have to write a harness to call the target function in the library, use afl-frida.c as a template. ## AFL UNTRACER If you want to fuzz a binary-only shared library then you can fuzz it with - examples/afl_untracer/, use afl-untracer.c as a template. + utils/afl_untracer/, use afl-untracer.c as a template. It is slower than AFL FRIDA (see above). diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index 53f783fe..6e16ba0f 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -268,8 +268,8 @@ afl-fuzz /path/to/program ## 4) Example -Please see [example.c](../examples/custom_mutators/example.c) and -[example.py](../examples/custom_mutators/example.py) +Please see [example.c](../utils/custom_mutators/example.c) and +[example.py](../utils/custom_mutators/example.py) ## 5) Other Resources diff --git a/docs/env_variables.md b/docs/env_variables.md index f7b4c994..ada89257 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -55,7 +55,7 @@ make fairly broad use of environmental variables instead: in your `$PATH`. - `AFL_PATH` can be used to point afl-gcc to an alternate location of afl-as. - One possible use of this is examples/clang_asm_normalize/, which lets + One possible use of this is utils/clang_asm_normalize/, which lets you instrument hand-written assembly when compiling clang code by plugging a normalizer into the chain. (There is no equivalent feature for GCC.) diff --git a/docs/life_pro_tips.md b/docs/life_pro_tips.md index 323f16f1..77845c63 100644 --- a/docs/life_pro_tips.md +++ b/docs/life_pro_tips.md @@ -78,10 +78,10 @@ Be sure to check out docs/sister_projects.md before writing your own. ## Need to fuzz the command-line arguments of a particular program? -You can find a simple solution in examples/argv_fuzzing. +You can find a simple solution in utils/argv_fuzzing. ## Attacking a format that uses checksums? Remove the checksum-checking code or use a postprocessor! -See examples/custom_mutators/ for more. +See utils/custom_mutators/ for more. diff --git a/docs/notes_for_asan.md b/docs/notes_for_asan.md index 2e18c15f..f08ae3fb 100644 --- a/docs/notes_for_asan.md +++ b/docs/notes_for_asan.md @@ -20,7 +20,7 @@ Because of this, fuzzing with ASAN is recommended only in four scenarios: - Precisely gauge memory needs using http://jwilk.net/software/recidivm . - Limit the memory available to process using cgroups on Linux (see - examples/asan_cgroups). + utils/asan_cgroups). To compile with ASAN, set AFL_USE_ASAN=1 before calling 'make clean all'. The afl-gcc / afl-clang wrappers will pick that up and add the appropriate flags. @@ -74,7 +74,7 @@ There are also cgroups, but they are Linux-specific, not universally available even on Linux systems, and they require root permissions to set up; I'm a bit hesitant to make afl-fuzz require root permissions just for that. That said, if you are on Linux and want to use cgroups, check out the contributed script -that ships in examples/asan_cgroups/. +that ships in utils/asan_cgroups/. In settings where cgroups aren't available, we have no nice, portable way to avoid counting the ASAN allocation toward the limit. On 32-bit systems, or for diff --git a/docs/parallel_fuzzing.md b/docs/parallel_fuzzing.md index bf57ace8..8f2afe1b 100644 --- a/docs/parallel_fuzzing.md +++ b/docs/parallel_fuzzing.md @@ -152,7 +152,7 @@ write a simple script that performs two actions: done ``` -There is an example of such a script in examples/distributed_fuzzing/. +There is an example of such a script in utils/distributed_fuzzing/. There are other (older) more featured, experimental tools: * https://github.com/richo/roving diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 7dd70d6a..00000000 --- a/examples/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# AFL++ Examples - -Here's a quick overview of the stuff you can find in this directory: - - - afl_network_proxy - fuzz a target over the network: afl-fuzz on - a host, target on an embedded system. - - - afl_proxy - skeleton file example to show how to fuzz - something where you gather coverage data via - different means, e.g. hw debugger - - - afl_untracer - fuzz binary-only libraries much faster but with - less coverage than qemu_mode - - - argv_fuzzing - a simple wrapper to allow cmdline to be fuzzed - (e.g., to test setuid programs). - - - asan_cgroups - a contributed script to simplify fuzzing ASAN - binaries with robust memory limits on Linux. - - - bash_shellshock - a simple hack used to find a bunch of - post-Shellshock bugs in bash. - - - canvas_harness - a test harness used to find browser bugs with a - corpus generated using simple image parsing - binaries & afl-fuzz. - - - clang_asm_normalize - a script that makes it easy to instrument - hand-written assembly, provided that you have clang. - - - crash_triage - a very rudimentary example of how to annotate crashes - with additional gdb metadata. - - - custom_mutators - examples for the afl++ custom mutator interface in - C and Python - - - distributed_fuzzing - a sample script for synchronizing fuzzer instances - across multiple machines (see parallel_fuzzing.md). - - - libpng_no_checksum - a sample patch for removing CRC checks in libpng. - - - persistent_mode - an example of how to use the LLVM persistent process - mode to speed up certain fuzzing jobs. - - - socket_fuzzing - a LD_PRELOAD library 'redirects' a socket to stdin - for fuzzing access with afl++ - -Note that the minimize_corpus.sh tool has graduated from the examples/ -directory and is now available as ../afl-cmin. The LLVM mode has likewise -graduated to ../instrumentation/*. - -Most of the tools in this directory are meant chiefly as examples that need to -be tweaked for your specific needs. They come with some basic documentation, -but are not necessarily production-grade. diff --git a/examples/afl_frida/GNUmakefile b/examples/afl_frida/GNUmakefile deleted file mode 100644 index c154f3a4..00000000 --- a/examples/afl_frida/GNUmakefile +++ /dev/null @@ -1,23 +0,0 @@ -ifdef DEBUG - OPT=-O0 -D_DEBUG=\"1\" -else - OPT=-O3 -funroll-loops -endif - -all: afl-frida libtestinstr.so - -libfrida-gum.a: - @echo Download and extract frida-gum-devkit-VERSION-PLATFORM.tar.xz for your platform from https://github.com/frida/frida/releases/latest - @exit 1 - -afl-frida: afl-frida.c libfrida-gum.a - $(CC) -g $(OPT) -o afl-frida -Wno-format -Wno-pointer-sign -I. -fpermissive -fPIC afl-frida.c ../../afl-llvm-rt.o libfrida-gum.a -ldl -lresolv -pthread - -libtestinstr.so: libtestinstr.c - $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c - -clean: - rm -f afl-frida *~ core *.o libtestinstr.so - -deepclean: clean - rm -f libfrida-gum.a frida-gum* diff --git a/examples/afl_frida/Makefile b/examples/afl_frida/Makefile deleted file mode 100644 index 0b306dde..00000000 --- a/examples/afl_frida/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - @echo please use GNU make, thanks! diff --git a/examples/afl_frida/README.md b/examples/afl_frida/README.md deleted file mode 100644 index 7743479b..00000000 --- a/examples/afl_frida/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# afl-frida - faster fuzzing of binary-only libraries - -## Introduction - -afl-frida is an example skeleton file which can easily be used to fuzz -a closed source library. - -It requires less memory and is x5-10 faster than qemu_mode but does not -provide interesting features like compcov or cmplog. - -## How-to - -### Modify afl-frida.c - -Read and modify afl-frida.c then `make`. -To adapt afl-frida.c to your needs, read the header of the file and then -search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations. - -### Fuzzing - -Example (after modifying afl-frida.c to your needs and compile it): -``` -LD_LIBRARY_PATH=/path/to/the/target/library afl-fuzz -i in -o out -- ./afl-frida -``` -(or even remote via afl-network-proxy). - -# Speed and stability - -The speed is very good, about x12 of fork() qemu_mode. -However the stability is low. Reason is currently unknown. - -# Background - -This code is copied for a larger part from https://github.com/meme/hotwax diff --git a/examples/afl_frida/afl-frida.c b/examples/afl_frida/afl-frida.c deleted file mode 100644 index 31bf8f25..00000000 --- a/examples/afl_frida/afl-frida.c +++ /dev/null @@ -1,542 +0,0 @@ -/* - american fuzzy lop++ - afl-frida skeleton example - ------------------------------------------------- - - Copyright 2020 AFLplusplus Project. All rights reserved. - - Written mostly by meme -> https://github.com/meme/hotwax - - Modifications by Marc Heuse - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - HOW-TO - ====== - - You only need to change the following: - - 1. set the defines and function call parameters. - 2. dl load the library you want to fuzz, lookup the functions you need - and setup the calls to these. - 3. in the while loop you call the functions in the necessary order - - incl the cleanup. the cleanup is important! - - Just look these steps up in the code, look for "// STEP x:" - -*/ - -#include -#include -#include -#include -#include -#include -#include - -#ifndef __APPLE__ - #include - #include -#endif - -int debug = 0; - -// STEP 1: - -// The presets are for the example libtestinstr.so: - -/* What is the name of the library to fuzz */ -#define TARGET_LIBRARY "libtestinstr.so" - -/* What is the name of the function to fuzz */ -#define TARGET_FUNCTION "testinstr" - -/* here you need to specify the parameter for the target function */ -static void *(*o_function)(uint8_t *, int); - -// END STEP 1 - -#include "frida-gum.h" - -G_BEGIN_DECLS - -#define GUM_TYPE_FAKE_EVENT_SINK (gum_fake_event_sink_get_type()) -G_DECLARE_FINAL_TYPE(GumFakeEventSink, gum_fake_event_sink, GUM, - FAKE_EVENT_SINK, GObject) - -struct _GumFakeEventSink { - - GObject parent; - GumEventType mask; - -}; - -GumEventSink *gum_fake_event_sink_new(void); -void gum_fake_event_sink_reset(GumFakeEventSink *self); - -G_END_DECLS - -static void gum_fake_event_sink_iface_init(gpointer g_iface, - gpointer iface_data); -static void gum_fake_event_sink_finalize(GObject *obj); -static GumEventType gum_fake_event_sink_query_mask(GumEventSink *sink); -static void gum_fake_event_sink_process(GumEventSink *sink, const GumEvent *ev); -void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, - gpointer user_data); -void afl_setup(void); -void afl_start_forkserver(void); -int __afl_persistent_loop(unsigned int max_cnt); - -static void gum_fake_event_sink_class_init(GumFakeEventSinkClass *klass) { - - GObjectClass *object_class = G_OBJECT_CLASS(klass); - object_class->finalize = gum_fake_event_sink_finalize; - -} - -static void gum_fake_event_sink_iface_init(gpointer g_iface, - gpointer iface_data) { - - GumEventSinkInterface *iface = (GumEventSinkInterface *)g_iface; - iface->query_mask = gum_fake_event_sink_query_mask; - iface->process = gum_fake_event_sink_process; - -} - -G_DEFINE_TYPE_EXTENDED(GumFakeEventSink, gum_fake_event_sink, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE(GUM_TYPE_EVENT_SINK, - gum_fake_event_sink_iface_init)) - -#include "../../config.h" - -// Shared memory fuzzing. -int __afl_sharedmem_fuzzing = 1; -extern unsigned int * __afl_fuzz_len; -extern unsigned char *__afl_fuzz_ptr; - -// Notify AFL about persistent mode. -static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; -int __afl_persistent_loop(unsigned int); - -// Notify AFL about deferred forkserver. -static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; -void __afl_manual_init(); - -// Because we do our own logging. -extern uint8_t * __afl_area_ptr; -static __thread guint64 previous_pc; - -// Frida stuff below. -typedef struct { - - GumAddress base_address; - guint64 code_start, code_end; - -} range_t; - -inline static void afl_maybe_log(guint64 current_pc) { - - // fprintf(stderr, "PC: %p ^ %p\n", current_pc, previous_pc); - - current_pc = (current_pc >> 4) ^ (current_pc << 8); - current_pc &= MAP_SIZE - 1; - - __afl_area_ptr[current_pc ^ previous_pc]++; - previous_pc = current_pc >> 1; - -} - -static void on_basic_block(GumCpuContext *context, gpointer user_data) { - - afl_maybe_log((guint64)user_data); - -} - -void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, - gpointer user_data) { - - range_t *range = (range_t *)user_data; - - const cs_insn *instr; - gboolean begin = TRUE; - while (gum_stalker_iterator_next(iterator, &instr)) { - - if (begin) { - - if (instr->address >= range->code_start && - instr->address <= range->code_end) { - - gum_stalker_iterator_put_callout(iterator, on_basic_block, - (gpointer)instr->address, NULL); - begin = FALSE; - - } - - } - - gum_stalker_iterator_keep(iterator); - - } - -} - -static void gum_fake_event_sink_init(GumFakeEventSink *self) { - -} - -static void gum_fake_event_sink_finalize(GObject *obj) { - - G_OBJECT_CLASS(gum_fake_event_sink_parent_class)->finalize(obj); - -} - -GumEventSink *gum_fake_event_sink_new(void) { - - GumFakeEventSink *sink; - sink = (GumFakeEventSink *)g_object_new(GUM_TYPE_FAKE_EVENT_SINK, NULL); - return GUM_EVENT_SINK(sink); - -} - -void gum_fake_event_sink_reset(GumFakeEventSink *self) { - -} - -static GumEventType gum_fake_event_sink_query_mask(GumEventSink *sink) { - - return 0; - -} - -typedef struct library_list { - - uint8_t *name; - uint64_t addr_start, addr_end; - -} library_list_t; - -#define MAX_LIB_COUNT 256 -static library_list_t liblist[MAX_LIB_COUNT]; -static u32 liblist_cnt; - -void read_library_information() { - -#if defined(__linux__) - FILE *f; - u8 buf[1024], *b, *m, *e, *n; - - if ((f = fopen("/proc/self/maps", "r")) == NULL) { - - fprintf(stderr, "Error: cannot open /proc/self/maps\n"); - exit(-1); - - } - - if (debug) fprintf(stderr, "Library list:\n"); - while (fgets(buf, sizeof(buf), f)) { - - if (strstr(buf, " r-x")) { - - if (liblist_cnt >= MAX_LIB_COUNT) { - - fprintf( - stderr, - "Warning: too many libraries to old, maximum count of %d reached\n", - liblist_cnt); - return; - - } - - b = buf; - m = index(buf, '-'); - e = index(buf, ' '); - if ((n = rindex(buf, '/')) == NULL) n = rindex(buf, ' '); - if (n && - ((*n >= '0' && *n <= '9') || *n == '[' || *n == '{' || *n == '(')) - n = NULL; - else - n++; - if (b && m && e && n && *n) { - - *m++ = 0; - *e = 0; - if (n[strlen(n) - 1] == '\n') n[strlen(n) - 1] = 0; - - if (rindex(n, '/') != NULL) { - - n = rindex(n, '/'); - n++; - - } - - liblist[liblist_cnt].name = strdup(n); - liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16); - liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16); - if (debug) - fprintf( - stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name, - liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_end - 1); - liblist_cnt++; - - } - - } - - } - - if (debug) fprintf(stderr, "\n"); - -#elif defined(__FreeBSD__) - int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; - char * buf, *start, *end; - size_t miblen = sizeof(mib) / sizeof(mib[0]); - size_t len; - - if (debug) fprintf(stderr, "Library list:\n"); - if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) { return; } - - len = len * 4 / 3; - - buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); - if (buf == MAP_FAILED) { return; } - if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) { - - munmap(buf, len); - return; - - } - - start = buf; - end = buf + len; - - while (start < end) { - - struct kinfo_vmentry *region = (struct kinfo_vmentry *)start; - size_t size = region->kve_structsize; - - if (size == 0) { break; } - - if ((region->kve_protection & KVME_PROT_READ) && - !(region->kve_protection & KVME_PROT_EXEC)) { - - liblist[liblist_cnt].name = - region->kve_path[0] != '\0' ? strdup(region->kve_path) : 0; - liblist[liblist_cnt].addr_start = region->kve_start; - liblist[liblist_cnt].addr_end = region->kve_end; - - if (debug) { - - fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name, - liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_end - 1); - - } - - liblist_cnt++; - - } - - start += size; - - } - -#endif - -} - -library_list_t *find_library(char *name) { - - char *filename = rindex(name, '/'); - - if (filename) - filename++; - else - filename = name; - -#if defined(__linux__) - u32 i; - for (i = 0; i < liblist_cnt; i++) - if (strcmp(liblist[i].name, filename) == 0) return &liblist[i]; -#elif defined(__APPLE__) && defined(__LP64__) - kern_return_t err; - static library_list_t lib; - - // get the list of all loaded modules from dyld - // the task_info mach API will get the address of the dyld all_image_info - // struct for the given task from which we can get the names and load - // addresses of all modules - task_dyld_info_data_t task_dyld_info; - mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; - err = task_info(mach_task_self(), TASK_DYLD_INFO, - (task_info_t)&task_dyld_info, &count); - - const struct dyld_all_image_infos *all_image_infos = - (const struct dyld_all_image_infos *)task_dyld_info.all_image_info_addr; - const struct dyld_image_info *image_infos = all_image_infos->infoArray; - - for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) { - - const char * image_name = image_infos[i].imageFilePath; - mach_vm_address_t image_load_address = - (mach_vm_address_t)image_infos[i].imageLoadAddress; - if (strstr(image_name, name)) { - - lib.name = name; - lib.addr_start = (u64)image_load_address; - lib.addr_end = 0; - return &lib; - - } - - } - -#endif - - return NULL; - -} - -static void gum_fake_event_sink_process(GumEventSink * sink, - const GumEvent *ev) { - -} - -/* Because this CAN be called more than once, it will return the LAST range */ -static int enumerate_ranges(const GumRangeDetails *details, - gpointer user_data) { - - GumMemoryRange *code_range = (GumMemoryRange *)user_data; - memcpy(code_range, details->range, sizeof(*code_range)); - return 0; - -} - -int main() { - -#ifndef __APPLE__ - (void)personality(ADDR_NO_RANDOMIZE); // disable ASLR -#endif - - // STEP 2: load the library you want to fuzz and lookup the functions, - // inclusive of the cleanup functions. - // If there is just one function, then there is nothing to change - // or add here. - - void *dl = dlopen(TARGET_LIBRARY, RTLD_LAZY); - if (!dl) { - - fprintf(stderr, "Could not load %s\n", TARGET_LIBRARY); - exit(-1); - - } - - if (!(o_function = dlsym(dl, TARGET_FUNCTION))) { - - fprintf(stderr, "Could not find function %s\n", TARGET_FUNCTION); - exit(-1); - - } - - // END STEP 2 - - read_library_information(); - library_list_t *lib = find_library(TARGET_LIBRARY); - - if (lib == NULL) { - - fprintf(stderr, "Could not find target library\n"); - exit(-1); - - } - - gum_init_embedded(); - if (!gum_stalker_is_supported()) { - - gum_deinit_embedded(); - return 1; - - } - - GumStalker *stalker = gum_stalker_new(); - - /* - This does not work here as we load a shared library. pretty sure this - would also be easily solvable with frida gum, but I already have all the - code I need from afl-untracer - - GumAddress base_address = gum_module_find_base_address(TARGET_LIBRARY); - GumMemoryRange code_range; - gum_module_enumerate_ranges(TARGET_LIBRARY, GUM_PAGE_RX, enumerate_ranges, - &code_range); - guint64 code_start = code_range.base_address - base_address; - guint64 code_end = (code_range.base_address + code_range.size) - base_address; - range_t instr_range = {base_address, code_start, code_end}; - */ - range_t instr_range = {0, lib->addr_start, lib->addr_end}; - - GumStalkerTransformer *transformer = - gum_stalker_transformer_make_from_callback(instr_basic_block, - &instr_range, NULL); - - GumEventSink *event_sink = gum_fake_event_sink_new(); - - // to ensure that the signatures are not optimized out - memcpy(__afl_area_ptr, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT) + 1); - memcpy(__afl_area_ptr + 32, (void *)AFL_DEFER_FORKSVR, - sizeof(AFL_DEFER_FORKSVR) + 1); - __afl_manual_init(); - - // - // any expensive target library initialization that has to be done just once - // - put that here - // - - gum_stalker_follow_me(stalker, transformer, event_sink); - - while (__afl_persistent_loop(UINT32_MAX) != 0) { - - previous_pc = 0; // Required! - -#ifdef _DEBUG - fprintf(stderr, "CLIENT crc: %016llx len: %u\n", - hash64(__afl_fuzz_ptr, *__afl_fuzz_len), *__afl_fuzz_len); - fprintf(stderr, "RECV:"); - for (int i = 0; i < *__afl_fuzz_len; i++) - fprintf(stderr, "%02x", __afl_fuzz_ptr[i]); - fprintf(stderr, "\n"); -#endif - - // STEP 3: ensure the minimum length is present and setup the target - // function to fuzz. - - if (*__afl_fuzz_len > 0) { - - __afl_fuzz_ptr[*__afl_fuzz_len] = 0; // if you need to null terminate - (*o_function)(__afl_fuzz_ptr, *__afl_fuzz_len); - - } - - // END STEP 3 - - } - - gum_stalker_unfollow_me(stalker); - - while (gum_stalker_garbage_collect(stalker)) - g_usleep(10000); - - g_object_unref(stalker); - g_object_unref(transformer); - g_object_unref(event_sink); - gum_deinit_embedded(); - - return 0; - -} - diff --git a/examples/afl_frida/afl-frida.h b/examples/afl_frida/afl-frida.h deleted file mode 100644 index efa3440f..00000000 --- a/examples/afl_frida/afl-frida.h +++ /dev/null @@ -1,53 +0,0 @@ -extern int is_persistent; - -G_BEGIN_DECLS - -#define GUM_TYPE_FAKE_EVENT_SINK (gum_fake_event_sink_get_type()) - -G_DECLARE_FINAL_TYPE(GumFakeEventSink, gum_fake_event_sink, GUM, - FAKE_EVENT_SINK, GObject) - -struct _GumFakeEventSink { - - GObject parent; - GumEventType mask; - -}; - -GumEventSink *gum_fake_event_sink_new(void); -void gum_fake_event_sink_reset(GumFakeEventSink *self); - -G_END_DECLS - -typedef struct { - - GumAddress base_address; - guint64 code_start, code_end; - -} range_t; - -void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, - gpointer user_data); -#pragma once - -void afl_setup(void); -void afl_start_forkserver(void); -int __afl_persistent_loop(unsigned int max_cnt); - -inline static inline void afl_maybe_log(guint64 current_pc) { - - extern unsigned int afl_instr_rms; - extern uint8_t * afl_area_ptr; - - static __thread guint64 previous_pc; - - current_pc = (current_pc >> 4) ^ (current_pc << 8); - current_pc &= MAP_SIZE - 1; - - if (current_pc >= afl_instr_rms) return; - - afl_area_ptr[current_pc ^ previous_pc]++; - previous_pc = current_pc >> 1; - -} - diff --git a/examples/afl_frida/libtestinstr.c b/examples/afl_frida/libtestinstr.c deleted file mode 100644 index 96b1cf21..00000000 --- a/examples/afl_frida/libtestinstr.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - american fuzzy lop++ - a trivial program to test the build - -------------------------------------------------------- - Originally written by Michal Zalewski - Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - http://www.apache.org/licenses/LICENSE-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include - -void testinstr(char *buf, int len) { - - if (len < 1) return; - buf[len] = 0; - - // we support three input cases - if (buf[0] == '0') - printf("Looks like a zero to me!\n"); - else if (buf[0] == '1') - printf("Pretty sure that is a one!\n"); - else - printf("Neither one or zero? How quaint!\n"); - -} - diff --git a/examples/afl_network_proxy/GNUmakefile b/examples/afl_network_proxy/GNUmakefile deleted file mode 100644 index 25a3df82..00000000 --- a/examples/afl_network_proxy/GNUmakefile +++ /dev/null @@ -1,43 +0,0 @@ -PREFIX ?= /usr/local -BIN_PATH = $(PREFIX)/bin -DOC_PATH = $(PREFIX)/share/doc/afl - -PROGRAMS = afl-network-client afl-network-server - -HASH=\# - -CFLAGS += -Wno-pointer-sign - -ifdef STATIC - CFLAGS += -static -endif - -ifeq "$(shell echo '$(HASH)include @int main() { struct libdeflate_compressor *d = libdeflate_alloc_compressor(1); return 0;}' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test2 -ldeflate 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" - CFLAGS += -DUSE_DEFLATE=1 - LDFLAGS += -ldeflate - $(info libdeflate-dev was detected, using compression) -else - $(warn did not find libdeflate-dev, cannot use compression) -endif - -all: $(PROGRAMS) - -help: - @echo make options: - @echo STATIC - build as static binaries - @echo COMPRESS_TESTCASES - compress test cases - -afl-network-client: afl-network-client.c - $(CC) $(CFLAGS) -I../../include -o afl-network-client afl-network-client.c $(LDFLAGS) - -afl-network-server: afl-network-server.c - $(CC) $(CFLAGS) -I../../include -o afl-network-server afl-network-server.c ../../src/afl-forkserver.c ../../src/afl-sharedmem.c ../../src/afl-common.c -DBIN_PATH=\"$(BIN_PATH)\" $(LDFLAGS) - -clean: - rm -f $(PROGRAMS) *~ core - -install: all - install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(DOC_PATH) - install -m 755 $(PROGRAMS) $${DESTDIR}$(BIN_PATH) - install -T -m 644 README.md $${DESTDIR}$(DOC_PATH)/README.network_proxy.md - diff --git a/examples/afl_network_proxy/Makefile b/examples/afl_network_proxy/Makefile deleted file mode 100644 index 0b306dde..00000000 --- a/examples/afl_network_proxy/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - @echo please use GNU make, thanks! diff --git a/examples/afl_network_proxy/README.md b/examples/afl_network_proxy/README.md deleted file mode 100644 index a5ac3578..00000000 --- a/examples/afl_network_proxy/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# afl-network-proxy - -If you want to run afl-fuzz over the network than this is what you need :) -Note that the impact on fuzzing speed will be huge, expect a loss of 90%. - -## When to use this - -1. when you have to fuzz a target that has to run on a system that cannot - contain the fuzzing output (e.g. /tmp too small and file system is read-only) -2. when the target instantly reboots on crashes -3. ... any other reason you would need this - -## how to get it running - -### Compiling - -Just type `make` and let the autodetection do everything for you. - -Note that you will get a 40-50% performance increase if you have libdeflate-dev -installed. The GNUmakefile will autodetect it if present. - -If your target has large test cases (10+kb) that are ascii only or large chunks -of zero blocks then set `CFLAGS=-DCOMPRESS_TESTCASES=1` to compress them. -For most targets this hurts performance though so it is disabled by default. - -### on the target - -Run `afl-network-server` with your target with the -m and -t values you need. -Important is the -i parameter which is the TCP port to listen on. -e.g.: -``` -afl-network-server -i 1111 -m 25M -t 1000 -- /bin/target -f @@ -``` - -### on the (afl-fuzz) master - -Just run afl-fuzz with your normal options, however the target should be -`afl-network-client` with the IP and PORT of the `afl-network-server` and -increase the -t value: -``` -afl-fuzz -i in -o out -t 2000+ -- afl-network-client TARGET-IP 1111 -``` -Note the '+' on the -t parameter value. The afl-network-server will take -care of proper timeouts hence afl-fuzz should not. The '+' increases the -timeout and the value itself should be 500-1000 higher than the one on -afl-network-server. - -### networking - -The TARGET can be an IPv4 or IPv6 address, or a host name that resolves to -either. Note that also the outgoing interface can be specified with a '%' for -`afl-network-client`, e.g. `fe80::1234%eth0`. - -Also make sure your default TCP window size is larger than your MAP_SIZE -(130kb is a good value). -On Linux that is the middle value of `/proc/sys/net/ipv4/tcp_rmem` - -## how to compile and install - -`make && sudo make install` - diff --git a/examples/afl_network_proxy/afl-network-client.c b/examples/afl_network_proxy/afl-network-client.c deleted file mode 100644 index a2451fdc..00000000 --- a/examples/afl_network_proxy/afl-network-client.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - american fuzzy lop++ - afl-network-client - --------------------------------------- - - Written by Marc Heuse - - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - -*/ - -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif -#include "config.h" -#include "types.h" -#include "debug.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#ifndef USEMMAP - #include -#endif -#include -#include -#include -#include -#include - -#ifdef USE_DEFLATE - #include -#endif - -u8 *__afl_area_ptr; - -#ifdef __ANDROID__ -u32 __afl_map_size = MAP_SIZE; -#else -__thread u32 __afl_map_size = MAP_SIZE; -#endif - -/* Error reporting to forkserver controller */ - -void send_forkserver_error(int error) { - - u32 status; - if (!error || error > 0xffff) return; - status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); - if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; - -} - -/* SHM setup. */ - -static void __afl_map_shm(void) { - - char *id_str = getenv(SHM_ENV_VAR); - char *ptr; - - if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { - - u32 val = atoi(ptr); - if (val > 0) __afl_map_size = val; - - } - - if (__afl_map_size > MAP_SIZE) { - - if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { - - fprintf(stderr, - "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_map_size); - if (id_str) { - - send_forkserver_error(FS_ERROR_MAP_SIZE); - exit(-1); - - } - - } else { - - fprintf(stderr, - "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_map_size); - - } - - } - - if (id_str) { - -#ifdef USEMMAP - const char * shm_file_path = id_str; - int shm_fd = -1; - unsigned char *shm_base = NULL; - - /* create the shared memory segment as if it was a file */ - shm_fd = shm_open(shm_file_path, O_RDWR, 0600); - if (shm_fd == -1) { - - fprintf(stderr, "shm_open() failed\n"); - send_forkserver_error(FS_ERROR_SHM_OPEN); - exit(1); - - } - - /* map the shared memory segment to the address space of the process */ - shm_base = - mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); - - if (shm_base == MAP_FAILED) { - - close(shm_fd); - shm_fd = -1; - - fprintf(stderr, "mmap() failed\n"); - send_forkserver_error(FS_ERROR_MMAP); - exit(2); - - } - - __afl_area_ptr = shm_base; -#else - u32 shm_id = atoi(id_str); - - __afl_area_ptr = shmat(shm_id, 0, 0); - -#endif - - if (__afl_area_ptr == (void *)-1) { - - send_forkserver_error(FS_ERROR_SHMAT); - exit(1); - - } - - /* Write something into the bitmap so that the parent doesn't give up */ - - __afl_area_ptr[0] = 1; - - } - -} - -/* Fork server logic. */ - -static void __afl_start_forkserver(void) { - - u8 tmp[4] = {0, 0, 0, 0}; - u32 status = 0; - - if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) - status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); - if (status) status |= (FS_OPT_ENABLED); - memcpy(tmp, &status, 4); - - /* Phone home and tell the parent that we're OK. */ - - if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; - -} - -static u32 __afl_next_testcase(u8 *buf, u32 max_len) { - - s32 status, res = 0x0fffffff; // res is a dummy pid - - /* Wait for parent by reading from the pipe. Abort if read fails. */ - if (read(FORKSRV_FD, &status, 4) != 4) return 0; - - /* we have a testcase - read it */ - status = read(0, buf, max_len); - - /* report that we are starting the target */ - if (write(FORKSRV_FD + 1, &res, 4) != 4) return 0; - - if (status < 1) - return 0; - else - return status; - -} - -static void __afl_end_testcase(int status) { - - if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1); - -} - -/* you just need to modify the while() loop in this main() */ - -int main(int argc, char *argv[]) { - - u8 * interface, *buf, *ptr; - s32 s = -1; - struct addrinfo hints, *hres, *aip; - u32 * lenptr, max_len = 65536; -#ifdef USE_DEFLATE - u8 * buf2; - u32 * lenptr1, *lenptr2, buf2_len, compress_len; - size_t decompress_len; -#endif - - if (argc < 3 || argc > 4) { - - printf("Syntax: %s host port [max-input-size]\n\n", argv[0]); - printf("Requires host and port of the remote afl-proxy-server instance.\n"); - printf( - "IPv4 and IPv6 are supported, also binding to an interface with " - "\"%%\"\n"); - printf("The max-input-size default is %u.\n", max_len); - printf( - "The default map size is %u and can be changed with setting " - "AFL_MAP_SIZE.\n", - __afl_map_size); - exit(-1); - - } - - if ((interface = strchr(argv[1], '%')) != NULL) *interface++ = 0; - - if (argc > 3) - if ((max_len = atoi(argv[3])) < 0) - FATAL("max-input-size may not be negative or larger than 2GB: %s", - argv[3]); - - if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) - if ((__afl_map_size = atoi(ptr)) < 8) - FATAL("illegal map size, may not be < 8 or >= 2^30: %s", ptr); - - if ((buf = malloc(max_len + 4)) == NULL) - PFATAL("can not allocate %u memory", max_len + 4); - lenptr = (u32 *)buf; - -#ifdef USE_DEFLATE - buf2_len = (max_len > __afl_map_size ? max_len : __afl_map_size); - if ((buf2 = malloc(buf2_len + 8)) == NULL) - PFATAL("can not allocate %u memory", buf2_len + 8); - lenptr1 = (u32 *)buf2; - lenptr2 = (u32 *)(buf2 + 4); -#endif - - memset(&hints, 0, sizeof(hints)); - hints.ai_socktype = SOCK_STREAM; - hints.ai_family = PF_UNSPEC; - - if (getaddrinfo(argv[1], argv[2], &hints, &hres) != 0) - PFATAL("could not resolve target %s", argv[1]); - - for (aip = hres; aip != NULL && s == -1; aip = aip->ai_next) { - - if ((s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol)) >= 0) { - -#ifdef SO_BINDTODEVICE - if (interface != NULL) - if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, interface, - strlen(interface) + 1) < 0) - fprintf(stderr, "Warning: could not bind to device %s\n", interface); -#else - fprintf(stderr, - "Warning: binding to interface is not supported for your OS\n"); -#endif - -#ifdef SO_PRIORITY - int priority = 7; - if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < - 0) { - - priority = 6; - if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, - sizeof(priority)) < 0) - WARNF("could not set priority on socket"); - - } - -#endif - - if (connect(s, aip->ai_addr, aip->ai_addrlen) == -1) s = -1; - - } - - } - -#ifdef USE_DEFLATE - struct libdeflate_compressor *compressor; - compressor = libdeflate_alloc_compressor(1); - struct libdeflate_decompressor *decompressor; - decompressor = libdeflate_alloc_decompressor(); - fprintf(stderr, "Compiled with compression support\n"); -#endif - - if (s == -1) - FATAL("could not connect to target tcp://%s:%s", argv[1], argv[2]); - else - fprintf(stderr, "Connected to target tcp://%s:%s\n", argv[1], argv[2]); - - /* we initialize the shared memory map and start the forkserver */ - __afl_map_shm(); - __afl_start_forkserver(); - - int i = 1, j, status, ret, received; - - // fprintf(stderr, "Waiting for first testcase\n"); - while ((*lenptr = __afl_next_testcase(buf + 4, max_len)) > 0) { - - // fprintf(stderr, "Sending testcase with len %u\n", *lenptr); -#ifdef USE_DEFLATE - #ifdef COMPRESS_TESTCASES - // we only compress the testcase if it does not fit in the TCP packet - if (*lenptr > 1500 - 20 - 32 - 4) { - - // set highest byte to signify compression - *lenptr1 = (*lenptr | 0xff000000); - *lenptr2 = (u32)libdeflate_deflate_compress(compressor, buf + 4, *lenptr, - buf2 + 8, buf2_len); - if (send(s, buf2, *lenptr2 + 8, 0) != *lenptr2 + 8) - PFATAL("sending test data failed"); - // fprintf(stderr, "COMPRESS (%u->%u):\n", *lenptr, *lenptr2); - // for (u32 i = 0; i < *lenptr; i++) - // fprintf(stderr, "%02x", buf[i + 4]); - // fprintf(stderr, "\n"); - // for (u32 i = 0; i < *lenptr2; i++) - // fprintf(stderr, "%02x", buf2[i + 8]); - // fprintf(stderr, "\n"); - - } else { - - #endif -#endif - if (send(s, buf, *lenptr + 4, 0) != *lenptr + 4) - PFATAL("sending test data failed"); -#ifdef USE_DEFLATE - #ifdef COMPRESS_TESTCASES - // fprintf(stderr, "unCOMPRESS (%u)\n", *lenptr); - - } - - #endif -#endif - - received = 0; - while (received < 4 && - (ret = recv(s, &status + received, 4 - received, 0)) > 0) - received += ret; - if (received != 4) - FATAL("did not receive waitpid data (%d, %d)", received, ret); - // fprintf(stderr, "Received status\n"); - - received = 0; -#ifdef USE_DEFLATE - while (received < 4 && - (ret = recv(s, &compress_len + received, 4 - received, 0)) > 0) - received += ret; - if (received != 4) - FATAL("did not receive compress_len (%d, %d)", received, ret); - // fprintf(stderr, "Received status\n"); - - received = 0; - while (received < compress_len && - (ret = recv(s, buf2 + received, buf2_len - received, 0)) > 0) - received += ret; - if (received != compress_len) - FATAL("did not receive coverage data (%d, %d)", received, ret); - - if (libdeflate_deflate_decompress(decompressor, buf2, compress_len, - __afl_area_ptr, __afl_map_size, - &decompress_len) != LIBDEFLATE_SUCCESS || - decompress_len != __afl_map_size) - FATAL("decompression failed"); - // fprintf(stderr, "DECOMPRESS (%u->%u): ", compress_len, decompress_len); - // for (u32 i = 0; i < __afl_map_size; i++) fprintf(stderr, "%02x", - // __afl_area_ptr[i]); fprintf(stderr, "\n"); -#else - while (received < __afl_map_size && - (ret = recv(s, __afl_area_ptr + received, __afl_map_size - received, - 0)) > 0) - received += ret; - if (received != __afl_map_size) - FATAL("did not receive coverage data (%d, %d)", received, ret); -#endif - // fprintf(stderr, "Received coverage\n"); - - /* report the test case is done and wait for the next */ - __afl_end_testcase(status); - // fprintf(stderr, "Waiting for next testcase %d\n", ++i); - - } - -#ifdef USE_DEFLATE - libdeflate_free_compressor(compressor); - libdeflate_free_decompressor(decompressor); -#endif - - return 0; - -} - diff --git a/examples/afl_network_proxy/afl-network-server.c b/examples/afl_network_proxy/afl-network-server.c deleted file mode 100644 index 513dc8f2..00000000 --- a/examples/afl_network_proxy/afl-network-server.c +++ /dev/null @@ -1,720 +0,0 @@ -/* - american fuzzy lop++ - network proxy server - ------------------------------------------- - - Originally written by Michal Zalewski - - Forkserver design by Jann Horn - - Now maintained by Marc Heuse , - Heiko Eißfeldt and - Andrea Fioraldi and - Dominik Maier - - Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - */ - -#define AFL_MAIN - -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif - -#include "config.h" -#include "types.h" -#include "debug.h" -#include "alloc-inl.h" -#include "hash.h" -#include "forkserver.h" -#include "sharedmem.h" -#include "common.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef USE_DEFLATE - #include -struct libdeflate_compressor * compressor; -struct libdeflate_decompressor *decompressor; -#endif - -static u8 *in_file, /* Minimizer input test case */ - *out_file; - -static u8 *in_data; /* Input data for trimming */ -static u8 *buf2; - -static s32 in_len; -static s32 buf2_len; -static u32 map_size = MAP_SIZE; - -static volatile u8 stop_soon; /* Ctrl-C pressed? */ - -/* See if any bytes are set in the bitmap. */ - -static inline u8 anything_set(afl_forkserver_t *fsrv) { - - u32 *ptr = (u32 *)fsrv->trace_bits; - u32 i = (map_size >> 2); - - while (i--) { - - if (*(ptr++)) { return 1; } - - } - - return 0; - -} - -static void at_exit_handler(void) { - - afl_fsrv_killall(); - -} - -/* Write output file. */ - -static s32 write_to_file(u8 *path, u8 *mem, u32 len) { - - s32 ret; - - unlink(path); /* Ignore errors */ - - ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600); - - if (ret < 0) { PFATAL("Unable to create '%s'", path); } - - ck_write(ret, mem, len, path); - - lseek(ret, 0, SEEK_SET); - - return ret; - -} - -/* Execute target application. Returns 0 if the changes are a dud, or - 1 if they should be kept. */ - -static u8 run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len, - u8 first_run) { - - afl_fsrv_write_to_testcase(fsrv, mem, len); - - fsrv_run_result_t ret = - afl_fsrv_run_target(fsrv, fsrv->exec_tmout, &stop_soon); - - if (ret == FSRV_RUN_ERROR) { FATAL("Couldn't run child"); } - - if (stop_soon) { - - SAYF(cRST cLRD "\n+++ aborted by user +++\n" cRST); - exit(1); - - } - - return ret; - -} - -/* Handle Ctrl-C and the like. */ - -static void handle_stop_sig(int sig) { - - stop_soon = 1; - afl_fsrv_killall(); - -} - -/* Do basic preparations - persistent fds, filenames, etc. */ - -static void set_up_environment(afl_forkserver_t *fsrv) { - - u8 *x; - - fsrv->dev_null_fd = open("/dev/null", O_RDWR); - if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); } - - if (!out_file) { - - u8 *use_dir = "."; - - if (access(use_dir, R_OK | W_OK | X_OK)) { - - use_dir = get_afl_env("TMPDIR"); - if (!use_dir) { use_dir = "/tmp"; } - - } - - out_file = alloc_printf("%s/.afl-input-temp-%u", use_dir, getpid()); - - } - - unlink(out_file); - - fsrv->out_fd = open(out_file, O_RDWR | O_CREAT | O_EXCL, 0600); - - if (fsrv->out_fd < 0) { PFATAL("Unable to create '%s'", out_file); } - - /* Set sane defaults... */ - - x = get_afl_env("ASAN_OPTIONS"); - - if (x) { - - if (!strstr(x, "abort_on_error=1")) { - - FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); - - } - - if (!strstr(x, "symbolize=0")) { - - FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); - - } - - } - - x = get_afl_env("MSAN_OPTIONS"); - - if (x) { - - if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) { - - FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY( - MSAN_ERROR) " - please fix!"); - - } - - if (!strstr(x, "symbolize=0")) { - - FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); - - } - - } - - setenv("ASAN_OPTIONS", - "abort_on_error=1:" - "detect_leaks=0:" - "symbolize=0:" - "allocator_may_return_null=1", - 0); - - setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" - "symbolize=0:" - "abort_on_error=1:" - "allocator_may_return_null=1:" - "msan_track_origins=0", 0); - - if (get_afl_env("AFL_PRELOAD")) { - - if (fsrv->qemu_mode) { - - u8 *qemu_preload = getenv("QEMU_SET_ENV"); - u8 *afl_preload = getenv("AFL_PRELOAD"); - u8 *buf; - - s32 i, afl_preload_size = strlen(afl_preload); - for (i = 0; i < afl_preload_size; ++i) { - - if (afl_preload[i] == ',') { - - PFATAL( - "Comma (',') is not allowed in AFL_PRELOAD when -Q is " - "specified!"); - - } - - } - - if (qemu_preload) { - - buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - qemu_preload, afl_preload, afl_preload); - - } else { - - buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - afl_preload, afl_preload); - - } - - setenv("QEMU_SET_ENV", buf, 1); - - afl_free(buf); - - } else { - - setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); - setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); - - } - - } - -} - -/* Setup signal handlers, duh. */ - -static void setup_signal_handlers(void) { - - struct sigaction sa; - - sa.sa_handler = NULL; - sa.sa_flags = SA_RESTART; - sa.sa_sigaction = NULL; - - sigemptyset(&sa.sa_mask); - - /* Various ways of saying "stop". */ - - sa.sa_handler = handle_stop_sig; - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - -} - -/* Display usage hints. */ - -static void usage(u8 *argv0) { - - SAYF( - "\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" - - "Required parameters:\n" - - " -i port - the port to listen for the client to connect to\n\n" - - "Execution control settings:\n" - - " -f file - input file read by the tested program (stdin)\n" - " -t msec - timeout for each run (%d ms)\n" - " -m megs - memory limit for child process (%d MB)\n" - " -Q - use binary-only instrumentation (QEMU mode)\n" - " -U - use unicorn-based instrumentation (Unicorn mode)\n" - " -W - use qemu-based instrumentation with Wine (Wine " - "mode)\n\n" - - "Environment variables used:\n" - "TMPDIR: directory to use for temporary input files\n" - "ASAN_OPTIONS: custom settings for ASAN\n" - " (must contain abort_on_error=1 and symbolize=0)\n" - "MSAN_OPTIONS: custom settings for MSAN\n" - " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n" - "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" - " the target was compiled for\n" - "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" - - , argv0, EXEC_TIMEOUT, MEM_LIMIT); - - exit(1); - -} - -int recv_testcase(int s, void **buf) { - - u32 size; - s32 ret; - size_t received; - - received = 0; - while (received < 4 && (ret = recv(s, &size + received, 4 - received, 0)) > 0) - received += ret; - if (received != 4) FATAL("did not receive size information"); - if (size == 0) FATAL("did not receive valid size information"); - // fprintf(stderr, "received size information of %d\n", size); - - if ((size & 0xff000000) != 0xff000000) { - - *buf = afl_realloc(buf, size); - if (unlikely(!*buf)) { PFATAL("Alloc"); } - received = 0; - // fprintf(stderr, "unCOMPRESS (%u)\n", size); - while (received < size && - (ret = recv(s, ((char *)*buf) + received, size - received, 0)) > 0) - received += ret; - - } else { - -#ifdef USE_DEFLATE - u32 clen; - size -= 0xff000000; - *buf = afl_realloc(buf, size); - if (unlikely(!*buf)) { PFATAL("Alloc"); } - received = 0; - while (received < 4 && - (ret = recv(s, &clen + received, 4 - received, 0)) > 0) - received += ret; - if (received != 4) FATAL("did not receive clen1 information"); - // fprintf(stderr, "received clen information of %d\n", clen); - if (clen < 1) - FATAL("did not receive valid compressed len information: %u", clen); - buf2 = afl_realloc((void **)&buf2, clen); - buf2_len = clen; - if (unlikely(!buf2)) { PFATAL("Alloc"); } - received = 0; - while (received < clen && - (ret = recv(s, buf2 + received, clen - received, 0)) > 0) - received += ret; - if (received != clen) FATAL("did not receive compressed information"); - if (libdeflate_deflate_decompress(decompressor, buf2, clen, (char *)*buf, - size, &received) != LIBDEFLATE_SUCCESS) - FATAL("decompression failed"); - // fprintf(stderr, "DECOMPRESS (%u->%u):\n", clen, received); - // for (u32 i = 0; i < clen; i++) fprintf(stderr, "%02x", buf2[i]); - // fprintf(stderr, "\n"); - // for (u32 i = 0; i < received; i++) fprintf(stderr, "%02x", - // ((u8*)(*buf))[i]); fprintf(stderr, "\n"); -#else - FATAL("Received compressed data but not compiled with compression support"); -#endif - - } - - // fprintf(stderr, "receiving testcase %p %p max %u\n", buf, *buf, *max_len); - if (received != size) - FATAL("did not receive testcase data %lu != %u, %d", received, size, ret); - // fprintf(stderr, "received testcase\n"); - return size; - -} - -/* Main entry point */ - -int main(int argc, char **argv_orig, char **envp) { - - s32 opt, s, sock, on = 1, port = -1; - u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0; - char **use_argv; - struct sockaddr_in6 serveraddr, clientaddr; - int addrlen = sizeof(clientaddr); - char str[INET6_ADDRSTRLEN]; - char ** argv = argv_cpy_dup(argc, argv_orig); - u8 * send_buf; -#ifdef USE_DEFLATE - u32 *lenptr; -#endif - - afl_forkserver_t fsrv_var = {0}; - afl_forkserver_t *fsrv = &fsrv_var; - afl_fsrv_init(fsrv); - map_size = get_map_size(); - fsrv->map_size = map_size; - - if ((send_buf = malloc(map_size + 4)) == NULL) PFATAL("malloc"); - - while ((opt = getopt(argc, argv, "+i:f:m:t:QUWh")) > 0) { - - switch (opt) { - - case 'i': - - if (port > 0) { FATAL("Multiple -i options not supported"); } - port = atoi(optarg); - if (port < 1 || port > 65535) - FATAL("invalid port definition, must be between 1-65535: %s", optarg); - break; - - case 'f': - - if (out_file) { FATAL("Multiple -f options not supported"); } - fsrv->use_stdin = 0; - out_file = optarg; - break; - - case 'm': { - - u8 suffix = 'M'; - - if (mem_limit_given) { FATAL("Multiple -m options not supported"); } - mem_limit_given = 1; - - if (!optarg) { FATAL("Wrong usage of -m"); } - - if (!strcmp(optarg, "none")) { - - fsrv->mem_limit = 0; - break; - - } - - if (sscanf(optarg, "%llu%c", &fsrv->mem_limit, &suffix) < 1 || - optarg[0] == '-') { - - FATAL("Bad syntax used for -m"); - - } - - switch (suffix) { - - case 'T': - fsrv->mem_limit *= 1024 * 1024; - break; - case 'G': - fsrv->mem_limit *= 1024; - break; - case 'k': - fsrv->mem_limit /= 1024; - break; - case 'M': - break; - - default: - FATAL("Unsupported suffix or bad syntax for -m"); - - } - - if (fsrv->mem_limit < 5) { FATAL("Dangerously low value of -m"); } - - if (sizeof(rlim_t) == 4 && fsrv->mem_limit > 2000) { - - FATAL("Value of -m out of range on 32-bit systems"); - - } - - } - - break; - - case 't': - - if (timeout_given) { FATAL("Multiple -t options not supported"); } - timeout_given = 1; - - if (!optarg) { FATAL("Wrong usage of -t"); } - - fsrv->exec_tmout = atoi(optarg); - - if (fsrv->exec_tmout < 10 || optarg[0] == '-') { - - FATAL("Dangerously low value of -t"); - - } - - break; - - case 'Q': - - if (fsrv->qemu_mode) { FATAL("Multiple -Q options not supported"); } - if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; } - - fsrv->qemu_mode = 1; - break; - - case 'U': - - if (unicorn_mode) { FATAL("Multiple -Q options not supported"); } - if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; } - - unicorn_mode = 1; - break; - - case 'W': /* Wine+QEMU mode */ - - if (use_wine) { FATAL("Multiple -W options not supported"); } - fsrv->qemu_mode = 1; - use_wine = 1; - - if (!mem_limit_given) { fsrv->mem_limit = 0; } - - break; - - case 'h': - usage(argv[0]); - return -1; - break; - - default: - usage(argv[0]); - - } - - } - - if (optind == argc || port < 1) { usage(argv[0]); } - - check_environment_vars(envp); - - sharedmem_t shm = {0}; - fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); - - in_data = afl_realloc((void **)&in_data, 65536); - if (unlikely(!in_data)) { PFATAL("Alloc"); } - - atexit(at_exit_handler); - setup_signal_handlers(); - - set_up_environment(fsrv); - - fsrv->target_path = find_binary(argv[optind]); - detect_file_args(argv + optind, out_file, &fsrv->use_stdin); - - if (fsrv->qemu_mode) { - - if (use_wine) { - - use_argv = get_wine_argv(argv[0], &fsrv->target_path, argc - optind, - argv + optind); - - } else { - - use_argv = get_qemu_argv(argv[0], &fsrv->target_path, argc - optind, - argv + optind); - - } - - } else { - - use_argv = argv + optind; - - } - - if ((sock = socket(AF_INET6, SOCK_STREAM, 0)) < 0) PFATAL("socket() failed"); - -#ifdef SO_REUSEADDR - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) { - - WARNF("setsockopt(SO_REUSEADDR) failed"); - - } - -#endif - -#ifdef SO_PRIORITY - int priority = 7; - if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < - 0) { - - priority = 6; - if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < - 0) - WARNF("could not set priority on socket"); - - } - -#endif - - memset(&serveraddr, 0, sizeof(serveraddr)); - serveraddr.sin6_family = AF_INET6; - serveraddr.sin6_port = htons(port); - serveraddr.sin6_addr = in6addr_any; - - if (bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) - PFATAL("bind() failed"); - - if (listen(sock, 1) < 0) { PFATAL("listen() failed"); } - - afl_fsrv_start( - fsrv, use_argv, &stop_soon, - (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) - ? 1 - : 0); - -#ifdef USE_DEFLATE - compressor = libdeflate_alloc_compressor(1); - decompressor = libdeflate_alloc_decompressor(); - buf2 = afl_realloc((void **)&buf2, map_size + 16); - buf2_len = map_size + 16; - if (unlikely(!buf2)) { PFATAL("alloc"); } - lenptr = (u32 *)(buf2 + 4); - fprintf(stderr, "Compiled with compression support\n"); -#endif - - fprintf(stderr, - "Waiting for incoming connection from afl-network-client on port %d " - "...\n", - port); - - if ((s = accept(sock, NULL, NULL)) < 0) { PFATAL("accept() failed"); } - fprintf(stderr, "Received connection, starting ...\n"); - -#ifdef SO_PRIORITY - priority = 7; - if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) { - - priority = 6; - if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) - WARNF("could not set priority on socket"); - - } - -#endif - - while ((in_len = recv_testcase(s, (void **)&in_data)) > 0) { - - // fprintf(stderr, "received %u\n", in_len); - (void)run_target(fsrv, use_argv, in_data, in_len, 1); - - memcpy(send_buf + 4, fsrv->trace_bits, fsrv->map_size); - -#ifdef USE_DEFLATE - memcpy(buf2, &fsrv->child_status, 4); - *lenptr = (u32)libdeflate_deflate_compress( - compressor, send_buf + 4, fsrv->map_size, buf2 + 8, buf2_len - 8); - // fprintf(stderr, "COMPRESS (%u->%u): ", fsrv->map_size, *lenptr); - // for (u32 i = 0; i < fsrv->map_size; i++) fprintf(stderr, "%02x", - // fsrv->trace_bits[i]); fprintf(stderr, "\n"); - if (send(s, buf2, *lenptr + 8, 0) != 8 + *lenptr) - FATAL("could not send data"); -#else - memcpy(send_buf, &fsrv->child_status, 4); - if (send(s, send_buf, fsrv->map_size + 4, 0) != 4 + fsrv->map_size) - FATAL("could not send data"); -#endif - - // fprintf(stderr, "sent result\n"); - - } - - unlink(out_file); - if (out_file) { ck_free(out_file); } - out_file = NULL; - - afl_shm_deinit(&shm); - afl_fsrv_deinit(fsrv); - if (fsrv->target_path) { ck_free(fsrv->target_path); } - afl_free(in_data); -#if USE_DEFLATE - afl_free(buf2); - libdeflate_free_compressor(compressor); - libdeflate_free_decompressor(decompressor); -#endif - - argv_cpy_free(argv); - - exit(0); - -} - diff --git a/examples/afl_proxy/Makefile b/examples/afl_proxy/Makefile deleted file mode 100644 index 4b368f8d..00000000 --- a/examples/afl_proxy/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: afl-proxy - -afl-proxy: afl-proxy.c - $(CC) -I../../include -o afl-proxy afl-proxy.c - -clean: - rm -f afl-proxy *~ core diff --git a/examples/afl_proxy/README.md b/examples/afl_proxy/README.md deleted file mode 100644 index 3c768a19..00000000 --- a/examples/afl_proxy/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# afl-proxy - -afl-proxy is an example skeleton file which can easily be used to fuzz -and instrument non-standard things. - -You only need to change the while() loop of the main() to send the -data of buf[] with length len to the target and write the coverage -information to __afl_area_ptr[__afl_map_size] - diff --git a/examples/afl_proxy/afl-proxy.c b/examples/afl_proxy/afl-proxy.c deleted file mode 100644 index f2dfeac1..00000000 --- a/examples/afl_proxy/afl-proxy.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - american fuzzy lop++ - afl-proxy skeleton example - --------------------------------------------------- - - Written by Marc Heuse - - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - - HOW-TO - ====== - - You only need to change the while() loop of the main() to send the - data of buf[] with length len to the target and write the coverage - information to __afl_area_ptr[__afl_map_size] - - -*/ - -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif -#include "config.h" -#include "types.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -u8 *__afl_area_ptr; - -#ifdef __ANDROID__ -u32 __afl_map_size = MAP_SIZE; -#else -__thread u32 __afl_map_size = MAP_SIZE; -#endif - -/* Error reporting to forkserver controller */ - -void send_forkserver_error(int error) { - - u32 status; - if (!error || error > 0xffff) return; - status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); - if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; - -} - -/* SHM setup. */ - -static void __afl_map_shm(void) { - - char *id_str = getenv(SHM_ENV_VAR); - char *ptr; - - if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { - - u32 val = atoi(ptr); - if (val > 0) __afl_map_size = val; - - } - - if (__afl_map_size > MAP_SIZE) { - - if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { - - fprintf(stderr, - "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_map_size); - if (id_str) { - - send_forkserver_error(FS_ERROR_MAP_SIZE); - exit(-1); - - } - - } else { - - fprintf(stderr, - "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_map_size); - - } - - } - - if (id_str) { - -#ifdef USEMMAP - const char * shm_file_path = id_str; - int shm_fd = -1; - unsigned char *shm_base = NULL; - - /* create the shared memory segment as if it was a file */ - shm_fd = shm_open(shm_file_path, O_RDWR, 0600); - if (shm_fd == -1) { - - fprintf(stderr, "shm_open() failed\n"); - send_forkserver_error(FS_ERROR_SHM_OPEN); - exit(1); - - } - - /* map the shared memory segment to the address space of the process */ - shm_base = - mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); - - if (shm_base == MAP_FAILED) { - - close(shm_fd); - shm_fd = -1; - - fprintf(stderr, "mmap() failed\n"); - send_forkserver_error(FS_ERROR_MMAP); - exit(2); - - } - - __afl_area_ptr = shm_base; -#else - u32 shm_id = atoi(id_str); - - __afl_area_ptr = shmat(shm_id, 0, 0); - -#endif - - if (__afl_area_ptr == (void *)-1) { - - send_forkserver_error(FS_ERROR_SHMAT); - exit(1); - - } - - /* Write something into the bitmap so that the parent doesn't give up */ - - __afl_area_ptr[0] = 1; - - } - -} - -/* Fork server logic. */ - -static void __afl_start_forkserver(void) { - - u8 tmp[4] = {0, 0, 0, 0}; - u32 status = 0; - - if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) - status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); - if (status) status |= (FS_OPT_ENABLED); - memcpy(tmp, &status, 4); - - /* Phone home and tell the parent that we're OK. */ - - if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; - -} - -static u32 __afl_next_testcase(u8 *buf, u32 max_len) { - - s32 status, res = 0xffffff; - - /* Wait for parent by reading from the pipe. Abort if read fails. */ - if (read(FORKSRV_FD, &status, 4) != 4) return 0; - - /* we have a testcase - read it */ - status = read(0, buf, max_len); - - /* report that we are starting the target */ - if (write(FORKSRV_FD + 1, &res, 4) != 4) return 0; - - if (status < 1) - return 0; - else - return status; - -} - -static void __afl_end_testcase(void) { - - int status = 0xffffff; - - if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1); - -} - -/* you just need to modify the while() loop in this main() */ - -int main(int argc, char *argv[]) { - - /* This is were the testcase data is written into */ - u8 buf[1024]; // this is the maximum size for a test case! set it! - u32 len; - - /* here you specify the map size you need that you are reporting to - afl-fuzz. */ - __afl_map_size = MAP_SIZE; // default is 65536 - - /* then we initialize the shared memory map and start the forkserver */ - __afl_map_shm(); - __afl_start_forkserver(); - - while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) { - - /* here you have to create the magic that feeds the buf/len to the - target and write the coverage to __afl_area_ptr */ - - // ... the magic ... - - /* report the test case is done and wait for the next */ - __afl_end_testcase(); - - } - - return 0; - -} - diff --git a/examples/afl_untracer/Makefile b/examples/afl_untracer/Makefile deleted file mode 100644 index 14a09b41..00000000 --- a/examples/afl_untracer/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -ifdef DEBUG - OPT=-O0 -else - OPT=-O3 -endif - -all: afl-untracer libtestinstr.so - -afl-untracer: afl-untracer.c - $(CC) $(OPT) -I../../include -g -o afl-untracer afl-untracer.c -ldl - -libtestinstr.so: libtestinstr.c - $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c - -clean: - rm -f afl-untracer libtestinstr.so *~ core diff --git a/examples/afl_untracer/README.md b/examples/afl_untracer/README.md deleted file mode 100644 index ada0c916..00000000 --- a/examples/afl_untracer/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# afl-untracer - fast fuzzing of binary-only libraries - -## Introduction - -afl-untracer is an example skeleton file which can easily be used to fuzz -a closed source library. - -It requires less memory and is x3-5 faster than qemu_mode however it is way -more course grained and does not provide interesting features like compcov -or cmplog. - -Supported is so far Intel (i386/x86_64) and AARCH64. - -## How-to - -### Modify afl-untracer.c - -Read and modify afl-untracer.c then `make`. -To adapt afl-untracer.c to your needs, read the header of the file and then -search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations. - -### Generate patches.txt file - -To generate the `patches.txt` file for your target library use the -`ida_get_patchpoints.py` script for IDA Pro or -`ghidra_get_patchpoints.java` for Ghidra. - -The patches.txt file has to be pointed to by `AFL_UNTRACER_FILE`. - -To easily run the scripts without needing to run the GUI with Ghidra: -``` -/opt/ghidra/support/analyzeHeadless /tmp/ tmp$$ -import libtestinstr.so -postscript ./ghidra_get_patchpoints.java -rm -rf /tmp/tmp$$ -``` -The file is created at `~/Desktop/patches.txt` - -### Fuzzing - -Example (after modifying afl-untracer.c to your needs, compiling and creating -patches.txt): -``` -LD_LIBRARY_PATH=/path/to/target/library AFL_UNTRACER_FILE=./patches.txt afl-fuzz -i in -o out -- ./afl-untracer -``` -(or even remote via afl-network-proxy). - -### Testing and debugging - -For testing/debugging you can try: -``` -make DEBUG=1 -AFL_UNTRACER_FILE=./patches.txt AFL_DEBUG=1 gdb ./afl-untracer -``` -and then you can easily set breakpoints to "breakpoint" and "fuzz". - -# Background - -This idea is based on [UnTracer](https://github.com/FoRTE-Research/UnTracer-AFL) -and modified by [Trapfuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz). -This implementation is slower because the traps are not patched out with each -run, but on the other hand gives much better coverage information. diff --git a/examples/afl_untracer/TODO b/examples/afl_untracer/TODO deleted file mode 100644 index fffffacf..00000000 --- a/examples/afl_untracer/TODO +++ /dev/null @@ -1,2 +0,0 @@ - * add shmem fuzzing - * add snapshot feature? diff --git a/examples/afl_untracer/afl-untracer.c b/examples/afl_untracer/afl-untracer.c deleted file mode 100644 index cb6f948c..00000000 --- a/examples/afl_untracer/afl-untracer.c +++ /dev/null @@ -1,768 +0,0 @@ -/* - american fuzzy lop++ - afl-untracer skeleton example - --------------------------------------------------- - - Written by Marc Heuse - - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - - HOW-TO - ====== - - You only need to change the following: - - 1. decide if you want to receive data from stdin [DEFAULT] or file(name) - -> use_stdin = 0 if via file, and what the maximum input size is - 2. dl load the library you want to fuzz, lookup the functions you need - and setup the calls to these - 3. in the while loop you call the functions in the necessary order - - incl the cleanup. the cleanup is important! - - Just look these steps up in the code, look for "// STEP x:" - - -*/ - -#define __USE_GNU -#define _GNU_SOURCE - -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif -#include "config.h" -#include "types.h" -#include "debug.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#if defined(__linux__) - #include -#elif defined(__APPLE__) && defined(__LP64__) - #include -#elif defined(__FreeBSD__) - #include - #include -#else - #error "Unsupported platform" -#endif - -#define MEMORY_MAP_DECREMENT 0x200000000000 -#define MAX_LIB_COUNT 128 - -// STEP 1: - -/* here you need to specify the parameter for the target function */ -static void *(*o_function)(u8 *buf, int len); - -/* use stdin (1) or a file on the commandline (0) */ -static u32 use_stdin = 1; - -/* This is were the testcase data is written into */ -static u8 buf[10000]; // this is the maximum size for a test case! set it! - -/* If you want to have debug output set this to 1, can also be set with - AFL_DEBUG */ -static u32 debug = 0; - -// END STEP 1 - -typedef struct library_list { - - u8 *name; - u64 addr_start, addr_end; - -} library_list_t; - -#ifdef __ANDROID__ -u32 __afl_map_size = MAP_SIZE; -u32 do_exit; -#else -__thread u32 __afl_map_size = MAP_SIZE; -__thread u32 do_exit; -#endif - -static pid_t pid = 65537; -static pthread_t __afl_thread; -static u8 __afl_dummy[MAP_SIZE]; -static u8 * __afl_area_ptr = __afl_dummy; -static u8 * inputfile; // this will point to argv[1] -static u32 len; - -static library_list_t liblist[MAX_LIB_COUNT]; -static u32 liblist_cnt; - -static void sigtrap_handler(int signum, siginfo_t *si, void *context); -static void fuzz(void); - -/* read the library information */ -void read_library_information(void) { - -#if defined(__linux__) - FILE *f; - u8 buf[1024], *b, *m, *e, *n; - - if ((f = fopen("/proc/self/maps", "r")) == NULL) - FATAL("cannot open /proc/self/maps"); - - if (debug) fprintf(stderr, "Library list:\n"); - while (fgets(buf, sizeof(buf), f)) { - - if (strstr(buf, " r-x")) { - - if (liblist_cnt >= MAX_LIB_COUNT) { - - WARNF("too many libraries to old, maximum count of %d reached", - liblist_cnt); - return; - - } - - b = buf; - m = index(buf, '-'); - e = index(buf, ' '); - if ((n = rindex(buf, '/')) == NULL) n = rindex(buf, ' '); - if (n && - ((*n >= '0' && *n <= '9') || *n == '[' || *n == '{' || *n == '(')) - n = NULL; - else - n++; - if (b && m && e && n && *n) { - - *m++ = 0; - *e = 0; - if (n[strlen(n) - 1] == '\n') n[strlen(n) - 1] = 0; - - liblist[liblist_cnt].name = strdup(n); - liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16); - liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16); - if (debug) - fprintf( - stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name, - liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_end - 1); - liblist_cnt++; - - } - - } - - } - - if (debug) fprintf(stderr, "\n"); - -#elif defined(__FreeBSD__) - int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; - char * buf, *start, *end; - size_t miblen = sizeof(mib) / sizeof(mib[0]); - size_t len; - - if (debug) fprintf(stderr, "Library list:\n"); - if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) { return; } - - len = len * 4 / 3; - - buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); - if (buf == MAP_FAILED) { return; } - - if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) { - - munmap(buf, len); - return; - - } - - start = buf; - end = buf + len; - - while (start < end) { - - struct kinfo_vmentry *region = (struct kinfo_vmentry *)start; - size_t size = region->kve_structsize; - - if (size == 0) { break; } - - if ((region->kve_protection & KVME_PROT_READ) && - !(region->kve_protection & KVME_PROT_EXEC)) { - - liblist[liblist_cnt].name = - region->kve_path[0] != '\0' ? strdup(region->kve_path) : 0; - liblist[liblist_cnt].addr_start = region->kve_start; - liblist[liblist_cnt].addr_end = region->kve_end; - - if (debug) { - - fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name, - liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_end - 1); - - } - - liblist_cnt++; - - } - - start += size; - - } - -#endif - -} - -library_list_t *find_library(char *name) { - -#if defined(__linux__) - u32 i; - - for (i = 0; i < liblist_cnt; i++) - if (strncmp(liblist[i].name, name, strlen(name)) == 0) return &liblist[i]; -#elif defined(__APPLE__) && defined(__LP64__) - kern_return_t err; - static library_list_t lib; - - // get the list of all loaded modules from dyld - // the task_info mach API will get the address of the dyld all_image_info - // struct for the given task from which we can get the names and load - // addresses of all modules - task_dyld_info_data_t task_dyld_info; - mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; - err = task_info(mach_task_self(), TASK_DYLD_INFO, - (task_info_t)&task_dyld_info, &count); - - const struct dyld_all_image_infos *all_image_infos = - (const struct dyld_all_image_infos *)task_dyld_info.all_image_info_addr; - const struct dyld_image_info *image_infos = all_image_infos->infoArray; - - for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) { - - const char * image_name = image_infos[i].imageFilePath; - mach_vm_address_t image_load_address = - (mach_vm_address_t)image_infos[i].imageLoadAddress; - if (strstr(image_name, name)) { - - lib.name = name; - lib.addr_start = (u64)image_load_address; - lib.addr_end = 0; - return &lib; - - } - - } - -#endif - - return NULL; - -} - -/* for having an easy breakpoint location after loading the shared library */ -// this seems to work for clang too. nice :) requires gcc 4.4+ -#pragma GCC push_options -#pragma GCC optimize("O0") -void breakpoint(void) { - - if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n"); - -} - -#pragma GCC pop_options - -/* Error reporting to forkserver controller */ - -void send_forkserver_error(int error) { - - u32 status; - if (!error || error > 0xffff) return; - status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); - if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; - -} - -/* SHM setup. */ - -static void __afl_map_shm(void) { - - char *id_str = getenv(SHM_ENV_VAR); - char *ptr; - - if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { - - u32 val = atoi(ptr); - if (val > 0) __afl_map_size = val; - - } - - if (__afl_map_size > MAP_SIZE) { - - if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { - - fprintf(stderr, - "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_map_size); - if (id_str) { - - send_forkserver_error(FS_ERROR_MAP_SIZE); - exit(-1); - - } - - } else { - - fprintf(stderr, - "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_map_size); - - } - - } - - if (id_str) { - -#ifdef USEMMAP - const char * shm_file_path = id_str; - int shm_fd = -1; - unsigned char *shm_base = NULL; - - /* create the shared memory segment as if it was a file */ - shm_fd = shm_open(shm_file_path, O_RDWR, 0600); - if (shm_fd == -1) { - - fprintf(stderr, "shm_open() failed\n"); - send_forkserver_error(FS_ERROR_SHM_OPEN); - exit(1); - - } - - /* map the shared memory segment to the address space of the process */ - shm_base = - mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); - - if (shm_base == MAP_FAILED) { - - close(shm_fd); - shm_fd = -1; - - fprintf(stderr, "mmap() failed\n"); - send_forkserver_error(FS_ERROR_MMAP); - exit(2); - - } - - __afl_area_ptr = shm_base; -#else - u32 shm_id = atoi(id_str); - - __afl_area_ptr = shmat(shm_id, 0, 0); - -#endif - - if (__afl_area_ptr == (void *)-1) { - - send_forkserver_error(FS_ERROR_SHMAT); - exit(1); - - } - - /* Write something into the bitmap so that the parent doesn't give up */ - - __afl_area_ptr[0] = 1; - - } - -} - -/* Fork server logic. */ -inline static void __afl_start_forkserver(void) { - - u8 tmp[4] = {0, 0, 0, 0}; - u32 status = 0; - - if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) - status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); - if (status) status |= (FS_OPT_ENABLED); - memcpy(tmp, &status, 4); - - /* Phone home and tell the parent that we're OK. */ - if (write(FORKSRV_FD + 1, tmp, 4) != 4) do_exit = 1; - // fprintf(stderr, "write0 %d\n", do_exit); - -} - -inline static u32 __afl_next_testcase(u8 *buf, u32 max_len) { - - s32 status; - - /* Wait for parent by reading from the pipe. Abort if read fails. */ - if (read(FORKSRV_FD, &status, 4) != 4) do_exit = 1; - // fprintf(stderr, "read %d\n", do_exit); - - /* we have a testcase - read it if we read from stdin */ - if (use_stdin) { - - if ((status = read(0, buf, max_len)) <= 0) exit(-1); - - } else - - status = 1; - // fprintf(stderr, "stdin: %d %d\n", use_stdin, status); - - /* report that we are starting the target */ - if (write(FORKSRV_FD + 1, &pid, 4) != 4) do_exit = 1; - // fprintf(stderr, "write1 %d\n", do_exit); - - __afl_area_ptr[0] = 1; // put something in the map - - return status; - -} - -inline static void __afl_end_testcase(int status) { - - if (write(FORKSRV_FD + 1, &status, 4) != 4) do_exit = 1; - // fprintf(stderr, "write2 %d\n", do_exit); - if (do_exit) exit(0); - -} - -#ifdef __aarch64__ - #define SHADOW(addr) \ - ((uint64_t *)(((uintptr_t)addr & 0xfffffffffffffff8) - \ - MEMORY_MAP_DECREMENT - \ - ((uintptr_t)addr & 0x7) * 0x10000000000)) -#else - #define SHADOW(addr) \ - ((uint32_t *)(((uintptr_t)addr & 0xfffffffffffffffc) - \ - MEMORY_MAP_DECREMENT - \ - ((uintptr_t)addr & 0x3) * 0x10000000000)) -#endif - -void setup_trap_instrumentation(void) { - - library_list_t *lib_base = NULL; - size_t lib_size = 0; - u8 * lib_addr; - char * line = NULL; - size_t nread, len = 0; - char * filename = getenv("AFL_UNTRACER_FILE"); - if (!filename) filename = getenv("TRAPFUZZ_FILE"); - if (!filename) FATAL("AFL_UNTRACER_FILE environment variable not set"); - - FILE *patches = fopen(filename, "r"); - if (!patches) FATAL("Couldn't open AFL_UNTRACER_FILE file %s", filename); - - // Index into the coverage bitmap for the current trap instruction. -#ifdef __aarch64__ - uint64_t bitmap_index = 0; -#else - uint32_t bitmap_index = 0; -#endif - - while ((nread = getline(&line, &len, patches)) != -1) { - - char *end = line + len; - - char *col = strchr(line, ':'); - if (col) { - - // It's a library:size pair - *col++ = 0; - - lib_base = find_library(line); - if (!lib_base) FATAL("Library %s does not appear to be loaded", line); - - // we ignore the defined lib_size - lib_size = strtoul(col, NULL, 16); -#if (__linux__) - if (lib_size < lib_base->addr_end - lib_base->addr_start) - lib_size = lib_base->addr_end - lib_base->addr_start; -#endif - if (lib_size % 0x1000 != 0) - WARNF("Invalid library size 0x%zx. Must be multiple of 0x1000", - lib_size); - - lib_addr = (u8 *)lib_base->addr_start; - - // Make library code writable. - if (mprotect((void *)lib_addr, lib_size, - PROT_READ | PROT_WRITE | PROT_EXEC) != 0) - FATAL("Failed to mprotect library %s writable", line); - - // Create shadow memory. -#ifdef __aarch64__ - for (int i = 0; i < 8; i++) { - -#else - for (int i = 0; i < 4; i++) { - -#endif - - void *shadow_addr = SHADOW(lib_addr + i); - void *shadow = mmap(shadow_addr, lib_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0); - if (debug) - fprintf(stderr, "Shadow: %s %d = %p-%p for %p\n", line, i, shadow, - shadow + lib_size - 1, lib_addr); - if (shadow == MAP_FAILED) FATAL("Failed to mmap shadow memory"); - - } - - // Done, continue with next line. - continue; - - } - - // It's an offset, parse it and do the patching. - unsigned long offset = strtoul(line, NULL, 16); - - if (offset > lib_size) - FATAL("Invalid offset: 0x%lx. Current library is 0x%zx bytes large", - offset, lib_size); - - if (bitmap_index >= __afl_map_size) - FATAL("Too many basic blocks to instrument"); - -#ifdef __arch64__ - uint64_t -#else - uint32_t -#endif - *shadow = SHADOW(lib_addr + offset); - if (*shadow != 0) continue; // skip duplicates - - // Make lookup entry in shadow memory. - -#if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__) || \ - defined(__i386__)) - - // this is for Intel x64 - - uint8_t orig_byte = lib_addr[offset]; - *shadow = (bitmap_index << 8) | orig_byte; - lib_addr[offset] = 0xcc; // replace instruction with debug trap - if (debug) - fprintf(stderr, - "Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %08x\n", - lib_addr, offset, lib_addr + offset, orig_byte, shadow, - bitmap_index, *shadow); - -#elif defined(__aarch64__) - - // this is for aarch64 - - uint32_t *patch_bytes = (uint32_t *)(lib_addr + offset); - uint32_t orig_bytes = *patch_bytes; - *shadow = (bitmap_index << 32) | orig_bytes; - *patch_bytes = 0xd4200000; // replace instruction with debug trap - if (debug) - fprintf(stderr, - "Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %016x\n", - lib_addr, offset, lib_addr + offset, orig_bytes, shadow, - bitmap_index, *shadow); - -#else - // this will be ARM and AARCH64 - // for ARM we will need to identify if the code is in thumb or ARM - #error "non x86_64/aarch64 not supported yet" - //__arm__: - // linux thumb: 0xde01 - // linux arm: 0xe7f001f0 - //__aarch64__: - // linux aarch64: 0xd4200000 -#endif - - bitmap_index++; - - } - - free(line); - fclose(patches); - - // Install signal handler for SIGTRAP. - struct sigaction s; - s.sa_flags = SA_SIGINFO; - s.sa_sigaction = sigtrap_handler; - sigemptyset(&s.sa_mask); - sigaction(SIGTRAP, &s, 0); - - if (debug) fprintf(stderr, "Patched %u locations.\n", bitmap_index); - __afl_map_size = bitmap_index; - if (__afl_map_size % 8) __afl_map_size = (((__afl_map_size + 7) >> 3) << 3); - -} - -/* the signal handler for the traps / debugging interrupts - No debug output here because this would cost speed */ -static void sigtrap_handler(int signum, siginfo_t *si, void *context) { - - uint64_t addr; - // Must re-execute the instruction, so decrement PC by one instruction. - ucontext_t *ctx = (ucontext_t *)context; -#if defined(__APPLE__) && defined(__LP64__) - ctx->uc_mcontext->__ss.__rip -= 1; - addr = ctx->uc_mcontext->__ss.__rip; -#elif defined(__linux__) - #if defined(__x86_64__) || defined(__i386__) - ctx->uc_mcontext.gregs[REG_RIP] -= 1; - addr = ctx->uc_mcontext.gregs[REG_RIP]; - #elif defined(__aarch64__) - ctx->uc_mcontext.pc -= 4; - addr = ctx->uc_mcontext.pc; - #else - #error "Unsupported processor" - #endif -#elif defined(__FreeBSD__) && defined(__LP64__) - ctx->uc_mcontext.mc_rip -= 1; - addr = ctx->uc_mcontext.mc_rip; -#else - #error "Unsupported platform" -#endif - - // fprintf(stderr, "TRAP at context addr = %lx, fault addr = %lx\n", addr, - // si->si_addr); - - // If the trap didn't come from our instrumentation, then we probably will - // just segfault here - uint8_t *faultaddr; - if (unlikely(si->si_addr)) - faultaddr = (u8 *)si->si_addr - 1; - else - faultaddr = (u8 *)addr; - // if (debug) fprintf(stderr, "Shadow location: %p\n", SHADOW(faultaddr)); - uint32_t shadow = *SHADOW(faultaddr); - uint8_t orig_byte = shadow & 0xff; - uint32_t index = shadow >> 8; - - // if (debug) fprintf(stderr, "shadow data: %x, orig_byte %02x, index %d\n", - // shadow, orig_byte, index); - - // Index zero is invalid so that it is still possible to catch actual trap - // instructions in instrumented libraries. - if (unlikely(index == 0)) abort(); - - // Restore original instruction - *faultaddr = orig_byte; - - __afl_area_ptr[index] = 128; - -} - -/* the MAIN function */ -int main(int argc, char *argv[]) { - - (void)personality(ADDR_NO_RANDOMIZE); // disable ASLR - - pid = getpid(); - if (getenv("AFL_DEBUG")) debug = 1; - - /* by default we use stdin, but also a filename can be passed, in this - case the input is argv[1] and we have to disable stdin */ - if (argc > 1) { - - use_stdin = 0; - inputfile = argv[1]; - - } - - // STEP 2: load the library you want to fuzz and lookup the functions, - // inclusive of the cleanup functions - // NOTE: above the main() you have to define the functions! - - void *dl = dlopen("./libtestinstr.so", RTLD_LAZY); - if (!dl) FATAL("could not find target library"); - o_function = dlsym(dl, "testinstr"); - if (!o_function) FATAL("could not resolve target function from library"); - if (debug) fprintf(stderr, "Function address: %p\n", o_function); - - // END STEP 2 - - /* setup instrumentation, shared memory and forkserver */ - breakpoint(); - read_library_information(); - setup_trap_instrumentation(); - __afl_map_shm(); - __afl_start_forkserver(); - - while (1) { - - // instead of fork() we could also use the snapshot lkm or do our own mini - // snapshot feature like in https://github.com/marcinguy/fuzzer - // -> snapshot.c - if ((pid = fork()) == -1) PFATAL("fork failed"); - - if (pid) { - - u32 status; - if (waitpid(pid, &status, 0) < 0) exit(1); - /* report the test case is done and wait for the next */ - __afl_end_testcase(status); - - } else { - - pid = getpid(); - while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) { - - // in this function the fuzz magic happens, this is STEP 3 - fuzz(); - - // we can use _exit which is faster because our target library - // was loaded via dlopen and therefore cannot have deconstructors - // registered. - _exit(0); - - } - - } - - } - - return 0; - -} - -#ifndef _DEBUG -inline -#endif - static void - fuzz(void) { - - // STEP 3: call the function to fuzz, also the functions you might - // need to call to prepare the function and - important! - - // to clean everything up - - // in this example we use the input file, not stdin! - (*o_function)(buf, len); - - // normally you also need to cleanup - //(*o_LibFree)(foo); - - // END STEP 3 - -} - diff --git a/examples/afl_untracer/ghidra_get_patchpoints.java b/examples/afl_untracer/ghidra_get_patchpoints.java deleted file mode 100644 index d341bea4..00000000 --- a/examples/afl_untracer/ghidra_get_patchpoints.java +++ /dev/null @@ -1,84 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// Find patch points for untracer tools (e.g. afl++ examples/afl_untracer) -// -// Copy to ..../Ghidra/Features/Search/ghidra_scripts/ -// Writes the results to ~/Desktop/patches.txt -// -// This is my very first Ghidra script. I am sure this could be done better. -// -//@category Search - -import ghidra.app.script.GhidraScript; -import ghidra.program.model.address.*; -import ghidra.program.model.block.*; -import ghidra.program.model.listing.*; -import ghidra.program.model.symbol.*; -import ghidra.program.model.mem.*; - -import java.io.*; - -public class ghidra_get_patchpoints extends GhidraScript { - - @Override - public void run() throws Exception { - - long segment_start = 0; - Memory memory = currentProgram.getMemory(); - MultEntSubModel model = new MultEntSubModel(currentProgram); - CodeBlockIterator subIter = model.getCodeBlocks(monitor); - BufferedWriter out = new BufferedWriter(new FileWriter(System.getProperty("user.home") + File.separator + "Desktop" + File.separator + "patches.txt")); - - while (subIter.hasNext()) { - - CodeBlock multiEntryBlock = subIter.next(); - SimpleBlockModel basicBlockModel = new SimpleBlockModel(currentProgram); - CodeBlockIterator bbIter = basicBlockModel.getCodeBlocksContaining(multiEntryBlock, monitor); - - while (bbIter.hasNext()) { - - CodeBlock basicBlock = bbIter.next(); - - if (segment_start == 0) { - - Address firstAddr = basicBlock.getFirstStartAddress(); - long firstBlockAddr = firstAddr.getAddressableWordOffset(); - MemoryBlock mb = memory.getBlock(firstAddr); - Address startAddr = mb.getStart(); - Address endAddr = mb.getEnd(); - segment_start = startAddr.getAddressableWordOffset(); - if ((firstBlockAddr - segment_start) >= 0x1000) - segment_start += 0x1000; - long segment_end = endAddr.getAddressableWordOffset(); - long segment_size = segment_end - segment_start; - if ((segment_size % 0x1000) > 0) - segment_size = (((segment_size / 0x1000) + 1) * 0x1000); - out.write(currentProgram.getName() + ":0x" + Long.toHexString(segment_size) + "\n"); - //println("Start: " + Long.toHexString(segment_start)); - //println("End: " + Long.toHexString(segment_end)); - - } - - if (basicBlock.getFirstStartAddress().getAddressableWordOffset() - segment_start > 0) - out.write("0x" + Long.toHexString(basicBlock.getFirstStartAddress().getAddressableWordOffset() - segment_start) + "\n"); - - } - } - - out.close(); - - } -} diff --git a/examples/afl_untracer/ida_get_patchpoints.py b/examples/afl_untracer/ida_get_patchpoints.py deleted file mode 100644 index 43cf6d89..00000000 --- a/examples/afl_untracer/ida_get_patchpoints.py +++ /dev/null @@ -1,62 +0,0 @@ -# -# IDAPython script for IDA Pro -# Slightly modified from https://github.com/googleprojectzero/p0tools/blob/master/TrapFuzz/findPatchPoints.py -# - -import idautils -import idaapi -import ida_nalt -import idc - -# See https://www.hex-rays.com/products/ida/support/ida74_idapython_no_bc695_porting_guide.shtml - -from os.path import expanduser -home = expanduser("~") - -patchpoints = set() - -max_offset = 0 -for seg_ea in idautils.Segments(): - name = idc.get_segm_name(seg_ea) - #print("Segment: " + name) - if name != "__text" and name != ".text": - continue - - start = idc.get_segm_start(seg_ea) - end = idc.get_segm_end(seg_ea) - first = 0 - subtract_addr = 0 - #print("Start: " + hex(start) + " End: " + hex(end)) - for func_ea in idautils.Functions(start, end): - f = idaapi.get_func(func_ea) - if not f: - continue - for block in idaapi.FlowChart(f): - if start <= block.start_ea < end: - if first == 0: - if block.start_ea >= 0x1000: - subtract_addr = 0x1000 - first = 1 - - max_offset = max(max_offset, block.start_ea) - patchpoints.add(block.start_ea - subtract_addr) - #else: - # print("Warning: broken CFG?") - -# Round up max_offset to page size -size = max_offset -rem = size % 0x1000 -if rem != 0: - size += 0x1000 - rem - -print("Writing to " + home + "/Desktop/patches.txt") - -with open(home + "/Desktop/patches.txt", "w") as f: - f.write(ida_nalt.get_root_filename() + ':' + hex(size) + '\n') - f.write('\n'.join(map(hex, sorted(patchpoints)))) - f.write('\n') - -print("Done, found {} patchpoints".format(len(patchpoints))) - -# For headless script running remove the comment from the next line -#ida_pro.qexit() diff --git a/examples/afl_untracer/libtestinstr.c b/examples/afl_untracer/libtestinstr.c deleted file mode 100644 index 96b1cf21..00000000 --- a/examples/afl_untracer/libtestinstr.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - american fuzzy lop++ - a trivial program to test the build - -------------------------------------------------------- - Originally written by Michal Zalewski - Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - http://www.apache.org/licenses/LICENSE-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include - -void testinstr(char *buf, int len) { - - if (len < 1) return; - buf[len] = 0; - - // we support three input cases - if (buf[0] == '0') - printf("Looks like a zero to me!\n"); - else if (buf[0] == '1') - printf("Pretty sure that is a one!\n"); - else - printf("Neither one or zero? How quaint!\n"); - -} - diff --git a/examples/afl_untracer/libtestinstr.so b/examples/afl_untracer/libtestinstr.so deleted file mode 100755 index 389a946c..00000000 Binary files a/examples/afl_untracer/libtestinstr.so and /dev/null differ diff --git a/examples/afl_untracer/patches.txt b/examples/afl_untracer/patches.txt deleted file mode 100644 index 7e964249..00000000 --- a/examples/afl_untracer/patches.txt +++ /dev/null @@ -1,34 +0,0 @@ -libtestinstr.so:0x1000 -0x10 -0x12 -0x20 -0x36 -0x30 -0x40 -0x50 -0x63 -0x6f -0x78 -0x80 -0xa4 -0xb0 -0xb8 -0x100 -0xc0 -0xc9 -0xd7 -0xe3 -0xe8 -0xf8 -0x105 -0x11a -0x135 -0x141 -0x143 -0x14e -0x15a -0x15c -0x168 -0x16a -0x16b -0x170 diff --git a/examples/aflpp_driver/GNUmakefile b/examples/aflpp_driver/GNUmakefile deleted file mode 100644 index c1a087d7..00000000 --- a/examples/aflpp_driver/GNUmakefile +++ /dev/null @@ -1,46 +0,0 @@ -ifeq "" "$(LLVM_CONFIG)" - LLVM_CONFIG=llvm-config -endif - -LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null) -ifneq "" "$(LLVM_BINDIR)" - LLVM_BINDIR := $(LLVM_BINDIR)/ -endif - -CFLAGS := -O3 -funroll-loops -g - -all: libAFLDriver.a libAFLQemuDriver.a aflpp_qemu_driver_hook.so - -aflpp_driver.o: aflpp_driver.c - -$(LLVM_BINDIR)clang -I. -I../../include $(CFLAGS) -c aflpp_driver.c - -libAFLDriver.a: aflpp_driver.o - ar ru libAFLDriver.a aflpp_driver.o - cp -vf libAFLDriver.a ../../ - -debug: - $(LLVM_BINDIR)clang -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.o ../../src/afl-performance.c - $(LLVM_BINDIR)clang -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c - #$(LLVM_BINDIR)clang -S -emit-llvm -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.ll ../../src/afl-performance.c - #$(LLVM_BINDIR)clang -S -emit-llvm -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c - ar ru libAFLDriver.a afl-performance.o aflpp_driver.o - -aflpp_qemu_driver.o: aflpp_qemu_driver.c - $(LLVM_BINDIR)clang $(CFLAGS) -O0 -funroll-loops -c aflpp_qemu_driver.c - -libAFLQemuDriver.a: aflpp_qemu_driver.o - ar ru libAFLQemuDriver.a aflpp_qemu_driver.o - cp -vf libAFLQemuDriver.a ../../ - -aflpp_qemu_driver_hook.so: aflpp_qemu_driver_hook.o - $(LLVM_BINDIR)clang -shared aflpp_qemu_driver_hook.o -o aflpp_qemu_driver_hook.so - -aflpp_qemu_driver_hook.o: aflpp_qemu_driver_hook.c - $(LLVM_BINDIR)clang -fPIC $(CFLAGS) -funroll-loops -c aflpp_qemu_driver_hook.c - -test: debug - #clang -S -emit-llvm -D_DEBUG=\"1\" -I../../include -Wl,--allow-multiple-definition -funroll-loops -o aflpp_driver_test.ll aflpp_driver_test.c - afl-clang-fast -D_DEBUG=\"1\" -I../../include -Wl,--allow-multiple-definition -funroll-loops -o aflpp_driver_test aflpp_driver_test.c libAFLDriver.a afl-performance.o - -clean: - rm -f *.o libAFLDriver*.a libAFLQemuDriver.a aflpp_qemu_driver_hook.so *~ core aflpp_driver_test diff --git a/examples/aflpp_driver/Makefile b/examples/aflpp_driver/Makefile deleted file mode 100644 index 3666a74d..00000000 --- a/examples/aflpp_driver/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - @gmake all || echo please install GNUmake diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c deleted file mode 100644 index 017aa72b..00000000 --- a/examples/aflpp_driver/aflpp_driver.c +++ /dev/null @@ -1,326 +0,0 @@ -//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -//===----------------------------------------------------------------------===// - -/* This file allows to fuzz libFuzzer-style target functions - (LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode. - -Usage: -################################################################################ -cat << EOF > test_fuzzer.cc -#include -#include -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - - if (size > 0 && data[0] == 'H') - if (size > 1 && data[1] == 'I') - if (size > 2 && data[2] == '!') - __builtin_trap(); - return 0; - -} - -EOF -# Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang. -clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c -# Build afl-llvm-rt.o.c from the AFL distribution. -clang -c -w $AFL_HOME/instrumentation/afl-llvm-rt.o.c -# Build this file, link it with afl-llvm-rt.o.o and the target code. -clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o -# Run AFL: -rm -rf IN OUT; mkdir IN OUT; echo z > IN/z; -$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out -################################################################################ -AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file -specified. If the file does not exist, it is created. This is useful for getting -stack traces (when using ASAN for example) or original error messages on hard -to reproduce bugs. Note that any content written to stderr will be written to -this file instead of stderr's usual location. - -AFL_DRIVER_CLOSE_FD_MASK: Similar to libFuzzer's -close_fd_mask behavior option. -If 1, close stdout at startup. If 2 close stderr; if 3 close both. - -*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "cmplog.h" - -#ifdef _DEBUG - #include "hash.h" -#endif - -#ifndef MAP_FIXED_NOREPLACE - #define MAP_FIXED_NOREPLACE 0x100000 -#endif - -#define MAX_DUMMY_SIZE 256000 - -// Platform detection. Copied from FuzzerInternal.h -#ifdef __linux__ - #define LIBFUZZER_LINUX 1 - #define LIBFUZZER_APPLE 0 - #define LIBFUZZER_NETBSD 0 - #define LIBFUZZER_FREEBSD 0 - #define LIBFUZZER_OPENBSD 0 -#elif __APPLE__ - #define LIBFUZZER_LINUX 0 - #define LIBFUZZER_APPLE 1 - #define LIBFUZZER_NETBSD 0 - #define LIBFUZZER_FREEBSD 0 - #define LIBFUZZER_OPENBSD 0 -#elif __NetBSD__ - #define LIBFUZZER_LINUX 0 - #define LIBFUZZER_APPLE 0 - #define LIBFUZZER_NETBSD 1 - #define LIBFUZZER_FREEBSD 0 - #define LIBFUZZER_OPENBSD 0 -#elif __FreeBSD__ - #define LIBFUZZER_LINUX 0 - #define LIBFUZZER_APPLE 0 - #define LIBFUZZER_NETBSD 0 - #define LIBFUZZER_FREEBSD 1 - #define LIBFUZZER_OPENBSD 0 -#elif __OpenBSD__ - #define LIBFUZZER_LINUX 0 - #define LIBFUZZER_APPLE 0 - #define LIBFUZZER_NETBSD 0 - #define LIBFUZZER_FREEBSD 0 - #define LIBFUZZER_OPENBSD 1 -#else - #error "Support for your platform has not been implemented" -#endif - -int __afl_sharedmem_fuzzing = 1; -extern unsigned int * __afl_fuzz_len; -extern unsigned char *__afl_fuzz_ptr; - -// libFuzzer interface is thin, so we don't include any libFuzzer headers. -int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); -__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); - -// Notify AFL about persistent mode. -static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; -int __afl_persistent_loop(unsigned int); - -// Notify AFL about deferred forkserver. -static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; -void __afl_manual_init(); - -// Use this optionally defined function to output sanitizer messages even if -// user asks to close stderr. -__attribute__((weak)) void __sanitizer_set_report_fd(void *); - -// Keep track of where stderr content is being written to, so that -// dup_and_close_stderr can use the correct one. -static FILE *output_file; - -// Experimental feature to use afl_driver without AFL's deferred mode. -// Needs to run before __afl_auto_init. -__attribute__((constructor(0))) static void __decide_deferred_forkserver(void) { - - if (getenv("AFL_DRIVER_DONT_DEFER")) { - - if (unsetenv("__AFL_DEFER_FORKSRV")) { - - perror("Failed to unset __AFL_DEFER_FORKSRV"); - abort(); - - } - - } - -} - -// If the user asks us to duplicate stderr, then do it. -static void maybe_duplicate_stderr() { - - char *stderr_duplicate_filename = - getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); - - if (!stderr_duplicate_filename) return; - - FILE *stderr_duplicate_stream = - freopen(stderr_duplicate_filename, "a+", stderr); - - if (!stderr_duplicate_stream) { - - fprintf( - stderr, - "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); - abort(); - - } - - output_file = stderr_duplicate_stream; - -} - -// Most of these I/O functions were inspired by/copied from libFuzzer's code. -static void discard_output(int fd) { - - FILE *temp = fopen("/dev/null", "w"); - if (!temp) abort(); - dup2(fileno(temp), fd); - fclose(temp); - -} - -static void close_stdout() { - - discard_output(STDOUT_FILENO); - -} - -// Prevent the targeted code from writing to "stderr" but allow sanitizers and -// this driver to do so. -static void dup_and_close_stderr() { - - int output_fileno = fileno(output_file); - int output_fd = dup(output_fileno); - if (output_fd <= 0) abort(); - FILE *new_output_file = fdopen(output_fd, "w"); - if (!new_output_file) abort(); - if (!__sanitizer_set_report_fd) return; - __sanitizer_set_report_fd((void *)(long int)output_fd); - discard_output(output_fileno); - -} - -// Close stdout and/or stderr if user asks for it. -static void maybe_close_fd_mask() { - - char *fd_mask_str = getenv("AFL_DRIVER_CLOSE_FD_MASK"); - if (!fd_mask_str) return; - int fd_mask = atoi(fd_mask_str); - if (fd_mask & 2) dup_and_close_stderr(); - if (fd_mask & 1) close_stdout(); - -} - -// Define LLVMFuzzerMutate to avoid link failures for targets that use it -// with libFuzzer's LLVMFuzzerCustomMutator. -size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { - - // assert(false && "LLVMFuzzerMutate should not be called from afl_driver"); - return 0; - -} - -// Execute any files provided as parameters. -static int ExecuteFilesOnyByOne(int argc, char **argv) { - - unsigned char *buf = malloc(MAX_FILE); - for (int i = 1; i < argc; i++) { - - int fd = open(argv[i], O_RDONLY); - if (fd == -1) continue; - ssize_t length = read(fd, buf, MAX_FILE); - if (length > 0) { - - printf("Reading %zu bytes from %s\n", length, argv[i]); - LLVMFuzzerTestOneInput(buf, length); - printf("Execution successful.\n"); - - } - - } - - free(buf); - return 0; - -} - -int main(int argc, char **argv) { - - printf( - "======================= INFO =========================\n" - "This binary is built for afl++.\n" - "To run the target function on individual input(s) execute this:\n" - " %s INPUT_FILE1 [INPUT_FILE2 ... ]\n" - "To fuzz with afl-fuzz execute this:\n" - " afl-fuzz [afl-flags] -- %s [-N]\n" - "afl-fuzz will run N iterations before re-spawning the process (default: " - "1000)\n" - "======================================================\n", - argv[0], argv[0]); - - output_file = stderr; - maybe_duplicate_stderr(); - maybe_close_fd_mask(); - if (LLVMFuzzerInitialize) { - - fprintf(stderr, "Running LLVMFuzzerInitialize ...\n"); - LLVMFuzzerInitialize(&argc, &argv); - fprintf(stderr, "continue...\n"); - - } - - // Do any other expensive one-time initialization here. - - uint8_t dummy_input[64] = {0}; - memcpy(dummy_input, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT)); - memcpy(dummy_input + 32, (void *)AFL_DEFER_FORKSVR, - sizeof(AFL_DEFER_FORKSVR)); - int N = INT_MAX; - if (argc == 2 && argv[1][0] == '-') - N = atoi(argv[1] + 1); - else if (argc == 2 && (N = atoi(argv[1])) > 0) - printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N); - else if (argc > 1) { - - __afl_sharedmem_fuzzing = 0; - __afl_manual_init(); - return ExecuteFilesOnyByOne(argc, argv); - - } - - assert(N > 0); - - // if (!getenv("AFL_DRIVER_DONT_DEFER")) - __afl_manual_init(); - - // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization - // on the first execution of LLVMFuzzerTestOneInput is ignored. - LLVMFuzzerTestOneInput(dummy_input, 1); - - int num_runs = 0; - while (__afl_persistent_loop(N)) { - -#ifdef _DEBUG - fprintf(stderr, "CLIENT crc: %016llx len: %u\n", - hash64(__afl_fuzz_ptr, *__afl_fuzz_len, 0xa5b35705), - *__afl_fuzz_len); - fprintf(stderr, "RECV:"); - for (int i = 0; i < *__afl_fuzz_len; i++) - fprintf(stderr, "%02x", __afl_fuzz_ptr[i]); - fprintf(stderr, "\n"); -#endif - if (*__afl_fuzz_len) { - - num_runs++; - LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len); - - } - - } - - printf("%s: successfully executed %d input(s)\n", argv[0], num_runs); - -} - diff --git a/examples/aflpp_driver/aflpp_driver_test.c b/examples/aflpp_driver/aflpp_driver_test.c deleted file mode 100644 index b4ff6bc6..00000000 --- a/examples/aflpp_driver/aflpp_driver_test.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include -#include - -#include "hash.h" - -void __attribute__((noinline)) crashme(const uint8_t *Data, size_t Size) { - - if (Size < 5) return; - - if (Data[0] == 'F') - if (Data[1] == 'A') - if (Data[2] == '$') - if (Data[3] == '$') - if (Data[4] == '$') abort(); - -} - -int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - - if (Size) - fprintf(stderr, "FUNC crc: %016llx len: %lu\n", - hash64((u8 *)Data, (unsigned int)Size, - (unsigned long long int)0xa5b35705), - Size); - - crashme(Data, Size); - - return 0; - -} - diff --git a/examples/aflpp_driver/aflpp_qemu_driver.c b/examples/aflpp_driver/aflpp_qemu_driver.c deleted file mode 100644 index 4f3e5f71..00000000 --- a/examples/aflpp_driver/aflpp_qemu_driver.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include - -// libFuzzer interface is thin, so we don't include any libFuzzer headers. -int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); -__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); - -static const size_t kMaxAflInputSize = 1 * 1024 * 1024; -static uint8_t AflInputBuf[kMaxAflInputSize]; - -void __attribute__((noinline)) afl_qemu_driver_stdin_input(void) { - - size_t l = read(0, AflInputBuf, kMaxAflInputSize); - LLVMFuzzerTestOneInput(AflInputBuf, l); - -} - -int main(int argc, char **argv) { - - if (LLVMFuzzerInitialize) LLVMFuzzerInitialize(&argc, &argv); - // Do any other expensive one-time initialization here. - - if (getenv("AFL_QEMU_DRIVER_NO_HOOK")) { - - afl_qemu_driver_stdin_input(); - - } else { - - uint8_t dummy_input[1024000] = {0}; - LLVMFuzzerTestOneInput(dummy_input, 1); - - } - - return 0; - -} - diff --git a/examples/aflpp_driver/aflpp_qemu_driver_hook.c b/examples/aflpp_driver/aflpp_qemu_driver_hook.c deleted file mode 100644 index 823cc42d..00000000 --- a/examples/aflpp_driver/aflpp_qemu_driver_hook.c +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include - -#define g2h(x) ((void *)((unsigned long)(x) + guest_base)) - -#define REGS_RDI 7 -#define REGS_RSI 6 - -void afl_persistent_hook(uint64_t *regs, uint64_t guest_base, - uint8_t *input_buf, uint32_t input_len) { - - memcpy(g2h(regs[REGS_RDI]), input_buf, input_len); - regs[REGS_RSI] = input_len; - -} - -int afl_persistent_hook_init(void) { - - return 1; - -} - diff --git a/examples/analysis_scripts/queue2csv.sh b/examples/analysis_scripts/queue2csv.sh deleted file mode 100755 index 2528b438..00000000 --- a/examples/analysis_scripts/queue2csv.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/bin/bash - -test -z "$1" -o -z "$2" -o "$1" = "-h" -o "$1" = "-hh" -o "$1" = "--help" -o '!' -d "$1" && { - echo "Syntax: [-n] $0 out-directory file.csv [\"tools/target --opt @@\"]" - echo Option -n will suppress the CSV header. - echo If the target execution command is supplied then also edge coverage is gathered. - exit 1 -} - -function getval() { - VAL="" - if [ "$file" != "${file/$1/}" ]; then - TMP="${file/*$1:/}" - VAL="${TMP/,*/}" - fi -} - -SKIP= -if [ "$1" = "-n" ]; then - SKIP=1 - shift -fi - -test -n "$4" && { echo "Error: too many commandline options. Target command and options including @@ have to be passed within \"\"!"; exit 1; } - -test -d "$1"/queue && OUT="$1/queue" || OUT="$1" - -OK=`ls $OUT/id:000000,time:0,orig:* 2> /dev/null` -if [ -n "$OK" ]; then - LISTCMD="ls $OUT/id:"* -else - LISTCMD="ls -tr $OUT/" -fi - -ID=;SRC=;TIME=;OP=;POS=;REP=;EDGES=;EDGES_TOTAL=; -DIR="$OUT/../stats" -rm -rf "$DIR" -> "$2" || exit 1 -mkdir "$DIR" || exit 1 -> "$DIR/../edges.txt" || exit 1 - -{ - - if [ -z "$SKIP" ]; then - echo "time;\"filename\";id;src;new_cov;edges;total_edges;\"op\";pos;rep;unique_edges" - fi - - $LISTCMD | grep -v ,sync: | sed 's/.*id:/id:/g' | while read file; do - - if [ -n "$3" ]; then - - TMP=${3/@@/$OUT/$file} - - if [ "$TMP" = "$3" ]; then - - cat "$OUT/$file" | afl-showmap -o "$DIR/$file" -q -- $3 >/dev/null 2>&1 - - else - - afl-showmap -o "$DIR/$file" -q -- $TMP >/dev/null 2>&1 - - fi - - { cat "$DIR/$file" | sed 's/:.*//' ; cat "$DIR/../edges.txt" ; } | sort -nu > $DIR/../edges.txt.tmp - mv $DIR/../edges.txt.tmp $DIR/../edges.txt - EDGES=$(cat "$DIR/$file" | wc -l) - EDGES_TOTAL=$(cat "$DIR/../edges.txt" | wc -l) - - fi - - getval id; ID="$VAL" - getval src; SRC="$VAL" - getval time; TIME="$VAL" - getval op; OP="$VAL" - getval pos; POS="$VAL" - getval rep; REP="$VAL" - if [ "$file" != "${file/+cov/}" ]; then - COV=1 - else - COV="" - fi - - if [ -n "$3" -a -s "$DIR/../edges.txt" ]; then - echo "$TIME;\"$file\";$ID;$SRC;$COV;$EDGES;$EDGES_TOTAL;\"$OP\";$POS;$REP;UNIQUE$file" - else - echo "$TIME;\"$file\";$ID;$SRC;$COV;;;\"$OP\";$POS;$REP;" - fi - - done - -} | tee "$DIR/../queue.csv" > "$2" || exit 1 - -if [ -n "$3" -a -s "$DIR/../edges.txt" ]; then - - cat "$DIR/"* | sed 's/:.*//' | sort -n | uniq -c | egrep '^[ \t]*1 ' | awk '{print$2}' > $DIR/../unique.txt - - if [ -s "$DIR/../unique.txt" ]; then - - ls "$DIR/id:"* | grep -v ",sync:" |sed 's/.*\/id:/id:/g' | while read file; do - - CNT=$(sed 's/:.*//' "$DIR/$file" | tee "$DIR/../tmp.txt" | wc -l) - DIFF=$(diff -u "$DIR/../tmp.txt" "$DIR/../unique.txt" | egrep '^-[0-9]' | wc -l) - UNIQUE=$(($CNT - $DIFF)) - sed -i "s/;UNIQUE$file/;$UNIQUE/" "$DIR/../queue.csv" "$2" - - done - - rm -f "$DIR/../tmp.txt" - - else - - sed -i 's/;UNIQUE.*/;/' "$DIR/../queue.csv" "$2" - - fi - -fi - -mv "$DIR/../queue.csv" "$DIR/queue.csv" -if [ -e "$DIR/../edges.txt" ]; then mv "$DIR/../edges.txt" "$DIR/edges.txt"; fi -if [ -e "$DIR/../unique.txt" ]; then mv "$DIR/../unique.txt" "$DIR/unique.txt"; fi - -echo "Created $2" diff --git a/examples/argv_fuzzing/Makefile b/examples/argv_fuzzing/Makefile deleted file mode 100644 index 5a0ac6e6..00000000 --- a/examples/argv_fuzzing/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -# -# american fuzzy lop++ - argvfuzz -# -------------------------------- -# -# Copyright 2019-2020 Kjell Braden -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -.PHONY: all install clean - -PREFIX ?= /usr/local -BIN_PATH = $(PREFIX)/bin -HELPER_PATH = $(PREFIX)/lib/afl - -CFLAGS = -fPIC -Wall -Wextra -LDFLAGS = -shared - -UNAME_SAYS_LINUX=$(shell uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?) -UNAME_SAYS_LINUX:sh=uname | grep -E '^Linux|^GNU' >/dev/null; echo $$? - -_LDFLAGS_ADD=$(UNAME_SAYS_LINUX:1=) -LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-ldl) -LDFLAGS += $(LDFLAGS_ADD) - -# on gcc for arm there is no -m32, but -mbe32 -M32FLAG = -m32 -M64FLAG = -m64 - -CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?) -CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$? -CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?) -CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$? - -_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER) -__M32FLAG=$(_M32FLAG:00=-mbe32) -___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32) -M32FLAG=$(___M32FLAG) - -all: argvfuzz32.so argvfuzz64.so - -argvfuzz32.so: argvfuzz.c - -@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "argvfuzz32 build failure (that's fine)" - -argvfuzz64.so: argvfuzz.c - -@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "argvfuzz64 build failure (that's fine)" - -install: argvfuzz32.so argvfuzz64.so - install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ - if [ -f argvfuzz32.so ]; then set -e; install -m 755 argvfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi - if [ -f argvfuzz64.so ]; then set -e; install -m 755 argvfuzz64.so $(DESTDIR)$(HELPER_PATH)/; fi - -clean: - rm -f argvfuzz32.so argvfuzz64.so diff --git a/examples/argv_fuzzing/README.md b/examples/argv_fuzzing/README.md deleted file mode 100644 index fa8cad80..00000000 --- a/examples/argv_fuzzing/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# argvfuzz - -afl supports fuzzing file inputs or stdin. When source is available, -`argv-fuzz-inl.h` can be used to change `main()` to build argv from stdin. - -`argvfuzz` tries to provide the same functionality for binaries. When loaded -using `LD_PRELOAD`, it will hook the call to `__libc_start_main` and replace -argv using the same logic of `argv-fuzz-inl.h`. - -A few conditions need to be fulfilled for this mechanism to work correctly: - -1. As it relies on hooking the loader, it cannot work on static binaries. -2. If the target binary does not use the default libc's `_start` implementation - (crt1.o), the hook may not run. -3. The hook will replace argv with pointers to `.data` of `argvfuzz.so`. If the - target binary expects argv to be living on the stack, things may go wrong. diff --git a/examples/argv_fuzzing/argv-fuzz-inl.h b/examples/argv_fuzzing/argv-fuzz-inl.h deleted file mode 100644 index c15c0271..00000000 --- a/examples/argv_fuzzing/argv-fuzz-inl.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - american fuzzy lop++ - sample argv fuzzing wrapper - ------------------------------------------------ - - Originally written by Michal Zalewski - - Copyright 2015 Google Inc. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This file shows a simple way to fuzz command-line parameters with stock - afl-fuzz. To use, add: - - #include "/path/to/argv-fuzz-inl.h" - - ...to the file containing main(), ideally placing it after all the - standard includes. Next, put AFL_INIT_ARGV(); near the very beginning of - main(). - - This will cause the program to read NUL-delimited input from stdin and - put it in argv[]. Two subsequent NULs terminate the array. Empty - params are encoded as a lone 0x02. Lone 0x02 can't be generated, but - that shouldn't matter in real life. - - If you would like to always preserve argv[0], use this instead: - AFL_INIT_SET0("prog_name"); - -*/ - -#ifndef _HAVE_ARGV_FUZZ_INL -#define _HAVE_ARGV_FUZZ_INL - -#include - -#define AFL_INIT_ARGV() \ - do { \ - \ - argv = afl_init_argv(&argc); \ - \ - } while (0) - -#define AFL_INIT_SET0(_p) \ - do { \ - \ - argv = afl_init_argv(&argc); \ - argv[0] = (_p); \ - if (!argc) argc = 1; \ - \ - } while (0) - -#define MAX_CMDLINE_LEN 100000 -#define MAX_CMDLINE_PAR 50000 - -static char **afl_init_argv(int *argc) { - - static char in_buf[MAX_CMDLINE_LEN]; - static char *ret[MAX_CMDLINE_PAR]; - - char *ptr = in_buf; - int rc = 0; - - if (read(0, in_buf, MAX_CMDLINE_LEN - 2) < 0) {} - - while (*ptr && rc < MAX_CMDLINE_PAR) { - - ret[rc] = ptr; - if (ret[rc][0] == 0x02 && !ret[rc][1]) ret[rc]++; - rc++; - - while (*ptr) - ptr++; - ptr++; - - } - - *argc = rc; - - return ret; - -} - -#undef MAX_CMDLINE_LEN -#undef MAX_CMDLINE_PAR - -#endif /* !_HAVE_ARGV_FUZZ_INL */ - diff --git a/examples/argv_fuzzing/argvfuzz.c b/examples/argv_fuzzing/argvfuzz.c deleted file mode 100644 index 4251ca4c..00000000 --- a/examples/argv_fuzzing/argvfuzz.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - american fuzzy lop++ - LD_PRELOAD for fuzzing argv in binaries - ------------------------------------------------------------ - - Copyright 2019-2020 Kjell Braden - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - */ - -#define _GNU_SOURCE /* for RTLD_NEXT */ -#include -#include -#include -#include -#include "argv-fuzz-inl.h" - -int __libc_start_main(int (*main)(int, char **, char **), int argc, char **argv, - void (*init)(void), void (*fini)(void), - void (*rtld_fini)(void), void *stack_end) { - - int (*orig)(int (*main)(int, char **, char **), int argc, char **argv, - void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), - void *stack_end); - int sub_argc; - char **sub_argv; - - (void)argc; - (void)argv; - - orig = dlsym(RTLD_NEXT, __func__); - - if (!orig) { - - fprintf(stderr, "hook did not find original %s: %s\n", __func__, dlerror()); - exit(EXIT_FAILURE); - - } - - sub_argv = afl_init_argv(&sub_argc); - - return orig(main, sub_argc, sub_argv, init, fini, rtld_fini, stack_end); - -} - diff --git a/examples/asan_cgroups/limit_memory.sh b/examples/asan_cgroups/limit_memory.sh deleted file mode 100755 index 1f0f04ad..00000000 --- a/examples/asan_cgroups/limit_memory.sh +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env bash -# -# american fuzzy lop++ - limit memory using cgroups -# ----------------------------------------------- -# -# Written by Samir Khakimov and -# David A. Wheeler -# -# Edits to bring the script in line with afl-cmin and other companion scripts -# by Michal Zalewski. All bugs are my fault. -# -# Copyright 2015 Institute for Defense Analyses. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# This tool allows the amount of actual memory allocated to a program -# to be limited on Linux systems using cgroups, instead of the traditional -# setrlimit() API. This helps avoid the address space problems discussed in -# docs/notes_for_asan.md. -# -# Important: the limit covers *both* afl-fuzz and the fuzzed binary. In some -# hopefully rare circumstances, afl-fuzz could be killed before the fuzzed -# task. -# - -echo "cgroup tool for afl-fuzz by and " -echo - -unset NEW_USER -MEM_LIMIT="50" - -while getopts "+u:m:" opt; do - - case "$opt" in - - "u") - NEW_USER="$OPTARG" - ;; - - "m") - MEM_LIMIT="$[OPTARG]" - ;; - - "?") - exit 1 - ;; - - esac - -done - -if [ "$MEM_LIMIT" -lt "5" ]; then - echo "[-] Error: malformed or dangerously low value of -m." 1>&2 - exit 1 -fi - -shift $((OPTIND-1)) - -TARGET_BIN="$1" - -if [ "$TARGET_BIN" = "" -o "$NEW_USER" = "" ]; then - - cat 1>&2 <<_EOF_ -Usage: $0 [ options ] -- /path/to/afl-fuzz [ ...afl options... ] - -Required parameters: - - -u user - run the fuzzer as a specific user after setting up limits - -Optional parameters: - - -m megs - set memory limit to a specified value ($MEM_LIMIT MB) - -This tool configures cgroups-based memory limits for a fuzzing job to simplify -the task of fuzzing ASAN or MSAN binaries. You would normally want to use it in -conjunction with '-m none' passed to the afl-fuzz binary itself, say: - - $0 -u joe ./afl-fuzz -i input -o output -m none /path/to/target - -_EOF_ - - exit 1 - -fi - -# Basic sanity checks - -if [ ! "`uname -s`" = "Linux" ]; then - echo "[-] Error: this tool does not support non-Linux systems." 1>&2 - exit 1 -fi - -if [ ! "`id -u`" = "0" ]; then - echo "[-] Error: you need to run this script as root (sorry!)." 1>&2 - exit 1 -fi - -if ! type cgcreate 2>/dev/null 1>&2; then - - echo "[-] Error: you need to install cgroup tools first." 1>&2 - - if type apt-get 2>/dev/null 1>&2; then - echo " (Perhaps 'apt-get install cgroup-bin' will work.)" 1>&2 - elif type yum 2>/dev/null 1>&2; then - echo " (Perhaps 'yum install libcgroup-tools' will work.)" 1>&2 - fi - - exit 1 - -fi - -if ! id -u "$NEW_USER" 2>/dev/null 1>&2; then - echo "[-] Error: user '$NEW_USER' does not seem to exist." 1>&2 - exit 1 -fi - -# Create a new cgroup path if necessary... We used PID-keyed groups to keep -# parallel afl-fuzz tasks separate from each other. - -CID="afl-$NEW_USER-$$" - -CPATH="/sys/fs/cgroup/memory/$CID" - -if [ ! -d "$CPATH" ]; then - - cgcreate -a "$NEW_USER" -g memory:"$CID" || exit 1 - -fi - -# Set the appropriate limit... - -if [ -f "$CPATH/memory.memsw.limit_in_bytes" ]; then - - echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" 2>/dev/null - echo "${MEM_LIMIT}M" > "$CPATH/memory.memsw.limit_in_bytes" || exit 1 - echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1 - -elif grep -qE 'partition|file' /proc/swaps; then - - echo "[-] Error: your system requires swap to be disabled first (swapoff -a)." 1>&2 - exit 1 - -else - - echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1 - -fi - -# All right. At this point, we can just run the command. - -cgexec -g "memory:$CID" su -c "$*" "$NEW_USER" - -cgdelete -g "memory:$CID" diff --git a/examples/bash_shellshock/shellshock-fuzz.diff b/examples/bash_shellshock/shellshock-fuzz.diff deleted file mode 100644 index 3fa05bf8..00000000 --- a/examples/bash_shellshock/shellshock-fuzz.diff +++ /dev/null @@ -1,59 +0,0 @@ -This patch shows a very simple way to find post-Shellshock bugs in bash, as -discussed here: - - http://lcamtuf.blogspot.com/2014/10/bash-bug-how-we-finally-cracked.html - -In essence, it shows a way to fuzz environmental variables. Instructions: - -1) Download bash 4.3, apply this patch, compile with: - - CC=/path/to/afl-gcc ./configure - make clean all - - Note that the harness puts the fuzzed output in $TEST_VARIABLE. With - Florian's Shellshock patch (bash43-028), this is no longer passed down - to the parser. - -2) Create and cd to an empty directory, put the compiled bash binary in - there, and run these commands: - - mkdir in_dir - echo -n '() { a() { a; }; : >b; }' >in_dir/script.txt - -3) Run the fuzzer with: - - /path/to/afl-fuzz -d -i in_dir -o out_dir ./bash -c : - - The -d parameter is advisable only if the tested shell is fairly slow - or if you are in a hurry; will cover more ground faster, but - less systematically. - -4) Watch for crashes in out_dir/crashes/. Also watch for any new files - created in cwd if you're interested in non-crash RCEs (files will be - created whenever the shell executes "foo>bar" or something like - that). You can correlate their creation date with new entries in - out_dir/queue/. - - You can also modify the bash binary to directly check for more subtle - fault conditions, or use the synthesized entries in out_dir/queue/ - as a seed for other, possibly slower or more involved testing regimes. - - Expect several hours to get decent coverage. - ---- bash-4.3/shell.c.orig 2014-01-14 14:04:32.000000000 +0100 -+++ bash-4.3/shell.c 2015-04-30 05:56:46.000000000 +0200 -@@ -371,6 +371,14 @@ - env = environ; - #endif /* __OPENNT */ - -+ { -+ -+ static char val[1024 * 16]; -+ read(0, val, sizeof(val) - 1); -+ setenv("TEST_VARIABLE", val, 1); -+ -+ } -+ - USE_VAR(argc); - USE_VAR(argv); - USE_VAR(env); diff --git a/examples/canvas_harness/canvas_harness.html b/examples/canvas_harness/canvas_harness.html deleted file mode 100644 index a37b6937..00000000 --- a/examples/canvas_harness/canvas_harness.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - -

- -
- - - -

Results

- -
    - - - - diff --git a/examples/clang_asm_normalize/as b/examples/clang_asm_normalize/as deleted file mode 100755 index 45537cae..00000000 --- a/examples/clang_asm_normalize/as +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/sh -# -# american fuzzy lop++ - clang assembly normalizer -# ---------------------------------------------- -# -# Originally written by Michal Zalewski -# The idea for this wrapper comes from Ryan Govostes. -# -# Copyright 2013, 2014 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# This 'as' wrapper should allow you to instrument unruly, hand-written -# assembly with afl-as. -# -# Usage: -# -# export AFL_REAL_PATH=/path/to/directory/with/afl-as/ -# AFL_PATH=/path/to/this/directory/ make clean all - -if [ "$#" -lt "2" ]; then - echo "[-] Error: this utility can't be called directly." 1>&2 - exit 1 -fi - -if [ "$AFL_REAL_PATH" = "" ]; then - echo "[-] Error: AFL_REAL_PATH not set!" 1>&2 - exit 1 -fi - -if [ ! -x "$AFL_REAL_PATH/afl-as" ]; then - echo "[-] Error: AFL_REAL_PATH does not contain the 'afl-as' binary." 1>&2 - exit 1 -fi - -unset __AFL_AS_CMDLINE __AFL_FNAME - -while [ ! "$#" = "0" ]; do - - if [ "$#" = "1" ]; then - __AFL_FNAME="$1" - else - __AFL_AS_CMDLINE="${__AFL_AS_CMDLINE} $1" - fi - - shift - -done - -test "$TMPDIR" = "" && TMPDIR=/tmp - -TMPFILE=`mktemp $TMPDIR/.afl-XXXXXXXXXX.s` - -test "$TMPFILE" = "" && exit 1 - -clang -cc1as -filetype asm -output-asm-variant 0 "${__AFL_FNAME}" >"$TMPFILE" - -ERR="$?" - -if [ ! "$ERR" = "0" ]; then - rm -f "$TMPFILE" - exit $ERR -fi - -"$AFL_REAL_PATH/afl-as" ${__AFL_AS_CMDLINE} "$TMPFILE" - -ERR="$?" - -rm -f "$TMPFILE" - -exit "$ERR" diff --git a/examples/crash_triage/triage_crashes.sh b/examples/crash_triage/triage_crashes.sh deleted file mode 100755 index bf763cba..00000000 --- a/examples/crash_triage/triage_crashes.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/bin/sh -# -# american fuzzy lop++ - crash triage utility -# ----------------------------------------- -# -# Originally written by Michal Zalewski -# -# Copyright 2013, 2014, 2017 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Note that this assumes that the targeted application reads from stdin -# and requires no other cmdline parameters. Modify as needed if this is -# not the case. -# -# Note that on OpenBSD, you may need to install a newer version of gdb -# (e.g., from ports). You can set GDB=/some/path to point to it if -# necessary. -# - -echo "crash triage utility for afl-fuzz by Michal Zalewski" -echo - -ulimit -v 100000 2>/dev/null -ulimit -d 100000 2>/dev/null - -if [ "$#" -lt "2" ]; then - echo "Usage: $0 /path/to/afl_output_dir /path/to/tested_binary [...target params...]" 1>&2 - echo 1>&2 - exit 1 -fi - -DIR="$1" -BIN="$2" -shift -shift - -if [ "$AFL_ALLOW_TMP" = "" ]; then - - echo "$DIR" | grep -qE '^(/var)?/tmp/' - T1="$?" - - echo "$BIN" | grep -qE '^(/var)?/tmp/' - T2="$?" - - if [ "$T1" = "0" -o "$T2" = "0" ]; then - echo "[-] Error: do not use shared /tmp or /var/tmp directories with this script." 1>&2 - exit 1 - fi - -fi - -if - [ "$GDB" = "" ]; then - GDB=gdb -fi - -if [ ! -f "$BIN" -o ! -x "$BIN" ]; then - echo "[-] Error: binary '$2' not found or is not executable." 1>&2 - exit 1 -fi - -if [ ! -d "$DIR/queue" ]; then - echo "[-] Error: directory '$1' not found or not created by afl-fuzz." 1>&2 - exit 1 -fi - -CCOUNT=$((`ls -- "$DIR/crashes" 2>/dev/null | wc -l`)) - -if [ "$CCOUNT" = "0" ]; then - echo "No crashes recorded in the target directory - nothing to be done." - exit 0 -fi - -echo - -for crash in $DIR/crashes/id:*; do - - id=`basename -- "$crash" | cut -d, -f1 | cut -d: -f2` - sig=`basename -- "$crash" | cut -d, -f2 | cut -d: -f2` - - # Grab the args, converting @@ to $crash - - use_args="" - use_stdio=1 - - for a in $@; do - - if [ "$a" = "@@" ] ; then - use_args="$use_args $crash" - unset use_stdio - else - use_args="$use_args $a" - fi - - done - - # Strip the trailing space - use_args="${use_args# }" - - echo "+++ ID $id, SIGNAL $sig +++" - echo - - if [ "$use_stdio" = "1" ]; then - $GDB --batch -q --ex "r $use_args <$crash" --ex 'back' --ex 'disass $pc, $pc+16' --ex 'info reg' --ex 'quit' "$BIN" 0 - -#define INITIAL_GROWTH_SIZE (64) - -#define RAND_BELOW(limit) (rand() % (limit)) - -/* Use in a struct: creates a name_buf and a name_size variable. */ -#define BUF_VAR(type, name) \ - type * name##_buf; \ - size_t name##_size; -/* this fills in `&structptr->something_buf, &structptr->something_size`. */ -#define BUF_PARAMS(struct, name) \ - (void **)&struct->name##_buf, &struct->name##_size - -typedef struct { - -} afl_t; - -static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) { - - static s8 interesting_8[] = {INTERESTING_8}; - static s16 interesting_16[] = {INTERESTING_8, INTERESTING_16}; - static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32}; - - switch (RAND_BELOW(12)) { - - case 0: { - - /* Flip a single bit somewhere. Spooky! */ - - s32 bit_idx = ((RAND_BELOW(end - begin) + begin) << 3) + RAND_BELOW(8); - - out_buf[bit_idx >> 3] ^= 128 >> (bit_idx & 7); - - break; - - } - - case 1: { - - /* Set byte to interesting value. */ - - u8 val = interesting_8[RAND_BELOW(sizeof(interesting_8))]; - out_buf[(RAND_BELOW(end - begin) + begin)] = val; - - break; - - } - - case 2: { - - /* Set word to interesting value, randomly choosing endian. */ - - if (end - begin < 2) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 1) break; - - switch (RAND_BELOW(2)) { - - case 0: - *(u16 *)(out_buf + byte_idx) = - interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]; - break; - case 1: - *(u16 *)(out_buf + byte_idx) = - SWAP16(interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]); - break; - - } - - break; - - } - - case 3: { - - /* Set dword to interesting value, randomly choosing endian. */ - - if (end - begin < 4) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 3) break; - - switch (RAND_BELOW(2)) { - - case 0: - *(u32 *)(out_buf + byte_idx) = - interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]; - break; - case 1: - *(u32 *)(out_buf + byte_idx) = - SWAP32(interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]); - break; - - } - - break; - - } - - case 4: { - - /* Set qword to interesting value, randomly choosing endian. */ - - if (end - begin < 8) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 7) break; - - switch (RAND_BELOW(2)) { - - case 0: - *(u64 *)(out_buf + byte_idx) = - (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]; - break; - case 1: - *(u64 *)(out_buf + byte_idx) = SWAP64( - (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]); - break; - - } - - break; - - } - - case 5: { - - /* Randomly subtract from byte. */ - - out_buf[(RAND_BELOW(end - begin) + begin)] -= 1 + RAND_BELOW(ARITH_MAX); - - break; - - } - - case 6: { - - /* Randomly add to byte. */ - - out_buf[(RAND_BELOW(end - begin) + begin)] += 1 + RAND_BELOW(ARITH_MAX); - - break; - - } - - case 7: { - - /* Randomly subtract from word, random endian. */ - - if (end - begin < 2) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 1) break; - - if (RAND_BELOW(2)) { - - *(u16 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u16 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u16 *)(out_buf + byte_idx) = - SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) - num); - - } - - break; - - } - - case 8: { - - /* Randomly add to word, random endian. */ - - if (end - begin < 2) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 1) break; - - if (RAND_BELOW(2)) { - - *(u16 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u16 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u16 *)(out_buf + byte_idx) = - SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) + num); - - } - - break; - - } - - case 9: { - - /* Randomly subtract from dword, random endian. */ - - if (end - begin < 4) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 3) break; - - if (RAND_BELOW(2)) { - - *(u32 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u32 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u32 *)(out_buf + byte_idx) = - SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) - num); - - } - - break; - - } - - case 10: { - - /* Randomly add to dword, random endian. */ - - if (end - begin < 4) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 3) break; - - if (RAND_BELOW(2)) { - - *(u32 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u32 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u32 *)(out_buf + byte_idx) = - SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) + num); - - } - - break; - - } - - case 11: { - - /* Just set a random byte to a random value. Because, - why not. We use XOR with 1-255 to eliminate the - possibility of a no-op. */ - - out_buf[(RAND_BELOW(end - begin) + begin)] ^= 1 + RAND_BELOW(255); - - break; - - } - - } - -} - -/* This function calculates the next power of 2 greater or equal its argument. - @return The rounded up power of 2 (if no overflow) or 0 on overflow. -*/ -static inline size_t next_pow2(size_t in) { - - if (in == 0 || in > (size_t)-1) - return 0; /* avoid undefined behaviour under-/overflow */ - size_t out = in - 1; - out |= out >> 1; - out |= out >> 2; - out |= out >> 4; - out |= out >> 8; - out |= out >> 16; - return out + 1; - -} - -/* This function makes sure *size is > size_needed after call. - It will realloc *buf otherwise. - *size will grow exponentially as per: - https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/ - Will return NULL and free *buf if size_needed is <1 or realloc failed. - @return For convenience, this function returns *buf. - */ -static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) { - - /* No need to realloc */ - if (likely(size_needed && *size >= size_needed)) return *buf; - - /* No initial size was set */ - if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE; - - /* grow exponentially */ - size_t next_size = next_pow2(size_needed); - - /* handle overflow */ - if (!next_size) { next_size = size_needed; } - - /* alloc */ - *buf = realloc(*buf, next_size); - *size = *buf ? next_size : 0; - - return *buf; - -} - -/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */ -static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2, - size_t *size2) { - - void * scratch_buf = *buf1; - size_t scratch_size = *size1; - *buf1 = *buf2; - *size1 = *size2; - *buf2 = scratch_buf; - *size2 = scratch_size; - -} - -#undef INITIAL_GROWTH_SIZE - -#endif - diff --git a/examples/custom_mutators/example.c b/examples/custom_mutators/example.c deleted file mode 100644 index 23add128..00000000 --- a/examples/custom_mutators/example.c +++ /dev/null @@ -1,376 +0,0 @@ -/* - New Custom Mutator for AFL++ - Written by Khaled Yakdan - Andrea Fioraldi - Shengtuo Hu - Dominik Maier -*/ - -// You need to use -I /path/to/AFLplusplus/include -#include "custom_mutator_helpers.h" - -#include -#include -#include -#include - -#define DATA_SIZE (100) - -static const char *commands[] = { - - "GET", - "PUT", - "DEL", - -}; - -typedef struct my_mutator { - - afl_t *afl; - - // any additional data here! - size_t trim_size_current; - int trimmming_steps; - int cur_step; - - // Reused buffers: - BUF_VAR(u8, fuzz); - BUF_VAR(u8, data); - BUF_VAR(u8, havoc); - BUF_VAR(u8, trim); - BUF_VAR(u8, post_process); - -} my_mutator_t; - -/** - * Initialize this custom mutator - * - * @param[in] afl a pointer to the internal state object. Can be ignored for - * now. - * @param[in] seed A seed for this mutator - the same seed should always mutate - * in the same way. - * @return Pointer to the data object this custom mutator instance should use. - * There may be multiple instances of this mutator in one afl-fuzz run! - * Return NULL on error. - */ -my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { - - srand(seed); // needed also by surgical_havoc_mutate() - - my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); - if (!data) { - - perror("afl_custom_init alloc"); - return NULL; - - } - - data->afl = afl; - - return data; - -} - -/** - * Perform custom mutations on a given input - * - * (Optional for now. Required in the future) - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @param[in] buf Pointer to input data to be mutated - * @param[in] buf_size Size of input data - * @param[out] out_buf the buffer we will work on. we can reuse *buf. NULL on - * error. - * @param[in] add_buf Buffer containing the additional test case - * @param[in] add_buf_size Size of the additional test case - * @param[in] max_size Maximum size of the mutated output. The mutation must not - * produce data larger than max_size. - * @return Size of the mutated output. - */ -size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, - u8 **out_buf, uint8_t *add_buf, - size_t add_buf_size, // add_buf can be NULL - size_t max_size) { - - // Make sure that the packet size does not exceed the maximum size expected by - // the fuzzer - size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_size; - - // maybe_grow is optimized to be quick for reused buffers. - u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), mutated_size); - if (!mutated_out) { - - *out_buf = NULL; - perror("custom mutator allocation (maybe_grow)"); - return 0; /* afl-fuzz will very likely error out after this. */ - - } - - // Randomly select a command string to add as a header to the packet - memcpy(mutated_out, commands[rand() % 3], 3); - - // Mutate the payload of the packet - int i; - for (i = 0; i < 8; ++i) { - - // Randomly perform one of the (no len modification) havoc mutations - surgical_havoc_mutate(mutated_out, 3, mutated_size); - - } - - *out_buf = mutated_out; - return mutated_size; - -} - -/** - * A post-processing function to use right before AFL writes the test case to - * disk in order to execute the target. - * - * (Optional) If this functionality is not needed, simply don't define this - * function. - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @param[in] buf Buffer containing the test case to be executed - * @param[in] buf_size Size of the test case - * @param[out] out_buf Pointer to the buffer containing the test case after - * processing. External library should allocate memory for out_buf. - * The buf pointer may be reused (up to the given buf_size); - * @return Size of the output buffer after processing or the needed amount. - * A return of 0 indicates an error. - */ -size_t afl_custom_post_process(my_mutator_t *data, uint8_t *buf, - size_t buf_size, uint8_t **out_buf) { - - uint8_t *post_process_buf = - maybe_grow(BUF_PARAMS(data, post_process), buf_size + 5); - if (!post_process_buf) { - - perror("custom mutator realloc failed."); - *out_buf = NULL; - return 0; - - } - - memcpy(post_process_buf + 5, buf, buf_size); - post_process_buf[0] = 'A'; - post_process_buf[1] = 'F'; - post_process_buf[2] = 'L'; - post_process_buf[3] = '+'; - post_process_buf[4] = '+'; - - *out_buf = post_process_buf; - - return buf_size + 5; - -} - -/** - * This method is called at the start of each trimming operation and receives - * the initial buffer. It should return the amount of iteration steps possible - * on this input (e.g. if your input has n elements and you want to remove - * them one by one, return n, if you do a binary search, return log(n), - * and so on...). - * - * If your trimming algorithm doesn't allow you to determine the amount of - * (remaining) steps easily (esp. while running), then you can alternatively - * return 1 here and always return 0 in post_trim until you are finished and - * no steps remain. In that case, returning 1 in post_trim will end the - * trimming routine. The whole current index/max iterations stuff is only used - * to show progress. - * - * (Optional) - * - * @param data pointer returned in afl_custom_init for this fuzz case - * @param buf Buffer containing the test case - * @param buf_size Size of the test case - * @return The amount of possible iteration steps to trim the input. - * negative on error. - */ -int32_t afl_custom_init_trim(my_mutator_t *data, uint8_t *buf, - size_t buf_size) { - - // We simply trim once - data->trimmming_steps = 1; - - data->cur_step = 0; - - if (!maybe_grow(BUF_PARAMS(data, trim), buf_size)) { - - perror("init_trim grow"); - return -1; - - } - - memcpy(data->trim_buf, buf, buf_size); - - data->trim_size_current = buf_size; - - return data->trimmming_steps; - -} - -/** - * This method is called for each trimming operation. It doesn't have any - * arguments because we already have the initial buffer from init_trim and we - * can memorize the current state in *data. This can also save - * reparsing steps for each iteration. It should return the trimmed input - * buffer, where the returned data must not exceed the initial input data in - * length. Returning anything that is larger than the original data (passed - * to init_trim) will result in a fatal abort of AFLFuzz. - * - * (Optional) - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @param[out] out_buf Pointer to the buffer containing the trimmed test case. - * External library should allocate memory for out_buf. - * AFL++ will not release the memory after saving the test case. - * Keep a ref in *data. - * *out_buf = NULL is treated as error. - * @return Pointer to the size of the trimmed test case - */ -size_t afl_custom_trim(my_mutator_t *data, uint8_t **out_buf) { - - *out_buf = data->trim_buf; - - // Remove the last byte of the trimming input - return data->trim_size_current - 1; - -} - -/** - * This method is called after each trim operation to inform you if your - * trimming step was successful or not (in terms of coverage). If you receive - * a failure here, you should reset your input to the last known good state. - * - * (Optional) - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @param success Indicates if the last trim operation was successful. - * @return The next trim iteration index (from 0 to the maximum amount of - * steps returned in init_trim). negative ret on failure. - */ -int32_t afl_custom_post_trim(my_mutator_t *data, int success) { - - if (success) { - - ++data->cur_step; - return data->cur_step; - - } - - return data->trimmming_steps; - -} - -/** - * Perform a single custom mutation on a given input. - * This mutation is stacked with the other muatations in havoc. - * - * (Optional) - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @param[in] buf Pointer to the input data to be mutated and the mutated - * output - * @param[in] buf_size Size of input data - * @param[out] out_buf The output buffer. buf can be reused, if the content - * fits. *out_buf = NULL is treated as error. - * @param[in] max_size Maximum size of the mutated output. The mutation must - * not produce data larger than max_size. - * @return Size of the mutated output. - */ -size_t afl_custom_havoc_mutation(my_mutator_t *data, u8 *buf, size_t buf_size, - u8 **out_buf, size_t max_size) { - - if (buf_size == 0) { - - *out_buf = maybe_grow(BUF_PARAMS(data, havoc), 1); - if (!*out_buf) { - - perror("custom havoc: maybe_grow"); - return 0; - - } - - **out_buf = rand() % 256; - buf_size = 1; - - } else { - - // We reuse buf here. It's legal and faster. - *out_buf = buf; - - } - - size_t victim = rand() % buf_size; - (*out_buf)[victim] += rand() % 10; - - return buf_size; - -} - -/** - * Return the probability (in percentage) that afl_custom_havoc_mutation - * is called in havoc. By default it is 6 %. - * - * (Optional) - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @return The probability (0-100). - */ -uint8_t afl_custom_havoc_mutation_probability(my_mutator_t *data) { - - return 5; // 5 % - -} - -/** - * Determine whether the fuzzer should fuzz the queue entry or not. - * - * (Optional) - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @param filename File name of the test case in the queue entry - * @return Return True(1) if the fuzzer will fuzz the queue entry, and - * False(0) otherwise. - */ -uint8_t afl_custom_queue_get(my_mutator_t *data, const uint8_t *filename) { - - return 1; - -} - -/** - * Allow for additional analysis (e.g. calling a different tool that does a - * different kind of coverage and saves this for the custom mutator). - * - * (Optional) - * - * @param data pointer returned in afl_custom_init for this fuzz case - * @param filename_new_queue File name of the new queue entry - * @param filename_orig_queue File name of the original queue entry - */ -void afl_custom_queue_new_entry(my_mutator_t * data, - const uint8_t *filename_new_queue, - const uint8_t *filename_orig_queue) { - - /* Additional analysis on the original or new test case */ - -} - -/** - * Deinitialize everything - * - * @param data The data ptr from afl_custom_init - */ -void afl_custom_deinit(my_mutator_t *data) { - - free(data->post_process_buf); - free(data->havoc_buf); - free(data->data_buf); - free(data->fuzz_buf); - free(data->trim_buf); - free(data); - -} - diff --git a/examples/custom_mutators/example.py b/examples/custom_mutators/example.py deleted file mode 100644 index cf659e5a..00000000 --- a/examples/custom_mutators/example.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 -''' -Example Python Module for AFLFuzz - -@author: Christian Holler (:decoder) - -@license: - -This Source Code Form is subject to the terms of the Mozilla Public -License, v. 2.0. If a copy of the MPL was not distributed with this -file, You can obtain one at http://mozilla.org/MPL/2.0/. - -@contact: choller@mozilla.com -''' - -import random - - -COMMANDS = [ - b"GET", - b"PUT", - b"DEL", - b"AAAAAAAAAAAAAAAAA", -] - - -def init(seed): - ''' - Called once when AFLFuzz starts up. Used to seed our RNG. - - @type seed: int - @param seed: A 32-bit random value - ''' - random.seed(seed) - - -def deinit(): - pass - - -def fuzz(buf, add_buf, max_size): - ''' - Called per fuzzing iteration. - - @type buf: bytearray - @param buf: The buffer that should be mutated. - - @type add_buf: bytearray - @param add_buf: A second buffer that can be used as mutation source. - - @type max_size: int - @param max_size: Maximum size of the mutated output. The mutation must not - produce data larger than max_size. - - @rtype: bytearray - @return: A new bytearray containing the mutated data - ''' - ret = bytearray(100) - - ret[:3] = random.choice(COMMANDS) - - return ret - -# Uncomment and implement the following methods if you want to use a custom -# trimming algorithm. See also the documentation for a better API description. - -# def init_trim(buf): -# ''' -# Called per trimming iteration. -# -# @type buf: bytearray -# @param buf: The buffer that should be trimmed. -# -# @rtype: int -# @return: The maximum number of trimming steps. -# ''' -# global ... -# -# # Initialize global variables -# -# # Figure out how many trimming steps are possible. -# # If this is not possible for your trimming, you can -# # return 1 instead and always return 0 in post_trim -# # until you are done (then you return 1). -# -# return steps -# -# def trim(): -# ''' -# Called per trimming iteration. -# -# @rtype: bytearray -# @return: A new bytearray containing the trimmed data. -# ''' -# global ... -# -# # Implement the actual trimming here -# -# return bytearray(...) -# -# def post_trim(success): -# ''' -# Called after each trimming operation. -# -# @type success: bool -# @param success: Indicates if the last trim operation was successful. -# -# @rtype: int -# @return: The next trim index (0 to max number of steps) where max -# number of steps indicates the trimming is done. -# ''' -# global ... -# -# if not success: -# # Restore last known successful input, determine next index -# else: -# # Just determine the next index, based on what was successfully -# # removed in the last step -# -# return next_index -# -# def post_process(buf): -# ''' -# Called just before the execution to write the test case in the format -# expected by the target -# -# @type buf: bytearray -# @param buf: The buffer containing the test case to be executed -# -# @rtype: bytearray -# @return: The buffer containing the test case after -# ''' -# return buf -# -# def havoc_mutation(buf, max_size): -# ''' -# Perform a single custom mutation on a given input. -# -# @type buf: bytearray -# @param buf: The buffer that should be mutated. -# -# @type max_size: int -# @param max_size: Maximum size of the mutated output. The mutation must not -# produce data larger than max_size. -# -# @rtype: bytearray -# @return: A new bytearray containing the mutated data -# ''' -# return mutated_buf -# -# def havoc_mutation_probability(): -# ''' -# Called for each `havoc_mutation`. Return the probability (in percentage) -# that `havoc_mutation` is called in havoc. Be default it is 6%. -# -# @rtype: int -# @return: The probability (0-100) -# ''' -# return prob -# -# def queue_get(filename): -# ''' -# Called at the beginning of each fuzz iteration to determine whether the -# test case should be fuzzed -# -# @type filename: str -# @param filename: File name of the test case in the current queue entry -# -# @rtype: bool -# @return: Return True if the custom mutator decides to fuzz the test case, -# and False otherwise -# ''' -# return True -# -# def queue_new_entry(filename_new_queue, filename_orig_queue): -# ''' -# Called after adding a new test case to the queue -# -# @type filename_new_queue: str -# @param filename_new_queue: File name of the new queue entry -# -# @type filename_orig_queue: str -# @param filename_orig_queue: File name of the original queue entry -# ''' -# pass diff --git a/examples/custom_mutators/post_library_gif.so.c b/examples/custom_mutators/post_library_gif.so.c deleted file mode 100644 index ac10f409..00000000 --- a/examples/custom_mutators/post_library_gif.so.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - american fuzzy lop++ - postprocessor library example - -------------------------------------------------- - - Originally written by Michal Zalewski - Edited by Dominik Maier, 2020 - - Copyright 2015 Google Inc. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - Postprocessor libraries can be passed to afl-fuzz to perform final cleanup - of any mutated test cases - for example, to fix up checksums in PNG files. - - Please heed the following warnings: - - 1) In almost all cases, it is more productive to comment out checksum logic - in the targeted binary (as shown in ../libpng_no_checksum/). One possible - exception is the process of fuzzing binary-only software in QEMU mode. - - 2) The use of postprocessors for anything other than checksums is - questionable and may cause more harm than good. AFL is normally pretty good - about dealing with length fields, magic values, etc. - - 3) Postprocessors that do anything non-trivial must be extremely robust to - gracefully handle malformed data and other error conditions - otherwise, - they will crash and take afl-fuzz down with them. Be wary of reading past - *len and of integer overflows when calculating file offsets. - - In other words, THIS IS PROBABLY NOT WHAT YOU WANT - unless you really, - honestly know what you're doing =) - - With that out of the way: the postprocessor library is passed to afl-fuzz - via AFL_POST_LIBRARY. The library must be compiled with: - - gcc -shared -Wall -O3 post_library.so.c -o post_library.so - - AFL will call the afl_custom_post_process() function for every mutated output - buffer. From there, you have three choices: - - 1) If you don't want to modify the test case, simply set `*out_buf = in_buf` - and return the original `len`. - - 2) If you want to skip this test case altogether and have AFL generate a - new one, return 0 or set `*out_buf = NULL`. - Use this sparingly - it's faster than running the target program - with patently useless inputs, but still wastes CPU time. - - 3) If you want to modify the test case, allocate an appropriately-sized - buffer, move the data into that buffer, make the necessary changes, and - then return the new pointer as out_buf. Return an appropriate len - afterwards. - - Note that the buffer will *not* be freed for you. To avoid memory leaks, - you need to free it or reuse it on subsequent calls (as shown below). - - *** Feel free to reuse the original 'in_buf' BUFFER and return it. *** - - Aight. The example below shows a simple postprocessor that tries to make - sure that all input files start with "GIF89a". - - PS. If you don't like C, you can try out the unix-based wrapper from - Ben Nagy instead: https://github.com/bnagy/aflfix - - */ - -#include -#include -#include - -/* Header that must be present at the beginning of every test case: */ - -#define HEADER "GIF89a" - -typedef struct post_state { - - unsigned char *buf; - size_t size; - -} post_state_t; - -void *afl_custom_init(void *afl) { - - post_state_t *state = malloc(sizeof(post_state_t)); - if (!state) { - - perror("malloc"); - return NULL; - - } - - state->buf = calloc(sizeof(unsigned char), 4096); - if (!state->buf) { - - free(state); - perror("calloc"); - return NULL; - - } - - return state; - -} - -/* The actual postprocessor routine called by afl-fuzz: */ - -size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf, - unsigned int len, unsigned char **out_buf) { - - /* Skip execution altogether for buffers shorter than 6 bytes (just to - show how it's done). We can trust len to be sane. */ - - if (len < strlen(HEADER)) return 0; - - /* Do nothing for buffers that already start with the expected header. */ - - if (!memcmp(in_buf, HEADER, strlen(HEADER))) { - - *out_buf = in_buf; - return len; - - } - - /* Allocate memory for new buffer, reusing previous allocation if - possible. */ - - *out_buf = realloc(data->buf, len); - - /* If we're out of memory, the most graceful thing to do is to return the - original buffer and give up on modifying it. Let AFL handle OOM on its - own later on. */ - - if (!*out_buf) { - - *out_buf = in_buf; - return len; - - } - - /* Copy the original data to the new location. */ - - memcpy(*out_buf, in_buf, len); - - /* Insert the new header. */ - - memcpy(*out_buf, HEADER, strlen(HEADER)); - - /* Return the new len. It hasn't changed, so it's just len. */ - - return len; - -} - -/* Gets called afterwards */ -void afl_custom_deinit(post_state_t *data) { - - free(data->buf); - free(data); - -} - diff --git a/examples/custom_mutators/post_library_png.so.c b/examples/custom_mutators/post_library_png.so.c deleted file mode 100644 index 941f7e55..00000000 --- a/examples/custom_mutators/post_library_png.so.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - american fuzzy lop++ - postprocessor for PNG - ------------------------------------------ - - Originally written by Michal Zalewski - - Copyright 2015 Google Inc. All rights reserved. - Adapted to the new API, 2020 by Dominik Maier - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - See post_library.so.c for a general discussion of how to implement - postprocessors. This specific postprocessor attempts to fix up PNG - checksums, providing a slightly more complicated example than found - in post_library.so.c. - - Compile with: - - gcc -shared -Wall -O3 post_library_png.so.c -o post_library_png.so -lz - - */ - -#include -#include -#include -#include -#include - -#include - -/* A macro to round an integer up to 4 kB. */ - -#define UP4K(_i) ((((_i) >> 12) + 1) << 12) - -typedef struct post_state { - - unsigned char *buf; - size_t size; - -} post_state_t; - -void *afl_custom_init(void *afl) { - - post_state_t *state = malloc(sizeof(post_state_t)); - if (!state) { - - perror("malloc"); - return NULL; - - } - - state->buf = calloc(sizeof(unsigned char), 4096); - if (!state->buf) { - - free(state); - perror("calloc"); - return NULL; - - } - - return state; - -} - -size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf, - unsigned int len, - const unsigned char **out_buf) { - - unsigned char *new_buf = (unsigned char *)in_buf; - unsigned int pos = 8; - - /* Don't do anything if there's not enough room for the PNG header - (8 bytes). */ - - if (len < 8) { - - *out_buf = in_buf; - return len; - - } - - /* Minimum size of a zero-length PNG chunk is 12 bytes; if we - don't have that, we can bail out. */ - - while (pos + 12 <= len) { - - unsigned int chunk_len, real_cksum, file_cksum; - - /* Chunk length is the first big-endian dword in the chunk. */ - - chunk_len = ntohl(*(uint32_t *)(in_buf + pos)); - - /* Bail out if chunk size is too big or goes past EOF. */ - - if (chunk_len > 1024 * 1024 || pos + 12 + chunk_len > len) break; - - /* Chunk checksum is calculated for chunk ID (dword) and the actual - payload. */ - - real_cksum = htonl(crc32(0, in_buf + pos + 4, chunk_len + 4)); - - /* The in-file checksum is the last dword past the chunk data. */ - - file_cksum = *(uint32_t *)(in_buf + pos + 8 + chunk_len); - - /* If the checksums do not match, we need to fix the file. */ - - if (real_cksum != file_cksum) { - - /* First modification? Make a copy of the input buffer. Round size - up to 4 kB to minimize the number of reallocs needed. */ - - if (new_buf == in_buf) { - - if (len <= data->size) { - - new_buf = data->buf; - - } else { - - new_buf = realloc(data->buf, UP4K(len)); - if (!new_buf) { - - *out_buf = in_buf; - return len; - - } - - data->buf = new_buf; - data->size = UP4K(len); - memcpy(new_buf, in_buf, len); - - } - - } - - *(uint32_t *)(new_buf + pos + 8 + chunk_len) = real_cksum; - - } - - /* Skip the entire chunk and move to the next one. */ - - pos += 12 + chunk_len; - - } - - *out_buf = new_buf; - return len; - -} - -/* Gets called afterwards */ -void afl_custom_deinit(post_state_t *data) { - - free(data->buf); - free(data); - -} - diff --git a/examples/custom_mutators/simple-chunk-replace.py b/examples/custom_mutators/simple-chunk-replace.py deleted file mode 100644 index df2f4ca7..00000000 --- a/examples/custom_mutators/simple-chunk-replace.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 -''' -Simple Chunk Cross-Over Replacement Module for AFLFuzz - -@author: Christian Holler (:decoder) - -@license: - -This Source Code Form is subject to the terms of the Mozilla Public -License, v. 2.0. If a copy of the MPL was not distributed with this -file, You can obtain one at http://mozilla.org/MPL/2.0/. - -@contact: choller@mozilla.com -''' - -import random - - -def init(seed): - ''' - Called once when AFLFuzz starts up. Used to seed our RNG. - - @type seed: int - @param seed: A 32-bit random value - ''' - # Seed our RNG - random.seed(seed) - - -def fuzz(buf, add_buf, max_size): - ''' - Called per fuzzing iteration. - - @type buf: bytearray - @param buf: The buffer that should be mutated. - - @type add_buf: bytearray - @param add_buf: A second buffer that can be used as mutation source. - - @type max_size: int - @param max_size: Maximum size of the mutated output. The mutation must not - produce data larger than max_size. - - @rtype: bytearray - @return: A new bytearray containing the mutated data - ''' - # Make a copy of our input buffer for returning - ret = bytearray(buf) - - # Take a random fragment length between 2 and 32 (or less if add_buf is shorter) - fragment_len = random.randint(1, min(len(add_buf), 32)) - - # Determine a random source index where to take the data chunk from - rand_src_idx = random.randint(0, len(add_buf) - fragment_len) - - # Determine a random destination index where to put the data chunk - rand_dst_idx = random.randint(0, len(buf)) - - # Make the chunk replacement - ret[rand_dst_idx:rand_dst_idx + fragment_len] = add_buf[rand_src_idx:rand_src_idx + fragment_len] - - # Return data - return ret diff --git a/examples/custom_mutators/simple_example.c b/examples/custom_mutators/simple_example.c deleted file mode 100644 index d888ec1f..00000000 --- a/examples/custom_mutators/simple_example.c +++ /dev/null @@ -1,74 +0,0 @@ -// This simple example just creates random buffer <= 100 filled with 'A' -// needs -I /path/to/AFLplusplus/include -#include "custom_mutator_helpers.h" - -#include -#include -#include -#include - -#ifndef _FIXED_CHAR - #define _FIXED_CHAR 0x41 -#endif - -typedef struct my_mutator { - - afl_t *afl; - - // Reused buffers: - BUF_VAR(u8, fuzz); - -} my_mutator_t; - -my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { - - srand(seed); - my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); - if (!data) { - - perror("afl_custom_init alloc"); - return NULL; - - } - - data->afl = afl; - - return data; - -} - -size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, - u8 **out_buf, uint8_t *add_buf, - size_t add_buf_size, // add_buf can be NULL - size_t max_size) { - - int size = (rand() % 100) + 1; - if (size > max_size) size = max_size; - u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), size); - if (!mutated_out) { - - *out_buf = NULL; - perror("custom mutator allocation (maybe_grow)"); - return 0; /* afl-fuzz will very likely error out after this. */ - - } - - memset(mutated_out, _FIXED_CHAR, size); - - *out_buf = mutated_out; - return size; - -} - -/** - * Deinitialize everything - * - * @param data The data ptr from afl_custom_init - */ -void afl_custom_deinit(my_mutator_t *data) { - - free(data->fuzz_buf); - free(data); - -} - diff --git a/examples/custom_mutators/wrapper_afl_min.py b/examples/custom_mutators/wrapper_afl_min.py deleted file mode 100644 index ecb03b55..00000000 --- a/examples/custom_mutators/wrapper_afl_min.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python - -from XmlMutatorMin import XmlMutatorMin - -# Default settings (production mode) - -__mutator__ = None -__seed__ = "RANDOM" -__log__ = False -__log_file__ = "wrapper.log" - - -# AFL functions -def log(text): - """ - Logger - """ - - global __seed__ - global __log__ - global __log_file__ - - if __log__: - with open(__log_file__, "a") as logf: - logf.write("[%s] %s\n" % (__seed__, text)) - - -def init(seed): - """ - Called once when AFL starts up. Seed is used to identify the AFL instance in log files - """ - - global __mutator__ - global __seed__ - - # Get the seed - __seed__ = seed - - # Create a global mutation class - try: - __mutator__ = XmlMutatorMin(__seed__, verbose=__log__) - log("init(): Mutator created") - except RuntimeError as e: - log("init(): Can't create mutator: %s" % e.message) - - -def fuzz(buf, add_buf, max_size): - """ - Called for each fuzzing iteration. - """ - - global __mutator__ - - # Do we have a working mutator object? - if __mutator__ is None: - log("fuzz(): Can't fuzz, no mutator available") - return buf - - # Try to use the AFL buffer - via_buffer = True - - # Interpret the AFL buffer (an array of bytes) as a string - if via_buffer: - try: - buf_str = str(buf) - log("fuzz(): AFL buffer converted to a string") - except Exception: - via_buffer = False - log("fuzz(): Can't convert AFL buffer to a string") - - # Load XML from the AFL string - if via_buffer: - try: - __mutator__.init_from_string(buf_str) - log("fuzz(): Mutator successfully initialized with AFL buffer (%d bytes)" % len(buf_str)) - except Exception: - via_buffer = False - log("fuzz(): Can't initialize mutator with AFL buffer") - - # If init from AFL buffer wasn't succesful - if not via_buffer: - log("fuzz(): Returning unmodified AFL buffer") - return buf - - # Sucessful initialization -> mutate - try: - __mutator__.mutate(max=5) - log("fuzz(): Input mutated") - except Exception: - log("fuzz(): Can't mutate input => returning buf") - return buf - - # Convert mutated data to a array of bytes - try: - data = bytearray(__mutator__.save_to_string()) - log("fuzz(): Mutated data converted as bytes") - except Exception: - log("fuzz(): Can't convert mutated data to bytes => returning buf") - return buf - - # Everything went fine, returning mutated content - log("fuzz(): Returning %d bytes" % len(data)) - return data - - -# Main (for debug) -if __name__ == '__main__': - - __log__ = True - __log_file__ = "/dev/stdout" - __seed__ = "RANDOM" - - init(__seed__) - - in_1 = bytearray("ffff
    zzzzzzzzzzzz") - in_2 = bytearray("") - out = fuzz(in_1, in_2) - print(out) diff --git a/examples/defork/Makefile b/examples/defork/Makefile deleted file mode 100644 index e8240dba..00000000 --- a/examples/defork/Makefile +++ /dev/null @@ -1,64 +0,0 @@ -# -# american fuzzy lop++ - defork -# ---------------------------------- -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -.PHONY: all install clean - -PREFIX ?= /usr/local -BIN_PATH = $(PREFIX)/bin -HELPER_PATH = $(PREFIX)/lib/afl - -CFLAGS = -fPIC -Wall -Wextra -LDFLAGS = -shared - -UNAME_SAYS_LINUX=$(shell uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?) -UNAME_SAYS_LINUX:sh=uname | grep -E '^Linux|^GNU' >/dev/null; echo $$? - -_LDFLAGS_ADD=$(UNAME_SAYS_LINUX:1=) -LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-ldl) -LDFLAGS += $(LDFLAGS_ADD) - -# on gcc for arm there is no -m32, but -mbe32 -M32FLAG = -m32 -M64FLAG = -m64 - -CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?) -CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$? -CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?) -CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$? - -_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER) -__M32FLAG=$(_M32FLAG:00=-mbe32) -___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32) -M32FLAG=$(___M32FLAG) -#ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" -# ifneq (,$(findstring arm, "$(shell $(CC) -v 2>&1 >/dev/null)")) -# M32FLAG = -mbe32 -# endif -#endif - -all: defork32.so defork64.so - -defork32.so: defork.c - -@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "defork32 build failure (that's fine)" - -defork64.so: defork.c - -@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "defork64 build failure (that's fine)" - -install: defork32.so defork64.so - install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ - if [ -f defork32.so ]; then set -e; install -m 755 defork32.so $(DESTDIR)$(HELPER_PATH)/; fi - if [ -f defork64.so ]; then set -e; install -m 755 defork64.so $(DESTDIR)$(HELPER_PATH)/; fi - -target: - ../../afl-clang forking_target.c -o forking_target -Wall -Wextra -Werror - -clean: - rm -f defork32.so defork64.so forking_target diff --git a/examples/defork/README.md b/examples/defork/README.md deleted file mode 100644 index 7e950323..00000000 --- a/examples/defork/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# defork - -when the target forks, this breaks all normal fuzzing runs. -Sometimes, though, it is enough to just run the child process. -If this is the case, then this LD_PRELOAD library will always return 0 on fork, -the target will belive it is running as the child, post-fork. - -This is defork.c from the amazing preeny project -https://github.com/zardus/preeny - -It is altered for afl++ to work with its fork-server: the initial fork will go through, the second fork will be blocked. diff --git a/examples/defork/defork.c b/examples/defork/defork.c deleted file mode 100644 index f71d1124..00000000 --- a/examples/defork/defork.c +++ /dev/null @@ -1,50 +0,0 @@ -#define __GNU_SOURCE -#include -#include -#include -#include - -#include "../../include/config.h" - -/* we want to fork once (for the afl++ forkserver), - then immediately return as child on subsequent forks. */ -static bool forked = 0; - -pid_t (*original_fork)(void); - -/* In case we are not running in afl, we use a dummy original_fork */ -static pid_t nop(void) { - - return 0; - -} - -__attribute__((constructor)) void preeny_fork_orig() { - - if (getenv(SHM_ENV_VAR)) { - - printf("defork: running in AFL++. Allowing forkserver.\n"); - original_fork = dlsym(RTLD_NEXT, "socket"); - - } else { - - printf("defork: no AFL++ detected. Disabling fork from the start.\n"); - original_fork = &nop; - - } - -} - -pid_t fork(void) { - - /* If we forked before, or if we're in the child (pid==0), - we don't want to fork anymore, else, we are still in the forkserver. - The forkserver parent needs to fork infinite times, each child should never - fork again. This can be written without branches and I hate myself for it. - */ - pid_t ret = !forked && original_fork(); - forked = !ret; - return ret; - -} - diff --git a/examples/defork/forking_target.c b/examples/defork/forking_target.c deleted file mode 100644 index 628d23c9..00000000 --- a/examples/defork/forking_target.c +++ /dev/null @@ -1,49 +0,0 @@ -#include -#include -#include -#include - -/* This is an example target for defork.c - fuzz using -``` -mkdir in; echo a > ./in/a -AFL_PRELOAD=./defork64.so ../../afl-fuzz -i in -o out -- ./forking_target @@ -``` -*/ - -int main(int argc, char **argv) { - - if (argc < 2) { - - printf("Example tool to test defork.\nUsage ./forking_target \n"); - return -1; - - } - - pid_t pid = fork(); - if (pid == 0) { - - printf("We're in the child.\n"); - FILE *f = fopen(argv[1], "r"); - char buf[4096]; - fread(buf, 1, 4096, f); - fclose(f); - uint32_t offset = buf[100] + (buf[101] << 8); - char test_val = buf[offset]; - return test_val < 100; - - } else if (pid < 0) { - - perror("fork"); - return -1; - - } else { - - printf("We are in the parent - defork didn't work! :( (pid=%d)\n", - (int)pid); - - } - - return 0; - -} - diff --git a/examples/distributed_fuzzing/sync_script.sh b/examples/distributed_fuzzing/sync_script.sh deleted file mode 100755 index b28ff6cd..00000000 --- a/examples/distributed_fuzzing/sync_script.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/sh -# -# american fuzzy lop++ - fuzzer synchronization tool -# -------------------------------------------------- -# -# Originally written by Michal Zalewski -# -# Copyright 2014 Google Inc. All rights reserved. -# Copyright 2019-2020 AFLplusplus Project. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# To make this script work: -# -# - Edit FUZZ_HOSTS, FUZZ_DOMAIN, FUZZ_USER, and SYNC_DIR to reflect your -# environment. -# -# - Make sure that the system you are running this on can log into FUZZ_HOSTS -# without a password (authorized_keys or otherwise). -# -# - Make sure that every fuzzer is running with -o pointing to SYNC_DIR and -S -# that consists of its local host name, followed by an underscore, and then -# by some host-local fuzzer ID. -# - -# Hosts to synchronize the data across. -FUZZ_HOSTS='host1 host2 host3 host4' - -# Domain for all hosts -FUZZ_DOMAIN='example.com' - -# Remote user for SSH -FUZZ_USER=bob - -# Directory to synchronize -SYNC_DIR='/home/bob/sync_dir' - -# We only capture -M main nodes, set the name to your chosen naming scheme -MAIN_NAME='main' - -# Interval (seconds) between sync attempts (eg one hour) -SYNC_INTERVAL=$((60 * 60)) - -if [ "$AFL_ALLOW_TMP" = "" ]; then - - if [ "$PWD" = "/tmp" -o "$PWD" = "/var/tmp" ]; then - echo "[-] Error: do not use shared /tmp or /var/tmp directories with this script." 1>&2 - exit 1 - fi - -fi - -rm -rf .sync_tmp 2>/dev/null -mkdir .sync_tmp || exit 1 - -while :; do - - # Pull data in... - - for host in $FUZZ_HOSTS; do - - echo "[*] Retrieving data from ${host}.${FUZZ_DOMAIN}..." - - ssh -o 'passwordauthentication no' ${FUZZ_USER}@${host}.$FUZZ_DOMAIN \ - "cd '$SYNC_DIR' && tar -czf - ${host}_${MAIN_NAME}*/" > ".sync_tmp/${host}.tgz" - - done - - # Distribute data. For large fleets, see tips in the docs/ directory. - - for dst_host in $FUZZ_HOSTS; do - - echo "[*] Distributing data to ${dst_host}.${FUZZ_DOMAIN}..." - - for src_host in $FUZZ_HOSTS; do - - test "$src_host" = "$dst_host" && continue - - echo " Sending fuzzer data from ${src_host}.${FUZZ_DOMAIN}..." - - ssh -o 'passwordauthentication no' ${FUZZ_USER}@$dst_host \ - "cd '$SYNC_DIR' && tar -xkzf - " < ".sync_tmp/${src_host}.tgz" - - done - - done - - echo "[+] Done. Sleeping for $SYNC_INTERVAL seconds (Ctrl-C to quit)." - - sleep $SYNC_INTERVAL - -done - diff --git a/examples/libpng_no_checksum/libpng-nocrc.patch b/examples/libpng_no_checksum/libpng-nocrc.patch deleted file mode 100644 index 0a3793a0..00000000 --- a/examples/libpng_no_checksum/libpng-nocrc.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- pngrutil.c.orig 2014-06-12 03:35:16.000000000 +0200 -+++ pngrutil.c 2014-07-01 05:08:31.000000000 +0200 -@@ -268,7 +268,11 @@ - if (need_crc != 0) - { - crc = png_get_uint_32(crc_bytes); -- return ((int)(crc != png_ptr->crc)); -+ -+ if (crc != png_ptr->crc) -+ fprintf(stderr, "NOTE: CRC in the file is 0x%08x, change to 0x%08x\n", crc, png_ptr->crc); -+ -+ return ((int)(1 != 1)); - } - - else diff --git a/examples/persistent_mode/Makefile b/examples/persistent_mode/Makefile deleted file mode 100644 index 6fa1c30e..00000000 --- a/examples/persistent_mode/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: - afl-clang-fast -o persistent_demo persistent_demo.c - afl-clang-fast -o persistent_demo_new persistent_demo_new.c - AFL_DONT_OPTIMIZE=1 afl-clang-fast -o test-instr test-instr.c - -document: - AFL_DONT_OPTIMIZE=1 afl-clang-fast -D_AFL_DOCUMENT_MUTATIONS -o test-instr test-instr.c - -clean: - rm -f persistent_demo persistent_demo_new test-instr diff --git a/examples/persistent_mode/persistent_demo.c b/examples/persistent_mode/persistent_demo.c deleted file mode 100644 index 4cedc32c..00000000 --- a/examples/persistent_mode/persistent_demo.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - american fuzzy lop++ - persistent mode example - -------------------------------------------- - - Originally written by Michal Zalewski - - Copyright 2015 Google Inc. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This file demonstrates the high-performance "persistent mode" that may be - suitable for fuzzing certain fast and well-behaved libraries, provided that - they are stateless or that their internal state can be easily reset - across runs. - - To make this work, the library and this shim need to be compiled in LLVM - mode using afl-clang-fast (other compiler wrappers will *not* work). - - */ - -#include -#include -#include -#include -#include - -/* Main entry point. */ - -int main(int argc, char **argv) { - - ssize_t len; /* how much input did we read? */ - char buf[100]; /* Example-only buffer, you'd replace it with other global or - local variables appropriate for your use case. */ - - /* The number passed to __AFL_LOOP() controls the maximum number of - iterations before the loop exits and the program is allowed to - terminate normally. This limits the impact of accidental memory leaks - and similar hiccups. */ - - __AFL_INIT(); - while (__AFL_LOOP(1000)) { - - /*** PLACEHOLDER CODE ***/ - - /* STEP 1: Fully re-initialize all critical variables. In our example, this - involves zeroing buf[], our input buffer. */ - - memset(buf, 0, 100); - - /* STEP 2: Read input data. When reading from stdin, no special preparation - is required. When reading from a named file, you need to close - the old descriptor and reopen the file first! - - Beware of reading from buffered FILE* objects such as stdin. Use - raw file descriptors or call fopen() / fdopen() in every pass. */ - - len = read(0, buf, 100); - - /* STEP 3: This is where we'd call the tested library on the read data. - We just have some trivial inline code that faults on 'foo!'. */ - - /* do we have enough data? */ - if (len < 8) continue; - - if (buf[0] == 'f') { - - printf("one\n"); - if (buf[1] == 'o') { - - printf("two\n"); - if (buf[2] == 'o') { - - printf("three\n"); - if (buf[3] == '!') { - - printf("four\n"); - if (buf[4] == '!') { - - printf("five\n"); - if (buf[5] == '!') { - - printf("six\n"); - abort(); - - } - - } - - } - - } - - } - - } - - /*** END PLACEHOLDER CODE ***/ - - } - - /* Once the loop is exited, terminate normally - AFL will restart the process - when this happens, with a clean slate when it comes to allocated memory, - leftover file descriptors, etc. */ - - return 0; - -} - diff --git a/examples/persistent_mode/persistent_demo_new.c b/examples/persistent_mode/persistent_demo_new.c deleted file mode 100644 index a29792ff..00000000 --- a/examples/persistent_mode/persistent_demo_new.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - american fuzzy lop++ - persistent mode example - -------------------------------------------- - - Originally written by Michal Zalewski - - Copyright 2015 Google Inc. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This file demonstrates the high-performance "persistent mode" that may be - suitable for fuzzing certain fast and well-behaved libraries, provided that - they are stateless or that their internal state can be easily reset - across runs. - - To make this work, the library and this shim need to be compiled in LLVM - mode using afl-clang-fast (other compiler wrappers will *not* work). - - */ - -#include -#include -#include -#include -#include - -/* this lets the source compile without afl-clang-fast/lto */ -#ifndef __AFL_FUZZ_TESTCASE_LEN - -ssize_t fuzz_len; -unsigned char fuzz_buf[1024000]; - - #define __AFL_FUZZ_TESTCASE_LEN fuzz_len - #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf - #define __AFL_FUZZ_INIT() void sync(void); - #define __AFL_LOOP(x) \ - ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0) - #define __AFL_INIT() sync() - -#endif - -__AFL_FUZZ_INIT(); - -/* Main entry point. */ - -int main(int argc, char **argv) { - - ssize_t len; /* how much input did we read? */ - unsigned char *buf; /* test case buffer pointer */ - - /* The number passed to __AFL_LOOP() controls the maximum number of - iterations before the loop exits and the program is allowed to - terminate normally. This limits the impact of accidental memory leaks - and similar hiccups. */ - - __AFL_INIT(); - buf = __AFL_FUZZ_TESTCASE_BUF; // this must be assigned before __AFL_LOOP! - - while (__AFL_LOOP(1000)) { // increase if you have good stability - - len = __AFL_FUZZ_TESTCASE_LEN; // do not use the macro directly in a call! - - fprintf(stderr, "input: %zd \"%s\"\n", len, buf); - - /* do we have enough data? */ - if (len < 8) continue; - - if (strcmp((char *)buf, "thisisateststring") == 0) printf("teststring\n"); - - if (buf[0] == 'f') { - - printf("one\n"); - if (buf[1] == 'o') { - - printf("two\n"); - if (buf[2] == 'o') { - - printf("three\n"); - if (buf[3] == '!') { - - printf("four\n"); - if (buf[4] == '!') { - - printf("five\n"); - if (buf[6] == '!') { - - printf("six\n"); - abort(); - - } - - } - - } - - } - - } - - } - - /*** END PLACEHOLDER CODE ***/ - - } - - /* Once the loop is exited, terminate normally - AFL will restart the process - when this happens, with a clean slate when it comes to allocated memory, - leftover file descriptors, etc. */ - - return 0; - -} - diff --git a/examples/persistent_mode/test-instr.c b/examples/persistent_mode/test-instr.c deleted file mode 100644 index a6188b22..00000000 --- a/examples/persistent_mode/test-instr.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - american fuzzy lop++ - a trivial program to test the build - -------------------------------------------------------- - Originally written by Michal Zalewski - Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - http://www.apache.org/licenses/LICENSE-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include - -__AFL_FUZZ_INIT(); - -int main(int argc, char **argv) { - - __AFL_INIT(); - unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; - - while (__AFL_LOOP(2147483647)) { // MAX_INT if you have 100% stability - - unsigned int len = __AFL_FUZZ_TESTCASE_LEN; - -#ifdef _AFL_DOCUMENT_MUTATIONS - static unsigned int counter = 0; - char fn[32]; - sprintf(fn, "%09u:test-instr", counter); - int fd_doc = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (fd_doc >= 0) { - - if (write(fd_doc, buf, len) != __afl_fuzz_len) { - - fprintf(stderr, "write of mutation file failed: %s\n", fn); - unlink(fn); - - } - - close(fd_doc); - - } - - counter++; -#endif - - // fprintf(stderr, "len: %u\n", len); - - if (!len) continue; - - if (buf[0] == '0') - printf("Looks like a zero to me!\n"); - else if (buf[0] == '1') - printf("Pretty sure that is a one!\n"); - else - printf("Neither one or zero? How quaint!\n"); - - } - - return 0; - -} - diff --git a/examples/qemu_persistent_hook/Makefile b/examples/qemu_persistent_hook/Makefile deleted file mode 100644 index 85db1b46..00000000 --- a/examples/qemu_persistent_hook/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -all: - $(CC) -no-pie test.c -o test - $(CC) -fPIC -shared read_into_rdi.c -o read_into_rdi.so - -clean: - rm -rf in out test read_into_rdi.so diff --git a/examples/qemu_persistent_hook/README.md b/examples/qemu_persistent_hook/README.md deleted file mode 100644 index 3f908c22..00000000 --- a/examples/qemu_persistent_hook/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# QEMU persistent hook example - -Compile the test binary and the library: - -``` -make -``` - -Fuzz with: - -``` -export AFL_QEMU_PERSISTENT_ADDR=0x$(nm test | grep "T target_func" | awk '{print $1}') -export AFL_QEMU_PERSISTENT_HOOK=./read_into_rdi.so - -mkdir in -echo 0000 > in/in - -../../afl-fuzz -Q -i in -o out -- ./test -``` diff --git a/examples/qemu_persistent_hook/read_into_rdi.c b/examples/qemu_persistent_hook/read_into_rdi.c deleted file mode 100644 index f4a8ae59..00000000 --- a/examples/qemu_persistent_hook/read_into_rdi.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "../../qemu_mode/qemuafl/qemuafl/api.h" - -#include -#include - -void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base, - uint8_t *input_buf, uint32_t input_buf_len) { -\ -#define g2h(x) ((void *)((unsigned long)(x) + guest_base)) -#define h2g(x) ((uint64_t)(x)-guest_base) - - // In this example the register RDI is pointing to the memory location - // of the target buffer, and the length of the input is in RSI. - // This can be seen with a debugger, e.g. gdb (and "disass main") - - printf("Placing input into 0x%lx\n", regs->rdi); - - if (input_buf_len > 1024) input_buf_len = 1024; - memcpy(g2h(regs->rdi), input_buf, input_buf_len); - regs->rsi = input_buf_len; - -#undef g2h -#undef h2g - -} - -int afl_persistent_hook_init(void) { - - // 1 for shared memory input (faster), 0 for normal input (you have to use - // read(), input_buf will be NULL) - return 1; - -} - diff --git a/examples/qemu_persistent_hook/test.c b/examples/qemu_persistent_hook/test.c deleted file mode 100644 index afeff202..00000000 --- a/examples/qemu_persistent_hook/test.c +++ /dev/null @@ -1,35 +0,0 @@ -#include - -int target_func(unsigned char *buf, int size) { - - printf("buffer:%p, size:%p\n", buf, size); - switch (buf[0]) { - - case 1: - if (buf[1] == '\x44') { puts("a"); } - break; - case 0xff: - if (buf[2] == '\xff') { - - if (buf[1] == '\x44') { puts("b"); } - - } - - break; - default: - break; - - } - - return 1; - -} - -char data[1024]; - -int main() { - - target_func(data, 1024); - -} - diff --git a/examples/socket_fuzzing/Makefile b/examples/socket_fuzzing/Makefile deleted file mode 100644 index 9476e2d5..00000000 --- a/examples/socket_fuzzing/Makefile +++ /dev/null @@ -1,61 +0,0 @@ -# -# american fuzzy lop++ - socket_fuzz -# ---------------------------------- -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -.PHONY: all install clean - -PREFIX ?= /usr/local -BIN_PATH = $(PREFIX)/bin -HELPER_PATH = $(PREFIX)/lib/afl - -CFLAGS = -fPIC -Wall -Wextra -LDFLAGS = -shared - -UNAME_SAYS_LINUX=$(shell uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?) -UNAME_SAYS_LINUX:sh=uname | grep -E '^Linux|^GNU' >/dev/null; echo $$? - -_LDFLAGS_ADD=$(UNAME_SAYS_LINUX:1=) -LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-ldl) -LDFLAGS += $(LDFLAGS_ADD) - -# on gcc for arm there is no -m32, but -mbe32 -M32FLAG = -m32 -M64FLAG = -m64 - -CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?) -CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$? -CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?) -CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$? - -_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER) -__M32FLAG=$(_M32FLAG:00=-mbe32) -___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32) -M32FLAG=$(___M32FLAG) -#ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" -# ifneq (,$(findstring arm, "$(shell $(CC) -v 2>&1 >/dev/null)")) -# M32FLAG = -mbe32 -# endif -#endif - -all: socketfuzz32.so socketfuzz64.so - -socketfuzz32.so: socketfuzz.c - -@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "socketfuzz32 build failure (that's fine)" - -socketfuzz64.so: socketfuzz.c - -@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "socketfuzz64 build failure (that's fine)" - -install: socketfuzz32.so socketfuzz64.so - install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ - if [ -f socketfuzz32.so ]; then set -e; install -m 755 socketfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi - if [ -f socketfuzz64.so ]; then set -e; install -m 755 socketfuzz64.so $(DESTDIR)$(HELPER_PATH)/; fi - -clean: - rm -f socketfuzz32.so socketfuzz64.so diff --git a/examples/socket_fuzzing/README.md b/examples/socket_fuzzing/README.md deleted file mode 100644 index 79f28bea..00000000 --- a/examples/socket_fuzzing/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# socketfuzz - -when you want to fuzz a network service and you can not/do not want to modify -the source (or just have a binary), then this LD_PRELOAD library will allow -for sending input to stdin which the target binary will think is coming from -a network socket. - -This is desock_dup.c from the amazing preeny project -https://github.com/zardus/preeny - -It is packaged in afl++ to have it at hand if needed diff --git a/examples/socket_fuzzing/socketfuzz.c b/examples/socket_fuzzing/socketfuzz.c deleted file mode 100644 index 3ec8383b..00000000 --- a/examples/socket_fuzzing/socketfuzz.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This is desock_dup.c from the amazing preeny project - * https://github.com/zardus/preeny - * - * It is packaged in afl++ to have it at hand if needed - * - */ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include // -#include // -#include // -#include // -#include -#include -#include -#include -#include -#include -#include -//#include "logging.h" // switche from preeny_info() to fprintf(stderr, "Info: " - -// -// originals -// -int (*original_close)(int); -int (*original_dup2)(int, int); -__attribute__((constructor)) void preeny_desock_dup_orig() { - - original_close = dlsym(RTLD_NEXT, "close"); - original_dup2 = dlsym(RTLD_NEXT, "dup2"); - -} - -int close(int sockfd) { - - if (sockfd <= 2) { - - fprintf(stderr, "Info: Disabling close on %d\n", sockfd); - return 0; - - } else { - - return original_close(sockfd); - - } - -} - -int dup2(int old, int new) { - - if (new <= 2) { - - fprintf(stderr, "Info: Disabling dup from %d to %d\n", old, new); - return 0; - - } else { - - return original_dup2(old, new); - - } - -} - -int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { - - (void)sockfd; - (void)addr; - (void)addrlen; - fprintf(stderr, "Info: Emulating accept on %d\n", sockfd); - return 0; - -} - -int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { - - (void)sockfd; - (void)addr; - (void)addrlen; - fprintf(stderr, "Info: Emulating bind on port %d\n", - ntohs(((struct sockaddr_in *)addr)->sin_port)); - return 0; - -} - -int listen(int sockfd, int backlog) { - - (void)sockfd; - (void)backlog; - return 0; - -} - -int setsockopt(int sockfd, int level, int optid, const void *optdata, - socklen_t optdatalen) { - - (void)sockfd; - (void)level; - (void)optid; - (void)optdata; - (void)optdatalen; - return 0; - -} - diff --git a/instrumentation/README.gcc_plugin.md b/instrumentation/README.gcc_plugin.md index 6ccb5fd3..12449efd 100644 --- a/instrumentation/README.gcc_plugin.md +++ b/instrumentation/README.gcc_plugin.md @@ -147,7 +147,7 @@ The numerical value specified within the loop controls the maximum number of iterations before AFL will restart the process from scratch. This minimizes the impact of memory leaks and similar glitches; 1000 is a good starting point. -A more detailed template is shown in ../examples/persistent_mode/. +A more detailed template is shown in ../utils/persistent_mode/. Similarly to the previous mode, the feature works only with afl-gcc-fast or afl-clang-fast; #ifdef guards can be used to suppress it when using other compilers. diff --git a/instrumentation/README.persistent_mode.md b/instrumentation/README.persistent_mode.md index 49f5ee8b..2cf76adf 100644 --- a/instrumentation/README.persistent_mode.md +++ b/instrumentation/README.persistent_mode.md @@ -11,7 +11,7 @@ and that its state can be resetted so that multiple calls can be performed without resource leaks and former runs having no impact on following runs (this can be seen by the `stability` indicator in the `afl-fuzz` UI). -Examples can be found in [examples/persistent_mode](../examples/persistent_mode). +Examples can be found in [utils/persistent_mode](../utils/persistent_mode). ## 2) TLDR; @@ -150,7 +150,7 @@ the impact of memory leaks and similar glitches; 1000 is a good starting point, and going much higher increases the likelihood of hiccups without giving you any real performance benefits. -A more detailed template is shown in ../examples/persistent_mode/. +A more detailed template is shown in ../utils/persistent_mode/. Similarly to the previous mode, the feature works only with afl-clang-fast; #ifdef guards can be used to suppress it when using other compilers. diff --git a/qemu_mode/README.md b/qemu_mode/README.md index 1c5d240c..58e48e91 100644 --- a/qemu_mode/README.md +++ b/qemu_mode/README.md @@ -179,7 +179,7 @@ match. ## 12) Gotchas, feedback, bugs If you need to fix up checksums or do other cleanup on mutated test cases, see -examples/custom_mutators/ for a viable solution. +utils/custom_mutators/ for a viable solution. Do not mix QEMU mode with ASAN, MSAN, or the likes; QEMU doesn't appreciate the "shadow VM" trick employed by the sanitizers and will probably just diff --git a/qemu_mode/README.persistent.md b/qemu_mode/README.persistent.md index d9e7e1cc..2ca5c873 100644 --- a/qemu_mode/README.persistent.md +++ b/qemu_mode/README.persistent.md @@ -172,4 +172,4 @@ and so the input_buf variables of the hook becomes meaningful. Otherwise, you have to read the input from a file like stdin. An example that you can use with little modification for your target can -be found here: [examples/qemu_persistent_hook](../examples/qemu_persistent_hook) +be found here: [utils/qemu_persistent_hook](../utils/qemu_persistent_hook) diff --git a/src/afl-as.c b/src/afl-as.c index 7d70bfcd..3d6f7d5e 100644 --- a/src/afl-as.c +++ b/src/afl-as.c @@ -27,7 +27,7 @@ utility has right now is to be able to skip them gracefully and allow the compilation process to continue. - That said, see examples/clang_asm_normalize/ for a solution that may + That said, see utils/clang_asm_normalize/ for a solution that may allow clang users to make things work even with hand-crafted assembly. Just note that there is no equivalent for GCC. diff --git a/test/test-custom-mutators.sh b/test/test-custom-mutators.sh index d4d21048..24c95ac7 100755 --- a/test/test-custom-mutators.sh +++ b/test/test-custom-mutators.sh @@ -5,7 +5,7 @@ $ECHO "$BLUE[*] Testing: custom mutator" test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { # normalize path - CUSTOM_MUTATOR_PATH=$(cd $(pwd)/../examples/custom_mutators;pwd) + CUSTOM_MUTATOR_PATH=$(cd $(pwd)/../utils/custom_mutators;pwd) test -e test-custom-mutator.c -a -e ${CUSTOM_MUTATOR_PATH}/example.c -a -e ${CUSTOM_MUTATOR_PATH}/example.py && { unset AFL_CC # Compile the vulnerable program for single mutator @@ -29,8 +29,8 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { } } # Compile the custom mutator - cc -D_FIXED_CHAR=0x41 -g -fPIC -shared -I../include ../examples/custom_mutators/simple_example.c -o libexamplemutator.so > /dev/null 2>&1 - cc -D_FIXED_CHAR=0x42 -g -fPIC -shared -I../include ../examples/custom_mutators/simple_example.c -o libexamplemutator2.so > /dev/null 2>&1 + cc -D_FIXED_CHAR=0x41 -g -fPIC -shared -I../include ../utils/custom_mutators/simple_example.c -o libexamplemutator.so > /dev/null 2>&1 + cc -D_FIXED_CHAR=0x42 -g -fPIC -shared -I../include ../utils/custom_mutators/simple_example.c -o libexamplemutator2.so > /dev/null 2>&1 test -e test-custom-mutator -a -e ./libexamplemutator.so && { # Create input directory mkdir -p in @@ -109,7 +109,7 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { #test "$CODE" = 1 && { $ECHO "$YELLOW[!] custom mutator tests currently will not fail travis" ; CODE=0 ; } - make -C ../examples/custom_mutators clean > /dev/null 2>&1 + make -C ../utils/custom_mutators clean > /dev/null 2>&1 rm -f test-custom-mutator rm -f test-custom-mutators } || { diff --git a/test/test-gcc-plugin.sh b/test/test-gcc-plugin.sh index af8674c9..71d86364 100755 --- a/test/test-gcc-plugin.sh +++ b/test/test-gcc-plugin.sh @@ -94,7 +94,7 @@ test -e ../afl-gcc-fast -a -e ../afl-compiler-rt.o && { CODE=1 } rm -f test-compcov test.out instrumentlist.txt - ../afl-gcc-fast -o test-persistent ../examples/persistent_mode/persistent_demo.c > /dev/null 2>&1 + ../afl-gcc-fast -o test-persistent ../utils/persistent_mode/persistent_demo.c > /dev/null 2>&1 test -e test-persistent && { echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] gcc_plugin persistent mode feature works correctly" diff --git a/test/test-llvm-lto.sh b/test/test-llvm-lto.sh index bdb08559..e10f4cf7 100755 --- a/test/test-llvm-lto.sh +++ b/test/test-llvm-lto.sh @@ -57,7 +57,7 @@ test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && { CODE=1 } rm -f test-compcov test.out instrumentlist.txt - ../afl-clang-lto -o test-persistent ../examples/persistent_mode/persistent_mode.c > /dev/null 2>&1 + ../afl-clang-lto -o test-persistent ../utils/persistent_mode/persistent_mode.c > /dev/null 2>&1 test -e test-persistent && { echo foo | ../afl-showmap -m none -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] llvm_mode LTO persistent mode feature works correctly" diff --git a/test/test-llvm.sh b/test/test-llvm.sh index 14778e1c..4fcaf367 100755 --- a/test/test-llvm.sh +++ b/test/test-llvm.sh @@ -209,7 +209,7 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { INCOMPLETE=1 } rm -rf errors test-cmplog in core.* - ../afl-clang-fast -o test-persistent ../examples/persistent_mode/persistent_demo.c > /dev/null 2>&1 + ../afl-clang-fast -o test-persistent ../utils/persistent_mode/persistent_demo.c > /dev/null 2>&1 test -e test-persistent && { echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] llvm_mode persistent mode feature works correctly" diff --git a/utils/README.md b/utils/README.md new file mode 100644 index 00000000..336b6b6c --- /dev/null +++ b/utils/README.md @@ -0,0 +1,54 @@ +# AFL++ Examples + +Here's a quick overview of the stuff you can find in this directory: + + - afl_network_proxy - fuzz a target over the network: afl-fuzz on + a host, target on an embedded system. + + - afl_proxy - skeleton file example to show how to fuzz + something where you gather coverage data via + different means, e.g. hw debugger + + - afl_untracer - fuzz binary-only libraries much faster but with + less coverage than qemu_mode + + - argv_fuzzing - a simple wrapper to allow cmdline to be fuzzed + (e.g., to test setuid programs). + + - asan_cgroups - a contributed script to simplify fuzzing ASAN + binaries with robust memory limits on Linux. + + - bash_shellshock - a simple hack used to find a bunch of + post-Shellshock bugs in bash. + + - canvas_harness - a test harness used to find browser bugs with a + corpus generated using simple image parsing + binaries & afl-fuzz. + + - clang_asm_normalize - a script that makes it easy to instrument + hand-written assembly, provided that you have clang. + + - crash_triage - a very rudimentary example of how to annotate crashes + with additional gdb metadata. + + - custom_mutators - examples for the afl++ custom mutator interface in + C and Python + + - distributed_fuzzing - a sample script for synchronizing fuzzer instances + across multiple machines (see parallel_fuzzing.md). + + - libpng_no_checksum - a sample patch for removing CRC checks in libpng. + + - persistent_mode - an example of how to use the LLVM persistent process + mode to speed up certain fuzzing jobs. + + - socket_fuzzing - a LD_PRELOAD library 'redirects' a socket to stdin + for fuzzing access with afl++ + +Note that the minimize_corpus.sh tool has graduated from the utils/ +directory and is now available as ../afl-cmin. The LLVM mode has likewise +graduated to ../instrumentation/*. + +Most of the tools in this directory are meant chiefly as examples that need to +be tweaked for your specific needs. They come with some basic documentation, +but are not necessarily production-grade. diff --git a/utils/afl_frida/GNUmakefile b/utils/afl_frida/GNUmakefile new file mode 100644 index 00000000..c154f3a4 --- /dev/null +++ b/utils/afl_frida/GNUmakefile @@ -0,0 +1,23 @@ +ifdef DEBUG + OPT=-O0 -D_DEBUG=\"1\" +else + OPT=-O3 -funroll-loops +endif + +all: afl-frida libtestinstr.so + +libfrida-gum.a: + @echo Download and extract frida-gum-devkit-VERSION-PLATFORM.tar.xz for your platform from https://github.com/frida/frida/releases/latest + @exit 1 + +afl-frida: afl-frida.c libfrida-gum.a + $(CC) -g $(OPT) -o afl-frida -Wno-format -Wno-pointer-sign -I. -fpermissive -fPIC afl-frida.c ../../afl-llvm-rt.o libfrida-gum.a -ldl -lresolv -pthread + +libtestinstr.so: libtestinstr.c + $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c + +clean: + rm -f afl-frida *~ core *.o libtestinstr.so + +deepclean: clean + rm -f libfrida-gum.a frida-gum* diff --git a/utils/afl_frida/Makefile b/utils/afl_frida/Makefile new file mode 100644 index 00000000..0b306dde --- /dev/null +++ b/utils/afl_frida/Makefile @@ -0,0 +1,2 @@ +all: + @echo please use GNU make, thanks! diff --git a/utils/afl_frida/README.md b/utils/afl_frida/README.md new file mode 100644 index 00000000..7743479b --- /dev/null +++ b/utils/afl_frida/README.md @@ -0,0 +1,34 @@ +# afl-frida - faster fuzzing of binary-only libraries + +## Introduction + +afl-frida is an example skeleton file which can easily be used to fuzz +a closed source library. + +It requires less memory and is x5-10 faster than qemu_mode but does not +provide interesting features like compcov or cmplog. + +## How-to + +### Modify afl-frida.c + +Read and modify afl-frida.c then `make`. +To adapt afl-frida.c to your needs, read the header of the file and then +search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations. + +### Fuzzing + +Example (after modifying afl-frida.c to your needs and compile it): +``` +LD_LIBRARY_PATH=/path/to/the/target/library afl-fuzz -i in -o out -- ./afl-frida +``` +(or even remote via afl-network-proxy). + +# Speed and stability + +The speed is very good, about x12 of fork() qemu_mode. +However the stability is low. Reason is currently unknown. + +# Background + +This code is copied for a larger part from https://github.com/meme/hotwax diff --git a/utils/afl_frida/afl-frida.c b/utils/afl_frida/afl-frida.c new file mode 100644 index 00000000..31bf8f25 --- /dev/null +++ b/utils/afl_frida/afl-frida.c @@ -0,0 +1,542 @@ +/* + american fuzzy lop++ - afl-frida skeleton example + ------------------------------------------------- + + Copyright 2020 AFLplusplus Project. All rights reserved. + + Written mostly by meme -> https://github.com/meme/hotwax + + Modifications by Marc Heuse + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + HOW-TO + ====== + + You only need to change the following: + + 1. set the defines and function call parameters. + 2. dl load the library you want to fuzz, lookup the functions you need + and setup the calls to these. + 3. in the while loop you call the functions in the necessary order - + incl the cleanup. the cleanup is important! + + Just look these steps up in the code, look for "// STEP x:" + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __APPLE__ + #include + #include +#endif + +int debug = 0; + +// STEP 1: + +// The presets are for the example libtestinstr.so: + +/* What is the name of the library to fuzz */ +#define TARGET_LIBRARY "libtestinstr.so" + +/* What is the name of the function to fuzz */ +#define TARGET_FUNCTION "testinstr" + +/* here you need to specify the parameter for the target function */ +static void *(*o_function)(uint8_t *, int); + +// END STEP 1 + +#include "frida-gum.h" + +G_BEGIN_DECLS + +#define GUM_TYPE_FAKE_EVENT_SINK (gum_fake_event_sink_get_type()) +G_DECLARE_FINAL_TYPE(GumFakeEventSink, gum_fake_event_sink, GUM, + FAKE_EVENT_SINK, GObject) + +struct _GumFakeEventSink { + + GObject parent; + GumEventType mask; + +}; + +GumEventSink *gum_fake_event_sink_new(void); +void gum_fake_event_sink_reset(GumFakeEventSink *self); + +G_END_DECLS + +static void gum_fake_event_sink_iface_init(gpointer g_iface, + gpointer iface_data); +static void gum_fake_event_sink_finalize(GObject *obj); +static GumEventType gum_fake_event_sink_query_mask(GumEventSink *sink); +static void gum_fake_event_sink_process(GumEventSink *sink, const GumEvent *ev); +void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, + gpointer user_data); +void afl_setup(void); +void afl_start_forkserver(void); +int __afl_persistent_loop(unsigned int max_cnt); + +static void gum_fake_event_sink_class_init(GumFakeEventSinkClass *klass) { + + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = gum_fake_event_sink_finalize; + +} + +static void gum_fake_event_sink_iface_init(gpointer g_iface, + gpointer iface_data) { + + GumEventSinkInterface *iface = (GumEventSinkInterface *)g_iface; + iface->query_mask = gum_fake_event_sink_query_mask; + iface->process = gum_fake_event_sink_process; + +} + +G_DEFINE_TYPE_EXTENDED(GumFakeEventSink, gum_fake_event_sink, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE(GUM_TYPE_EVENT_SINK, + gum_fake_event_sink_iface_init)) + +#include "../../config.h" + +// Shared memory fuzzing. +int __afl_sharedmem_fuzzing = 1; +extern unsigned int * __afl_fuzz_len; +extern unsigned char *__afl_fuzz_ptr; + +// Notify AFL about persistent mode. +static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; +int __afl_persistent_loop(unsigned int); + +// Notify AFL about deferred forkserver. +static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; +void __afl_manual_init(); + +// Because we do our own logging. +extern uint8_t * __afl_area_ptr; +static __thread guint64 previous_pc; + +// Frida stuff below. +typedef struct { + + GumAddress base_address; + guint64 code_start, code_end; + +} range_t; + +inline static void afl_maybe_log(guint64 current_pc) { + + // fprintf(stderr, "PC: %p ^ %p\n", current_pc, previous_pc); + + current_pc = (current_pc >> 4) ^ (current_pc << 8); + current_pc &= MAP_SIZE - 1; + + __afl_area_ptr[current_pc ^ previous_pc]++; + previous_pc = current_pc >> 1; + +} + +static void on_basic_block(GumCpuContext *context, gpointer user_data) { + + afl_maybe_log((guint64)user_data); + +} + +void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, + gpointer user_data) { + + range_t *range = (range_t *)user_data; + + const cs_insn *instr; + gboolean begin = TRUE; + while (gum_stalker_iterator_next(iterator, &instr)) { + + if (begin) { + + if (instr->address >= range->code_start && + instr->address <= range->code_end) { + + gum_stalker_iterator_put_callout(iterator, on_basic_block, + (gpointer)instr->address, NULL); + begin = FALSE; + + } + + } + + gum_stalker_iterator_keep(iterator); + + } + +} + +static void gum_fake_event_sink_init(GumFakeEventSink *self) { + +} + +static void gum_fake_event_sink_finalize(GObject *obj) { + + G_OBJECT_CLASS(gum_fake_event_sink_parent_class)->finalize(obj); + +} + +GumEventSink *gum_fake_event_sink_new(void) { + + GumFakeEventSink *sink; + sink = (GumFakeEventSink *)g_object_new(GUM_TYPE_FAKE_EVENT_SINK, NULL); + return GUM_EVENT_SINK(sink); + +} + +void gum_fake_event_sink_reset(GumFakeEventSink *self) { + +} + +static GumEventType gum_fake_event_sink_query_mask(GumEventSink *sink) { + + return 0; + +} + +typedef struct library_list { + + uint8_t *name; + uint64_t addr_start, addr_end; + +} library_list_t; + +#define MAX_LIB_COUNT 256 +static library_list_t liblist[MAX_LIB_COUNT]; +static u32 liblist_cnt; + +void read_library_information() { + +#if defined(__linux__) + FILE *f; + u8 buf[1024], *b, *m, *e, *n; + + if ((f = fopen("/proc/self/maps", "r")) == NULL) { + + fprintf(stderr, "Error: cannot open /proc/self/maps\n"); + exit(-1); + + } + + if (debug) fprintf(stderr, "Library list:\n"); + while (fgets(buf, sizeof(buf), f)) { + + if (strstr(buf, " r-x")) { + + if (liblist_cnt >= MAX_LIB_COUNT) { + + fprintf( + stderr, + "Warning: too many libraries to old, maximum count of %d reached\n", + liblist_cnt); + return; + + } + + b = buf; + m = index(buf, '-'); + e = index(buf, ' '); + if ((n = rindex(buf, '/')) == NULL) n = rindex(buf, ' '); + if (n && + ((*n >= '0' && *n <= '9') || *n == '[' || *n == '{' || *n == '(')) + n = NULL; + else + n++; + if (b && m && e && n && *n) { + + *m++ = 0; + *e = 0; + if (n[strlen(n) - 1] == '\n') n[strlen(n) - 1] = 0; + + if (rindex(n, '/') != NULL) { + + n = rindex(n, '/'); + n++; + + } + + liblist[liblist_cnt].name = strdup(n); + liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16); + liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16); + if (debug) + fprintf( + stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name, + liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_end - 1); + liblist_cnt++; + + } + + } + + } + + if (debug) fprintf(stderr, "\n"); + +#elif defined(__FreeBSD__) + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; + char * buf, *start, *end; + size_t miblen = sizeof(mib) / sizeof(mib[0]); + size_t len; + + if (debug) fprintf(stderr, "Library list:\n"); + if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) { return; } + + len = len * 4 / 3; + + buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); + if (buf == MAP_FAILED) { return; } + if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) { + + munmap(buf, len); + return; + + } + + start = buf; + end = buf + len; + + while (start < end) { + + struct kinfo_vmentry *region = (struct kinfo_vmentry *)start; + size_t size = region->kve_structsize; + + if (size == 0) { break; } + + if ((region->kve_protection & KVME_PROT_READ) && + !(region->kve_protection & KVME_PROT_EXEC)) { + + liblist[liblist_cnt].name = + region->kve_path[0] != '\0' ? strdup(region->kve_path) : 0; + liblist[liblist_cnt].addr_start = region->kve_start; + liblist[liblist_cnt].addr_end = region->kve_end; + + if (debug) { + + fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name, + liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_end - 1); + + } + + liblist_cnt++; + + } + + start += size; + + } + +#endif + +} + +library_list_t *find_library(char *name) { + + char *filename = rindex(name, '/'); + + if (filename) + filename++; + else + filename = name; + +#if defined(__linux__) + u32 i; + for (i = 0; i < liblist_cnt; i++) + if (strcmp(liblist[i].name, filename) == 0) return &liblist[i]; +#elif defined(__APPLE__) && defined(__LP64__) + kern_return_t err; + static library_list_t lib; + + // get the list of all loaded modules from dyld + // the task_info mach API will get the address of the dyld all_image_info + // struct for the given task from which we can get the names and load + // addresses of all modules + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + err = task_info(mach_task_self(), TASK_DYLD_INFO, + (task_info_t)&task_dyld_info, &count); + + const struct dyld_all_image_infos *all_image_infos = + (const struct dyld_all_image_infos *)task_dyld_info.all_image_info_addr; + const struct dyld_image_info *image_infos = all_image_infos->infoArray; + + for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) { + + const char * image_name = image_infos[i].imageFilePath; + mach_vm_address_t image_load_address = + (mach_vm_address_t)image_infos[i].imageLoadAddress; + if (strstr(image_name, name)) { + + lib.name = name; + lib.addr_start = (u64)image_load_address; + lib.addr_end = 0; + return &lib; + + } + + } + +#endif + + return NULL; + +} + +static void gum_fake_event_sink_process(GumEventSink * sink, + const GumEvent *ev) { + +} + +/* Because this CAN be called more than once, it will return the LAST range */ +static int enumerate_ranges(const GumRangeDetails *details, + gpointer user_data) { + + GumMemoryRange *code_range = (GumMemoryRange *)user_data; + memcpy(code_range, details->range, sizeof(*code_range)); + return 0; + +} + +int main() { + +#ifndef __APPLE__ + (void)personality(ADDR_NO_RANDOMIZE); // disable ASLR +#endif + + // STEP 2: load the library you want to fuzz and lookup the functions, + // inclusive of the cleanup functions. + // If there is just one function, then there is nothing to change + // or add here. + + void *dl = dlopen(TARGET_LIBRARY, RTLD_LAZY); + if (!dl) { + + fprintf(stderr, "Could not load %s\n", TARGET_LIBRARY); + exit(-1); + + } + + if (!(o_function = dlsym(dl, TARGET_FUNCTION))) { + + fprintf(stderr, "Could not find function %s\n", TARGET_FUNCTION); + exit(-1); + + } + + // END STEP 2 + + read_library_information(); + library_list_t *lib = find_library(TARGET_LIBRARY); + + if (lib == NULL) { + + fprintf(stderr, "Could not find target library\n"); + exit(-1); + + } + + gum_init_embedded(); + if (!gum_stalker_is_supported()) { + + gum_deinit_embedded(); + return 1; + + } + + GumStalker *stalker = gum_stalker_new(); + + /* + This does not work here as we load a shared library. pretty sure this + would also be easily solvable with frida gum, but I already have all the + code I need from afl-untracer + + GumAddress base_address = gum_module_find_base_address(TARGET_LIBRARY); + GumMemoryRange code_range; + gum_module_enumerate_ranges(TARGET_LIBRARY, GUM_PAGE_RX, enumerate_ranges, + &code_range); + guint64 code_start = code_range.base_address - base_address; + guint64 code_end = (code_range.base_address + code_range.size) - base_address; + range_t instr_range = {base_address, code_start, code_end}; + */ + range_t instr_range = {0, lib->addr_start, lib->addr_end}; + + GumStalkerTransformer *transformer = + gum_stalker_transformer_make_from_callback(instr_basic_block, + &instr_range, NULL); + + GumEventSink *event_sink = gum_fake_event_sink_new(); + + // to ensure that the signatures are not optimized out + memcpy(__afl_area_ptr, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT) + 1); + memcpy(__afl_area_ptr + 32, (void *)AFL_DEFER_FORKSVR, + sizeof(AFL_DEFER_FORKSVR) + 1); + __afl_manual_init(); + + // + // any expensive target library initialization that has to be done just once + // - put that here + // + + gum_stalker_follow_me(stalker, transformer, event_sink); + + while (__afl_persistent_loop(UINT32_MAX) != 0) { + + previous_pc = 0; // Required! + +#ifdef _DEBUG + fprintf(stderr, "CLIENT crc: %016llx len: %u\n", + hash64(__afl_fuzz_ptr, *__afl_fuzz_len), *__afl_fuzz_len); + fprintf(stderr, "RECV:"); + for (int i = 0; i < *__afl_fuzz_len; i++) + fprintf(stderr, "%02x", __afl_fuzz_ptr[i]); + fprintf(stderr, "\n"); +#endif + + // STEP 3: ensure the minimum length is present and setup the target + // function to fuzz. + + if (*__afl_fuzz_len > 0) { + + __afl_fuzz_ptr[*__afl_fuzz_len] = 0; // if you need to null terminate + (*o_function)(__afl_fuzz_ptr, *__afl_fuzz_len); + + } + + // END STEP 3 + + } + + gum_stalker_unfollow_me(stalker); + + while (gum_stalker_garbage_collect(stalker)) + g_usleep(10000); + + g_object_unref(stalker); + g_object_unref(transformer); + g_object_unref(event_sink); + gum_deinit_embedded(); + + return 0; + +} + diff --git a/utils/afl_frida/afl-frida.h b/utils/afl_frida/afl-frida.h new file mode 100644 index 00000000..efa3440f --- /dev/null +++ b/utils/afl_frida/afl-frida.h @@ -0,0 +1,53 @@ +extern int is_persistent; + +G_BEGIN_DECLS + +#define GUM_TYPE_FAKE_EVENT_SINK (gum_fake_event_sink_get_type()) + +G_DECLARE_FINAL_TYPE(GumFakeEventSink, gum_fake_event_sink, GUM, + FAKE_EVENT_SINK, GObject) + +struct _GumFakeEventSink { + + GObject parent; + GumEventType mask; + +}; + +GumEventSink *gum_fake_event_sink_new(void); +void gum_fake_event_sink_reset(GumFakeEventSink *self); + +G_END_DECLS + +typedef struct { + + GumAddress base_address; + guint64 code_start, code_end; + +} range_t; + +void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, + gpointer user_data); +#pragma once + +void afl_setup(void); +void afl_start_forkserver(void); +int __afl_persistent_loop(unsigned int max_cnt); + +inline static inline void afl_maybe_log(guint64 current_pc) { + + extern unsigned int afl_instr_rms; + extern uint8_t * afl_area_ptr; + + static __thread guint64 previous_pc; + + current_pc = (current_pc >> 4) ^ (current_pc << 8); + current_pc &= MAP_SIZE - 1; + + if (current_pc >= afl_instr_rms) return; + + afl_area_ptr[current_pc ^ previous_pc]++; + previous_pc = current_pc >> 1; + +} + diff --git a/utils/afl_frida/libtestinstr.c b/utils/afl_frida/libtestinstr.c new file mode 100644 index 00000000..96b1cf21 --- /dev/null +++ b/utils/afl_frida/libtestinstr.c @@ -0,0 +1,35 @@ +/* + american fuzzy lop++ - a trivial program to test the build + -------------------------------------------------------- + Originally written by Michal Zalewski + Copyright 2014 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + http://www.apache.org/licenses/LICENSE-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +void testinstr(char *buf, int len) { + + if (len < 1) return; + buf[len] = 0; + + // we support three input cases + if (buf[0] == '0') + printf("Looks like a zero to me!\n"); + else if (buf[0] == '1') + printf("Pretty sure that is a one!\n"); + else + printf("Neither one or zero? How quaint!\n"); + +} + diff --git a/utils/afl_network_proxy/GNUmakefile b/utils/afl_network_proxy/GNUmakefile new file mode 100644 index 00000000..25a3df82 --- /dev/null +++ b/utils/afl_network_proxy/GNUmakefile @@ -0,0 +1,43 @@ +PREFIX ?= /usr/local +BIN_PATH = $(PREFIX)/bin +DOC_PATH = $(PREFIX)/share/doc/afl + +PROGRAMS = afl-network-client afl-network-server + +HASH=\# + +CFLAGS += -Wno-pointer-sign + +ifdef STATIC + CFLAGS += -static +endif + +ifeq "$(shell echo '$(HASH)include @int main() { struct libdeflate_compressor *d = libdeflate_alloc_compressor(1); return 0;}' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test2 -ldeflate 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" + CFLAGS += -DUSE_DEFLATE=1 + LDFLAGS += -ldeflate + $(info libdeflate-dev was detected, using compression) +else + $(warn did not find libdeflate-dev, cannot use compression) +endif + +all: $(PROGRAMS) + +help: + @echo make options: + @echo STATIC - build as static binaries + @echo COMPRESS_TESTCASES - compress test cases + +afl-network-client: afl-network-client.c + $(CC) $(CFLAGS) -I../../include -o afl-network-client afl-network-client.c $(LDFLAGS) + +afl-network-server: afl-network-server.c + $(CC) $(CFLAGS) -I../../include -o afl-network-server afl-network-server.c ../../src/afl-forkserver.c ../../src/afl-sharedmem.c ../../src/afl-common.c -DBIN_PATH=\"$(BIN_PATH)\" $(LDFLAGS) + +clean: + rm -f $(PROGRAMS) *~ core + +install: all + install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(DOC_PATH) + install -m 755 $(PROGRAMS) $${DESTDIR}$(BIN_PATH) + install -T -m 644 README.md $${DESTDIR}$(DOC_PATH)/README.network_proxy.md + diff --git a/utils/afl_network_proxy/Makefile b/utils/afl_network_proxy/Makefile new file mode 100644 index 00000000..0b306dde --- /dev/null +++ b/utils/afl_network_proxy/Makefile @@ -0,0 +1,2 @@ +all: + @echo please use GNU make, thanks! diff --git a/utils/afl_network_proxy/README.md b/utils/afl_network_proxy/README.md new file mode 100644 index 00000000..a5ac3578 --- /dev/null +++ b/utils/afl_network_proxy/README.md @@ -0,0 +1,61 @@ +# afl-network-proxy + +If you want to run afl-fuzz over the network than this is what you need :) +Note that the impact on fuzzing speed will be huge, expect a loss of 90%. + +## When to use this + +1. when you have to fuzz a target that has to run on a system that cannot + contain the fuzzing output (e.g. /tmp too small and file system is read-only) +2. when the target instantly reboots on crashes +3. ... any other reason you would need this + +## how to get it running + +### Compiling + +Just type `make` and let the autodetection do everything for you. + +Note that you will get a 40-50% performance increase if you have libdeflate-dev +installed. The GNUmakefile will autodetect it if present. + +If your target has large test cases (10+kb) that are ascii only or large chunks +of zero blocks then set `CFLAGS=-DCOMPRESS_TESTCASES=1` to compress them. +For most targets this hurts performance though so it is disabled by default. + +### on the target + +Run `afl-network-server` with your target with the -m and -t values you need. +Important is the -i parameter which is the TCP port to listen on. +e.g.: +``` +afl-network-server -i 1111 -m 25M -t 1000 -- /bin/target -f @@ +``` + +### on the (afl-fuzz) master + +Just run afl-fuzz with your normal options, however the target should be +`afl-network-client` with the IP and PORT of the `afl-network-server` and +increase the -t value: +``` +afl-fuzz -i in -o out -t 2000+ -- afl-network-client TARGET-IP 1111 +``` +Note the '+' on the -t parameter value. The afl-network-server will take +care of proper timeouts hence afl-fuzz should not. The '+' increases the +timeout and the value itself should be 500-1000 higher than the one on +afl-network-server. + +### networking + +The TARGET can be an IPv4 or IPv6 address, or a host name that resolves to +either. Note that also the outgoing interface can be specified with a '%' for +`afl-network-client`, e.g. `fe80::1234%eth0`. + +Also make sure your default TCP window size is larger than your MAP_SIZE +(130kb is a good value). +On Linux that is the middle value of `/proc/sys/net/ipv4/tcp_rmem` + +## how to compile and install + +`make && sudo make install` + diff --git a/utils/afl_network_proxy/afl-network-client.c b/utils/afl_network_proxy/afl-network-client.c new file mode 100644 index 00000000..a2451fdc --- /dev/null +++ b/utils/afl_network_proxy/afl-network-client.c @@ -0,0 +1,415 @@ +/* + american fuzzy lop++ - afl-network-client + --------------------------------------- + + Written by Marc Heuse + + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +*/ + +#ifdef __ANDROID__ + #include "android-ashmem.h" +#endif +#include "config.h" +#include "types.h" +#include "debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#ifndef USEMMAP + #include +#endif +#include +#include +#include +#include +#include + +#ifdef USE_DEFLATE + #include +#endif + +u8 *__afl_area_ptr; + +#ifdef __ANDROID__ +u32 __afl_map_size = MAP_SIZE; +#else +__thread u32 __afl_map_size = MAP_SIZE; +#endif + +/* Error reporting to forkserver controller */ + +void send_forkserver_error(int error) { + + u32 status; + if (!error || error > 0xffff) return; + status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); + if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; + +} + +/* SHM setup. */ + +static void __afl_map_shm(void) { + + char *id_str = getenv(SHM_ENV_VAR); + char *ptr; + + if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { + + u32 val = atoi(ptr); + if (val > 0) __afl_map_size = val; + + } + + if (__afl_map_size > MAP_SIZE) { + + if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { + + fprintf(stderr, + "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + if (id_str) { + + send_forkserver_error(FS_ERROR_MAP_SIZE); + exit(-1); + + } + + } else { + + fprintf(stderr, + "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + + } + + } + + if (id_str) { + +#ifdef USEMMAP + const char * shm_file_path = id_str; + int shm_fd = -1; + unsigned char *shm_base = NULL; + + /* create the shared memory segment as if it was a file */ + shm_fd = shm_open(shm_file_path, O_RDWR, 0600); + if (shm_fd == -1) { + + fprintf(stderr, "shm_open() failed\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); + exit(1); + + } + + /* map the shared memory segment to the address space of the process */ + shm_base = + mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + + if (shm_base == MAP_FAILED) { + + close(shm_fd); + shm_fd = -1; + + fprintf(stderr, "mmap() failed\n"); + send_forkserver_error(FS_ERROR_MMAP); + exit(2); + + } + + __afl_area_ptr = shm_base; +#else + u32 shm_id = atoi(id_str); + + __afl_area_ptr = shmat(shm_id, 0, 0); + +#endif + + if (__afl_area_ptr == (void *)-1) { + + send_forkserver_error(FS_ERROR_SHMAT); + exit(1); + + } + + /* Write something into the bitmap so that the parent doesn't give up */ + + __afl_area_ptr[0] = 1; + + } + +} + +/* Fork server logic. */ + +static void __afl_start_forkserver(void) { + + u8 tmp[4] = {0, 0, 0, 0}; + u32 status = 0; + + if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) + status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); + if (status) status |= (FS_OPT_ENABLED); + memcpy(tmp, &status, 4); + + /* Phone home and tell the parent that we're OK. */ + + if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; + +} + +static u32 __afl_next_testcase(u8 *buf, u32 max_len) { + + s32 status, res = 0x0fffffff; // res is a dummy pid + + /* Wait for parent by reading from the pipe. Abort if read fails. */ + if (read(FORKSRV_FD, &status, 4) != 4) return 0; + + /* we have a testcase - read it */ + status = read(0, buf, max_len); + + /* report that we are starting the target */ + if (write(FORKSRV_FD + 1, &res, 4) != 4) return 0; + + if (status < 1) + return 0; + else + return status; + +} + +static void __afl_end_testcase(int status) { + + if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1); + +} + +/* you just need to modify the while() loop in this main() */ + +int main(int argc, char *argv[]) { + + u8 * interface, *buf, *ptr; + s32 s = -1; + struct addrinfo hints, *hres, *aip; + u32 * lenptr, max_len = 65536; +#ifdef USE_DEFLATE + u8 * buf2; + u32 * lenptr1, *lenptr2, buf2_len, compress_len; + size_t decompress_len; +#endif + + if (argc < 3 || argc > 4) { + + printf("Syntax: %s host port [max-input-size]\n\n", argv[0]); + printf("Requires host and port of the remote afl-proxy-server instance.\n"); + printf( + "IPv4 and IPv6 are supported, also binding to an interface with " + "\"%%\"\n"); + printf("The max-input-size default is %u.\n", max_len); + printf( + "The default map size is %u and can be changed with setting " + "AFL_MAP_SIZE.\n", + __afl_map_size); + exit(-1); + + } + + if ((interface = strchr(argv[1], '%')) != NULL) *interface++ = 0; + + if (argc > 3) + if ((max_len = atoi(argv[3])) < 0) + FATAL("max-input-size may not be negative or larger than 2GB: %s", + argv[3]); + + if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) + if ((__afl_map_size = atoi(ptr)) < 8) + FATAL("illegal map size, may not be < 8 or >= 2^30: %s", ptr); + + if ((buf = malloc(max_len + 4)) == NULL) + PFATAL("can not allocate %u memory", max_len + 4); + lenptr = (u32 *)buf; + +#ifdef USE_DEFLATE + buf2_len = (max_len > __afl_map_size ? max_len : __afl_map_size); + if ((buf2 = malloc(buf2_len + 8)) == NULL) + PFATAL("can not allocate %u memory", buf2_len + 8); + lenptr1 = (u32 *)buf2; + lenptr2 = (u32 *)(buf2 + 4); +#endif + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = PF_UNSPEC; + + if (getaddrinfo(argv[1], argv[2], &hints, &hres) != 0) + PFATAL("could not resolve target %s", argv[1]); + + for (aip = hres; aip != NULL && s == -1; aip = aip->ai_next) { + + if ((s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol)) >= 0) { + +#ifdef SO_BINDTODEVICE + if (interface != NULL) + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, interface, + strlen(interface) + 1) < 0) + fprintf(stderr, "Warning: could not bind to device %s\n", interface); +#else + fprintf(stderr, + "Warning: binding to interface is not supported for your OS\n"); +#endif + +#ifdef SO_PRIORITY + int priority = 7; + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < + 0) { + + priority = 6; + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, + sizeof(priority)) < 0) + WARNF("could not set priority on socket"); + + } + +#endif + + if (connect(s, aip->ai_addr, aip->ai_addrlen) == -1) s = -1; + + } + + } + +#ifdef USE_DEFLATE + struct libdeflate_compressor *compressor; + compressor = libdeflate_alloc_compressor(1); + struct libdeflate_decompressor *decompressor; + decompressor = libdeflate_alloc_decompressor(); + fprintf(stderr, "Compiled with compression support\n"); +#endif + + if (s == -1) + FATAL("could not connect to target tcp://%s:%s", argv[1], argv[2]); + else + fprintf(stderr, "Connected to target tcp://%s:%s\n", argv[1], argv[2]); + + /* we initialize the shared memory map and start the forkserver */ + __afl_map_shm(); + __afl_start_forkserver(); + + int i = 1, j, status, ret, received; + + // fprintf(stderr, "Waiting for first testcase\n"); + while ((*lenptr = __afl_next_testcase(buf + 4, max_len)) > 0) { + + // fprintf(stderr, "Sending testcase with len %u\n", *lenptr); +#ifdef USE_DEFLATE + #ifdef COMPRESS_TESTCASES + // we only compress the testcase if it does not fit in the TCP packet + if (*lenptr > 1500 - 20 - 32 - 4) { + + // set highest byte to signify compression + *lenptr1 = (*lenptr | 0xff000000); + *lenptr2 = (u32)libdeflate_deflate_compress(compressor, buf + 4, *lenptr, + buf2 + 8, buf2_len); + if (send(s, buf2, *lenptr2 + 8, 0) != *lenptr2 + 8) + PFATAL("sending test data failed"); + // fprintf(stderr, "COMPRESS (%u->%u):\n", *lenptr, *lenptr2); + // for (u32 i = 0; i < *lenptr; i++) + // fprintf(stderr, "%02x", buf[i + 4]); + // fprintf(stderr, "\n"); + // for (u32 i = 0; i < *lenptr2; i++) + // fprintf(stderr, "%02x", buf2[i + 8]); + // fprintf(stderr, "\n"); + + } else { + + #endif +#endif + if (send(s, buf, *lenptr + 4, 0) != *lenptr + 4) + PFATAL("sending test data failed"); +#ifdef USE_DEFLATE + #ifdef COMPRESS_TESTCASES + // fprintf(stderr, "unCOMPRESS (%u)\n", *lenptr); + + } + + #endif +#endif + + received = 0; + while (received < 4 && + (ret = recv(s, &status + received, 4 - received, 0)) > 0) + received += ret; + if (received != 4) + FATAL("did not receive waitpid data (%d, %d)", received, ret); + // fprintf(stderr, "Received status\n"); + + received = 0; +#ifdef USE_DEFLATE + while (received < 4 && + (ret = recv(s, &compress_len + received, 4 - received, 0)) > 0) + received += ret; + if (received != 4) + FATAL("did not receive compress_len (%d, %d)", received, ret); + // fprintf(stderr, "Received status\n"); + + received = 0; + while (received < compress_len && + (ret = recv(s, buf2 + received, buf2_len - received, 0)) > 0) + received += ret; + if (received != compress_len) + FATAL("did not receive coverage data (%d, %d)", received, ret); + + if (libdeflate_deflate_decompress(decompressor, buf2, compress_len, + __afl_area_ptr, __afl_map_size, + &decompress_len) != LIBDEFLATE_SUCCESS || + decompress_len != __afl_map_size) + FATAL("decompression failed"); + // fprintf(stderr, "DECOMPRESS (%u->%u): ", compress_len, decompress_len); + // for (u32 i = 0; i < __afl_map_size; i++) fprintf(stderr, "%02x", + // __afl_area_ptr[i]); fprintf(stderr, "\n"); +#else + while (received < __afl_map_size && + (ret = recv(s, __afl_area_ptr + received, __afl_map_size - received, + 0)) > 0) + received += ret; + if (received != __afl_map_size) + FATAL("did not receive coverage data (%d, %d)", received, ret); +#endif + // fprintf(stderr, "Received coverage\n"); + + /* report the test case is done and wait for the next */ + __afl_end_testcase(status); + // fprintf(stderr, "Waiting for next testcase %d\n", ++i); + + } + +#ifdef USE_DEFLATE + libdeflate_free_compressor(compressor); + libdeflate_free_decompressor(decompressor); +#endif + + return 0; + +} + diff --git a/utils/afl_network_proxy/afl-network-server.c b/utils/afl_network_proxy/afl-network-server.c new file mode 100644 index 00000000..513dc8f2 --- /dev/null +++ b/utils/afl_network_proxy/afl-network-server.c @@ -0,0 +1,720 @@ +/* + american fuzzy lop++ - network proxy server + ------------------------------------------- + + Originally written by Michal Zalewski + + Forkserver design by Jann Horn + + Now maintained by Marc Heuse , + Heiko Eißfeldt and + Andrea Fioraldi and + Dominik Maier + + Copyright 2016, 2017 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + */ + +#define AFL_MAIN + +#ifdef __ANDROID__ + #include "android-ashmem.h" +#endif + +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" +#include "hash.h" +#include "forkserver.h" +#include "sharedmem.h" +#include "common.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_DEFLATE + #include +struct libdeflate_compressor * compressor; +struct libdeflate_decompressor *decompressor; +#endif + +static u8 *in_file, /* Minimizer input test case */ + *out_file; + +static u8 *in_data; /* Input data for trimming */ +static u8 *buf2; + +static s32 in_len; +static s32 buf2_len; +static u32 map_size = MAP_SIZE; + +static volatile u8 stop_soon; /* Ctrl-C pressed? */ + +/* See if any bytes are set in the bitmap. */ + +static inline u8 anything_set(afl_forkserver_t *fsrv) { + + u32 *ptr = (u32 *)fsrv->trace_bits; + u32 i = (map_size >> 2); + + while (i--) { + + if (*(ptr++)) { return 1; } + + } + + return 0; + +} + +static void at_exit_handler(void) { + + afl_fsrv_killall(); + +} + +/* Write output file. */ + +static s32 write_to_file(u8 *path, u8 *mem, u32 len) { + + s32 ret; + + unlink(path); /* Ignore errors */ + + ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600); + + if (ret < 0) { PFATAL("Unable to create '%s'", path); } + + ck_write(ret, mem, len, path); + + lseek(ret, 0, SEEK_SET); + + return ret; + +} + +/* Execute target application. Returns 0 if the changes are a dud, or + 1 if they should be kept. */ + +static u8 run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len, + u8 first_run) { + + afl_fsrv_write_to_testcase(fsrv, mem, len); + + fsrv_run_result_t ret = + afl_fsrv_run_target(fsrv, fsrv->exec_tmout, &stop_soon); + + if (ret == FSRV_RUN_ERROR) { FATAL("Couldn't run child"); } + + if (stop_soon) { + + SAYF(cRST cLRD "\n+++ aborted by user +++\n" cRST); + exit(1); + + } + + return ret; + +} + +/* Handle Ctrl-C and the like. */ + +static void handle_stop_sig(int sig) { + + stop_soon = 1; + afl_fsrv_killall(); + +} + +/* Do basic preparations - persistent fds, filenames, etc. */ + +static void set_up_environment(afl_forkserver_t *fsrv) { + + u8 *x; + + fsrv->dev_null_fd = open("/dev/null", O_RDWR); + if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); } + + if (!out_file) { + + u8 *use_dir = "."; + + if (access(use_dir, R_OK | W_OK | X_OK)) { + + use_dir = get_afl_env("TMPDIR"); + if (!use_dir) { use_dir = "/tmp"; } + + } + + out_file = alloc_printf("%s/.afl-input-temp-%u", use_dir, getpid()); + + } + + unlink(out_file); + + fsrv->out_fd = open(out_file, O_RDWR | O_CREAT | O_EXCL, 0600); + + if (fsrv->out_fd < 0) { PFATAL("Unable to create '%s'", out_file); } + + /* Set sane defaults... */ + + x = get_afl_env("ASAN_OPTIONS"); + + if (x) { + + if (!strstr(x, "abort_on_error=1")) { + + FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); + + } + + if (!strstr(x, "symbolize=0")) { + + FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); + + } + + } + + x = get_afl_env("MSAN_OPTIONS"); + + if (x) { + + if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) { + + FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY( + MSAN_ERROR) " - please fix!"); + + } + + if (!strstr(x, "symbolize=0")) { + + FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); + + } + + } + + setenv("ASAN_OPTIONS", + "abort_on_error=1:" + "detect_leaks=0:" + "symbolize=0:" + "allocator_may_return_null=1", + 0); + + setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" + "symbolize=0:" + "abort_on_error=1:" + "allocator_may_return_null=1:" + "msan_track_origins=0", 0); + + if (get_afl_env("AFL_PRELOAD")) { + + if (fsrv->qemu_mode) { + + u8 *qemu_preload = getenv("QEMU_SET_ENV"); + u8 *afl_preload = getenv("AFL_PRELOAD"); + u8 *buf; + + s32 i, afl_preload_size = strlen(afl_preload); + for (i = 0; i < afl_preload_size; ++i) { + + if (afl_preload[i] == ',') { + + PFATAL( + "Comma (',') is not allowed in AFL_PRELOAD when -Q is " + "specified!"); + + } + + } + + if (qemu_preload) { + + buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", + qemu_preload, afl_preload, afl_preload); + + } else { + + buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", + afl_preload, afl_preload); + + } + + setenv("QEMU_SET_ENV", buf, 1); + + afl_free(buf); + + } else { + + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); + setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + + } + + } + +} + +/* Setup signal handlers, duh. */ + +static void setup_signal_handlers(void) { + + struct sigaction sa; + + sa.sa_handler = NULL; + sa.sa_flags = SA_RESTART; + sa.sa_sigaction = NULL; + + sigemptyset(&sa.sa_mask); + + /* Various ways of saying "stop". */ + + sa.sa_handler = handle_stop_sig; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + +} + +/* Display usage hints. */ + +static void usage(u8 *argv0) { + + SAYF( + "\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" + + "Required parameters:\n" + + " -i port - the port to listen for the client to connect to\n\n" + + "Execution control settings:\n" + + " -f file - input file read by the tested program (stdin)\n" + " -t msec - timeout for each run (%d ms)\n" + " -m megs - memory limit for child process (%d MB)\n" + " -Q - use binary-only instrumentation (QEMU mode)\n" + " -U - use unicorn-based instrumentation (Unicorn mode)\n" + " -W - use qemu-based instrumentation with Wine (Wine " + "mode)\n\n" + + "Environment variables used:\n" + "TMPDIR: directory to use for temporary input files\n" + "ASAN_OPTIONS: custom settings for ASAN\n" + " (must contain abort_on_error=1 and symbolize=0)\n" + "MSAN_OPTIONS: custom settings for MSAN\n" + " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n" + "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" + " the target was compiled for\n" + "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" + + , argv0, EXEC_TIMEOUT, MEM_LIMIT); + + exit(1); + +} + +int recv_testcase(int s, void **buf) { + + u32 size; + s32 ret; + size_t received; + + received = 0; + while (received < 4 && (ret = recv(s, &size + received, 4 - received, 0)) > 0) + received += ret; + if (received != 4) FATAL("did not receive size information"); + if (size == 0) FATAL("did not receive valid size information"); + // fprintf(stderr, "received size information of %d\n", size); + + if ((size & 0xff000000) != 0xff000000) { + + *buf = afl_realloc(buf, size); + if (unlikely(!*buf)) { PFATAL("Alloc"); } + received = 0; + // fprintf(stderr, "unCOMPRESS (%u)\n", size); + while (received < size && + (ret = recv(s, ((char *)*buf) + received, size - received, 0)) > 0) + received += ret; + + } else { + +#ifdef USE_DEFLATE + u32 clen; + size -= 0xff000000; + *buf = afl_realloc(buf, size); + if (unlikely(!*buf)) { PFATAL("Alloc"); } + received = 0; + while (received < 4 && + (ret = recv(s, &clen + received, 4 - received, 0)) > 0) + received += ret; + if (received != 4) FATAL("did not receive clen1 information"); + // fprintf(stderr, "received clen information of %d\n", clen); + if (clen < 1) + FATAL("did not receive valid compressed len information: %u", clen); + buf2 = afl_realloc((void **)&buf2, clen); + buf2_len = clen; + if (unlikely(!buf2)) { PFATAL("Alloc"); } + received = 0; + while (received < clen && + (ret = recv(s, buf2 + received, clen - received, 0)) > 0) + received += ret; + if (received != clen) FATAL("did not receive compressed information"); + if (libdeflate_deflate_decompress(decompressor, buf2, clen, (char *)*buf, + size, &received) != LIBDEFLATE_SUCCESS) + FATAL("decompression failed"); + // fprintf(stderr, "DECOMPRESS (%u->%u):\n", clen, received); + // for (u32 i = 0; i < clen; i++) fprintf(stderr, "%02x", buf2[i]); + // fprintf(stderr, "\n"); + // for (u32 i = 0; i < received; i++) fprintf(stderr, "%02x", + // ((u8*)(*buf))[i]); fprintf(stderr, "\n"); +#else + FATAL("Received compressed data but not compiled with compression support"); +#endif + + } + + // fprintf(stderr, "receiving testcase %p %p max %u\n", buf, *buf, *max_len); + if (received != size) + FATAL("did not receive testcase data %lu != %u, %d", received, size, ret); + // fprintf(stderr, "received testcase\n"); + return size; + +} + +/* Main entry point */ + +int main(int argc, char **argv_orig, char **envp) { + + s32 opt, s, sock, on = 1, port = -1; + u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0; + char **use_argv; + struct sockaddr_in6 serveraddr, clientaddr; + int addrlen = sizeof(clientaddr); + char str[INET6_ADDRSTRLEN]; + char ** argv = argv_cpy_dup(argc, argv_orig); + u8 * send_buf; +#ifdef USE_DEFLATE + u32 *lenptr; +#endif + + afl_forkserver_t fsrv_var = {0}; + afl_forkserver_t *fsrv = &fsrv_var; + afl_fsrv_init(fsrv); + map_size = get_map_size(); + fsrv->map_size = map_size; + + if ((send_buf = malloc(map_size + 4)) == NULL) PFATAL("malloc"); + + while ((opt = getopt(argc, argv, "+i:f:m:t:QUWh")) > 0) { + + switch (opt) { + + case 'i': + + if (port > 0) { FATAL("Multiple -i options not supported"); } + port = atoi(optarg); + if (port < 1 || port > 65535) + FATAL("invalid port definition, must be between 1-65535: %s", optarg); + break; + + case 'f': + + if (out_file) { FATAL("Multiple -f options not supported"); } + fsrv->use_stdin = 0; + out_file = optarg; + break; + + case 'm': { + + u8 suffix = 'M'; + + if (mem_limit_given) { FATAL("Multiple -m options not supported"); } + mem_limit_given = 1; + + if (!optarg) { FATAL("Wrong usage of -m"); } + + if (!strcmp(optarg, "none")) { + + fsrv->mem_limit = 0; + break; + + } + + if (sscanf(optarg, "%llu%c", &fsrv->mem_limit, &suffix) < 1 || + optarg[0] == '-') { + + FATAL("Bad syntax used for -m"); + + } + + switch (suffix) { + + case 'T': + fsrv->mem_limit *= 1024 * 1024; + break; + case 'G': + fsrv->mem_limit *= 1024; + break; + case 'k': + fsrv->mem_limit /= 1024; + break; + case 'M': + break; + + default: + FATAL("Unsupported suffix or bad syntax for -m"); + + } + + if (fsrv->mem_limit < 5) { FATAL("Dangerously low value of -m"); } + + if (sizeof(rlim_t) == 4 && fsrv->mem_limit > 2000) { + + FATAL("Value of -m out of range on 32-bit systems"); + + } + + } + + break; + + case 't': + + if (timeout_given) { FATAL("Multiple -t options not supported"); } + timeout_given = 1; + + if (!optarg) { FATAL("Wrong usage of -t"); } + + fsrv->exec_tmout = atoi(optarg); + + if (fsrv->exec_tmout < 10 || optarg[0] == '-') { + + FATAL("Dangerously low value of -t"); + + } + + break; + + case 'Q': + + if (fsrv->qemu_mode) { FATAL("Multiple -Q options not supported"); } + if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; } + + fsrv->qemu_mode = 1; + break; + + case 'U': + + if (unicorn_mode) { FATAL("Multiple -Q options not supported"); } + if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; } + + unicorn_mode = 1; + break; + + case 'W': /* Wine+QEMU mode */ + + if (use_wine) { FATAL("Multiple -W options not supported"); } + fsrv->qemu_mode = 1; + use_wine = 1; + + if (!mem_limit_given) { fsrv->mem_limit = 0; } + + break; + + case 'h': + usage(argv[0]); + return -1; + break; + + default: + usage(argv[0]); + + } + + } + + if (optind == argc || port < 1) { usage(argv[0]); } + + check_environment_vars(envp); + + sharedmem_t shm = {0}; + fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); + + in_data = afl_realloc((void **)&in_data, 65536); + if (unlikely(!in_data)) { PFATAL("Alloc"); } + + atexit(at_exit_handler); + setup_signal_handlers(); + + set_up_environment(fsrv); + + fsrv->target_path = find_binary(argv[optind]); + detect_file_args(argv + optind, out_file, &fsrv->use_stdin); + + if (fsrv->qemu_mode) { + + if (use_wine) { + + use_argv = get_wine_argv(argv[0], &fsrv->target_path, argc - optind, + argv + optind); + + } else { + + use_argv = get_qemu_argv(argv[0], &fsrv->target_path, argc - optind, + argv + optind); + + } + + } else { + + use_argv = argv + optind; + + } + + if ((sock = socket(AF_INET6, SOCK_STREAM, 0)) < 0) PFATAL("socket() failed"); + +#ifdef SO_REUSEADDR + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) { + + WARNF("setsockopt(SO_REUSEADDR) failed"); + + } + +#endif + +#ifdef SO_PRIORITY + int priority = 7; + if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < + 0) { + + priority = 6; + if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < + 0) + WARNF("could not set priority on socket"); + + } + +#endif + + memset(&serveraddr, 0, sizeof(serveraddr)); + serveraddr.sin6_family = AF_INET6; + serveraddr.sin6_port = htons(port); + serveraddr.sin6_addr = in6addr_any; + + if (bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) + PFATAL("bind() failed"); + + if (listen(sock, 1) < 0) { PFATAL("listen() failed"); } + + afl_fsrv_start( + fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + +#ifdef USE_DEFLATE + compressor = libdeflate_alloc_compressor(1); + decompressor = libdeflate_alloc_decompressor(); + buf2 = afl_realloc((void **)&buf2, map_size + 16); + buf2_len = map_size + 16; + if (unlikely(!buf2)) { PFATAL("alloc"); } + lenptr = (u32 *)(buf2 + 4); + fprintf(stderr, "Compiled with compression support\n"); +#endif + + fprintf(stderr, + "Waiting for incoming connection from afl-network-client on port %d " + "...\n", + port); + + if ((s = accept(sock, NULL, NULL)) < 0) { PFATAL("accept() failed"); } + fprintf(stderr, "Received connection, starting ...\n"); + +#ifdef SO_PRIORITY + priority = 7; + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) { + + priority = 6; + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) + WARNF("could not set priority on socket"); + + } + +#endif + + while ((in_len = recv_testcase(s, (void **)&in_data)) > 0) { + + // fprintf(stderr, "received %u\n", in_len); + (void)run_target(fsrv, use_argv, in_data, in_len, 1); + + memcpy(send_buf + 4, fsrv->trace_bits, fsrv->map_size); + +#ifdef USE_DEFLATE + memcpy(buf2, &fsrv->child_status, 4); + *lenptr = (u32)libdeflate_deflate_compress( + compressor, send_buf + 4, fsrv->map_size, buf2 + 8, buf2_len - 8); + // fprintf(stderr, "COMPRESS (%u->%u): ", fsrv->map_size, *lenptr); + // for (u32 i = 0; i < fsrv->map_size; i++) fprintf(stderr, "%02x", + // fsrv->trace_bits[i]); fprintf(stderr, "\n"); + if (send(s, buf2, *lenptr + 8, 0) != 8 + *lenptr) + FATAL("could not send data"); +#else + memcpy(send_buf, &fsrv->child_status, 4); + if (send(s, send_buf, fsrv->map_size + 4, 0) != 4 + fsrv->map_size) + FATAL("could not send data"); +#endif + + // fprintf(stderr, "sent result\n"); + + } + + unlink(out_file); + if (out_file) { ck_free(out_file); } + out_file = NULL; + + afl_shm_deinit(&shm); + afl_fsrv_deinit(fsrv); + if (fsrv->target_path) { ck_free(fsrv->target_path); } + afl_free(in_data); +#if USE_DEFLATE + afl_free(buf2); + libdeflate_free_compressor(compressor); + libdeflate_free_decompressor(decompressor); +#endif + + argv_cpy_free(argv); + + exit(0); + +} + diff --git a/utils/afl_proxy/Makefile b/utils/afl_proxy/Makefile new file mode 100644 index 00000000..4b368f8d --- /dev/null +++ b/utils/afl_proxy/Makefile @@ -0,0 +1,7 @@ +all: afl-proxy + +afl-proxy: afl-proxy.c + $(CC) -I../../include -o afl-proxy afl-proxy.c + +clean: + rm -f afl-proxy *~ core diff --git a/utils/afl_proxy/README.md b/utils/afl_proxy/README.md new file mode 100644 index 00000000..3c768a19 --- /dev/null +++ b/utils/afl_proxy/README.md @@ -0,0 +1,9 @@ +# afl-proxy + +afl-proxy is an example skeleton file which can easily be used to fuzz +and instrument non-standard things. + +You only need to change the while() loop of the main() to send the +data of buf[] with length len to the target and write the coverage +information to __afl_area_ptr[__afl_map_size] + diff --git a/utils/afl_proxy/afl-proxy.c b/utils/afl_proxy/afl-proxy.c new file mode 100644 index 00000000..f2dfeac1 --- /dev/null +++ b/utils/afl_proxy/afl-proxy.c @@ -0,0 +1,238 @@ +/* + american fuzzy lop++ - afl-proxy skeleton example + --------------------------------------------------- + + Written by Marc Heuse + + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + + HOW-TO + ====== + + You only need to change the while() loop of the main() to send the + data of buf[] with length len to the target and write the coverage + information to __afl_area_ptr[__afl_map_size] + + +*/ + +#ifdef __ANDROID__ + #include "android-ashmem.h" +#endif +#include "config.h" +#include "types.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +u8 *__afl_area_ptr; + +#ifdef __ANDROID__ +u32 __afl_map_size = MAP_SIZE; +#else +__thread u32 __afl_map_size = MAP_SIZE; +#endif + +/* Error reporting to forkserver controller */ + +void send_forkserver_error(int error) { + + u32 status; + if (!error || error > 0xffff) return; + status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); + if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; + +} + +/* SHM setup. */ + +static void __afl_map_shm(void) { + + char *id_str = getenv(SHM_ENV_VAR); + char *ptr; + + if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { + + u32 val = atoi(ptr); + if (val > 0) __afl_map_size = val; + + } + + if (__afl_map_size > MAP_SIZE) { + + if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { + + fprintf(stderr, + "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + if (id_str) { + + send_forkserver_error(FS_ERROR_MAP_SIZE); + exit(-1); + + } + + } else { + + fprintf(stderr, + "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + + } + + } + + if (id_str) { + +#ifdef USEMMAP + const char * shm_file_path = id_str; + int shm_fd = -1; + unsigned char *shm_base = NULL; + + /* create the shared memory segment as if it was a file */ + shm_fd = shm_open(shm_file_path, O_RDWR, 0600); + if (shm_fd == -1) { + + fprintf(stderr, "shm_open() failed\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); + exit(1); + + } + + /* map the shared memory segment to the address space of the process */ + shm_base = + mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + + if (shm_base == MAP_FAILED) { + + close(shm_fd); + shm_fd = -1; + + fprintf(stderr, "mmap() failed\n"); + send_forkserver_error(FS_ERROR_MMAP); + exit(2); + + } + + __afl_area_ptr = shm_base; +#else + u32 shm_id = atoi(id_str); + + __afl_area_ptr = shmat(shm_id, 0, 0); + +#endif + + if (__afl_area_ptr == (void *)-1) { + + send_forkserver_error(FS_ERROR_SHMAT); + exit(1); + + } + + /* Write something into the bitmap so that the parent doesn't give up */ + + __afl_area_ptr[0] = 1; + + } + +} + +/* Fork server logic. */ + +static void __afl_start_forkserver(void) { + + u8 tmp[4] = {0, 0, 0, 0}; + u32 status = 0; + + if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) + status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); + if (status) status |= (FS_OPT_ENABLED); + memcpy(tmp, &status, 4); + + /* Phone home and tell the parent that we're OK. */ + + if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; + +} + +static u32 __afl_next_testcase(u8 *buf, u32 max_len) { + + s32 status, res = 0xffffff; + + /* Wait for parent by reading from the pipe. Abort if read fails. */ + if (read(FORKSRV_FD, &status, 4) != 4) return 0; + + /* we have a testcase - read it */ + status = read(0, buf, max_len); + + /* report that we are starting the target */ + if (write(FORKSRV_FD + 1, &res, 4) != 4) return 0; + + if (status < 1) + return 0; + else + return status; + +} + +static void __afl_end_testcase(void) { + + int status = 0xffffff; + + if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1); + +} + +/* you just need to modify the while() loop in this main() */ + +int main(int argc, char *argv[]) { + + /* This is were the testcase data is written into */ + u8 buf[1024]; // this is the maximum size for a test case! set it! + u32 len; + + /* here you specify the map size you need that you are reporting to + afl-fuzz. */ + __afl_map_size = MAP_SIZE; // default is 65536 + + /* then we initialize the shared memory map and start the forkserver */ + __afl_map_shm(); + __afl_start_forkserver(); + + while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) { + + /* here you have to create the magic that feeds the buf/len to the + target and write the coverage to __afl_area_ptr */ + + // ... the magic ... + + /* report the test case is done and wait for the next */ + __afl_end_testcase(); + + } + + return 0; + +} + diff --git a/utils/afl_untracer/Makefile b/utils/afl_untracer/Makefile new file mode 100644 index 00000000..14a09b41 --- /dev/null +++ b/utils/afl_untracer/Makefile @@ -0,0 +1,16 @@ +ifdef DEBUG + OPT=-O0 +else + OPT=-O3 +endif + +all: afl-untracer libtestinstr.so + +afl-untracer: afl-untracer.c + $(CC) $(OPT) -I../../include -g -o afl-untracer afl-untracer.c -ldl + +libtestinstr.so: libtestinstr.c + $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c + +clean: + rm -f afl-untracer libtestinstr.so *~ core diff --git a/utils/afl_untracer/README.md b/utils/afl_untracer/README.md new file mode 100644 index 00000000..ada0c916 --- /dev/null +++ b/utils/afl_untracer/README.md @@ -0,0 +1,60 @@ +# afl-untracer - fast fuzzing of binary-only libraries + +## Introduction + +afl-untracer is an example skeleton file which can easily be used to fuzz +a closed source library. + +It requires less memory and is x3-5 faster than qemu_mode however it is way +more course grained and does not provide interesting features like compcov +or cmplog. + +Supported is so far Intel (i386/x86_64) and AARCH64. + +## How-to + +### Modify afl-untracer.c + +Read and modify afl-untracer.c then `make`. +To adapt afl-untracer.c to your needs, read the header of the file and then +search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations. + +### Generate patches.txt file + +To generate the `patches.txt` file for your target library use the +`ida_get_patchpoints.py` script for IDA Pro or +`ghidra_get_patchpoints.java` for Ghidra. + +The patches.txt file has to be pointed to by `AFL_UNTRACER_FILE`. + +To easily run the scripts without needing to run the GUI with Ghidra: +``` +/opt/ghidra/support/analyzeHeadless /tmp/ tmp$$ -import libtestinstr.so -postscript ./ghidra_get_patchpoints.java +rm -rf /tmp/tmp$$ +``` +The file is created at `~/Desktop/patches.txt` + +### Fuzzing + +Example (after modifying afl-untracer.c to your needs, compiling and creating +patches.txt): +``` +LD_LIBRARY_PATH=/path/to/target/library AFL_UNTRACER_FILE=./patches.txt afl-fuzz -i in -o out -- ./afl-untracer +``` +(or even remote via afl-network-proxy). + +### Testing and debugging + +For testing/debugging you can try: +``` +make DEBUG=1 +AFL_UNTRACER_FILE=./patches.txt AFL_DEBUG=1 gdb ./afl-untracer +``` +and then you can easily set breakpoints to "breakpoint" and "fuzz". + +# Background + +This idea is based on [UnTracer](https://github.com/FoRTE-Research/UnTracer-AFL) +and modified by [Trapfuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz). +This implementation is slower because the traps are not patched out with each +run, but on the other hand gives much better coverage information. diff --git a/utils/afl_untracer/TODO b/utils/afl_untracer/TODO new file mode 100644 index 00000000..fffffacf --- /dev/null +++ b/utils/afl_untracer/TODO @@ -0,0 +1,2 @@ + * add shmem fuzzing + * add snapshot feature? diff --git a/utils/afl_untracer/afl-untracer.c b/utils/afl_untracer/afl-untracer.c new file mode 100644 index 00000000..cb6f948c --- /dev/null +++ b/utils/afl_untracer/afl-untracer.c @@ -0,0 +1,768 @@ +/* + american fuzzy lop++ - afl-untracer skeleton example + --------------------------------------------------- + + Written by Marc Heuse + + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + + HOW-TO + ====== + + You only need to change the following: + + 1. decide if you want to receive data from stdin [DEFAULT] or file(name) + -> use_stdin = 0 if via file, and what the maximum input size is + 2. dl load the library you want to fuzz, lookup the functions you need + and setup the calls to these + 3. in the while loop you call the functions in the necessary order - + incl the cleanup. the cleanup is important! + + Just look these steps up in the code, look for "// STEP x:" + + +*/ + +#define __USE_GNU +#define _GNU_SOURCE + +#ifdef __ANDROID__ + #include "android-ashmem.h" +#endif +#include "config.h" +#include "types.h" +#include "debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(__linux__) + #include +#elif defined(__APPLE__) && defined(__LP64__) + #include +#elif defined(__FreeBSD__) + #include + #include +#else + #error "Unsupported platform" +#endif + +#define MEMORY_MAP_DECREMENT 0x200000000000 +#define MAX_LIB_COUNT 128 + +// STEP 1: + +/* here you need to specify the parameter for the target function */ +static void *(*o_function)(u8 *buf, int len); + +/* use stdin (1) or a file on the commandline (0) */ +static u32 use_stdin = 1; + +/* This is were the testcase data is written into */ +static u8 buf[10000]; // this is the maximum size for a test case! set it! + +/* If you want to have debug output set this to 1, can also be set with + AFL_DEBUG */ +static u32 debug = 0; + +// END STEP 1 + +typedef struct library_list { + + u8 *name; + u64 addr_start, addr_end; + +} library_list_t; + +#ifdef __ANDROID__ +u32 __afl_map_size = MAP_SIZE; +u32 do_exit; +#else +__thread u32 __afl_map_size = MAP_SIZE; +__thread u32 do_exit; +#endif + +static pid_t pid = 65537; +static pthread_t __afl_thread; +static u8 __afl_dummy[MAP_SIZE]; +static u8 * __afl_area_ptr = __afl_dummy; +static u8 * inputfile; // this will point to argv[1] +static u32 len; + +static library_list_t liblist[MAX_LIB_COUNT]; +static u32 liblist_cnt; + +static void sigtrap_handler(int signum, siginfo_t *si, void *context); +static void fuzz(void); + +/* read the library information */ +void read_library_information(void) { + +#if defined(__linux__) + FILE *f; + u8 buf[1024], *b, *m, *e, *n; + + if ((f = fopen("/proc/self/maps", "r")) == NULL) + FATAL("cannot open /proc/self/maps"); + + if (debug) fprintf(stderr, "Library list:\n"); + while (fgets(buf, sizeof(buf), f)) { + + if (strstr(buf, " r-x")) { + + if (liblist_cnt >= MAX_LIB_COUNT) { + + WARNF("too many libraries to old, maximum count of %d reached", + liblist_cnt); + return; + + } + + b = buf; + m = index(buf, '-'); + e = index(buf, ' '); + if ((n = rindex(buf, '/')) == NULL) n = rindex(buf, ' '); + if (n && + ((*n >= '0' && *n <= '9') || *n == '[' || *n == '{' || *n == '(')) + n = NULL; + else + n++; + if (b && m && e && n && *n) { + + *m++ = 0; + *e = 0; + if (n[strlen(n) - 1] == '\n') n[strlen(n) - 1] = 0; + + liblist[liblist_cnt].name = strdup(n); + liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16); + liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16); + if (debug) + fprintf( + stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name, + liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_end - 1); + liblist_cnt++; + + } + + } + + } + + if (debug) fprintf(stderr, "\n"); + +#elif defined(__FreeBSD__) + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; + char * buf, *start, *end; + size_t miblen = sizeof(mib) / sizeof(mib[0]); + size_t len; + + if (debug) fprintf(stderr, "Library list:\n"); + if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) { return; } + + len = len * 4 / 3; + + buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); + if (buf == MAP_FAILED) { return; } + + if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) { + + munmap(buf, len); + return; + + } + + start = buf; + end = buf + len; + + while (start < end) { + + struct kinfo_vmentry *region = (struct kinfo_vmentry *)start; + size_t size = region->kve_structsize; + + if (size == 0) { break; } + + if ((region->kve_protection & KVME_PROT_READ) && + !(region->kve_protection & KVME_PROT_EXEC)) { + + liblist[liblist_cnt].name = + region->kve_path[0] != '\0' ? strdup(region->kve_path) : 0; + liblist[liblist_cnt].addr_start = region->kve_start; + liblist[liblist_cnt].addr_end = region->kve_end; + + if (debug) { + + fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name, + liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_end - 1); + + } + + liblist_cnt++; + + } + + start += size; + + } + +#endif + +} + +library_list_t *find_library(char *name) { + +#if defined(__linux__) + u32 i; + + for (i = 0; i < liblist_cnt; i++) + if (strncmp(liblist[i].name, name, strlen(name)) == 0) return &liblist[i]; +#elif defined(__APPLE__) && defined(__LP64__) + kern_return_t err; + static library_list_t lib; + + // get the list of all loaded modules from dyld + // the task_info mach API will get the address of the dyld all_image_info + // struct for the given task from which we can get the names and load + // addresses of all modules + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + err = task_info(mach_task_self(), TASK_DYLD_INFO, + (task_info_t)&task_dyld_info, &count); + + const struct dyld_all_image_infos *all_image_infos = + (const struct dyld_all_image_infos *)task_dyld_info.all_image_info_addr; + const struct dyld_image_info *image_infos = all_image_infos->infoArray; + + for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) { + + const char * image_name = image_infos[i].imageFilePath; + mach_vm_address_t image_load_address = + (mach_vm_address_t)image_infos[i].imageLoadAddress; + if (strstr(image_name, name)) { + + lib.name = name; + lib.addr_start = (u64)image_load_address; + lib.addr_end = 0; + return &lib; + + } + + } + +#endif + + return NULL; + +} + +/* for having an easy breakpoint location after loading the shared library */ +// this seems to work for clang too. nice :) requires gcc 4.4+ +#pragma GCC push_options +#pragma GCC optimize("O0") +void breakpoint(void) { + + if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n"); + +} + +#pragma GCC pop_options + +/* Error reporting to forkserver controller */ + +void send_forkserver_error(int error) { + + u32 status; + if (!error || error > 0xffff) return; + status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); + if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; + +} + +/* SHM setup. */ + +static void __afl_map_shm(void) { + + char *id_str = getenv(SHM_ENV_VAR); + char *ptr; + + if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { + + u32 val = atoi(ptr); + if (val > 0) __afl_map_size = val; + + } + + if (__afl_map_size > MAP_SIZE) { + + if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { + + fprintf(stderr, + "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + if (id_str) { + + send_forkserver_error(FS_ERROR_MAP_SIZE); + exit(-1); + + } + + } else { + + fprintf(stderr, + "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + + } + + } + + if (id_str) { + +#ifdef USEMMAP + const char * shm_file_path = id_str; + int shm_fd = -1; + unsigned char *shm_base = NULL; + + /* create the shared memory segment as if it was a file */ + shm_fd = shm_open(shm_file_path, O_RDWR, 0600); + if (shm_fd == -1) { + + fprintf(stderr, "shm_open() failed\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); + exit(1); + + } + + /* map the shared memory segment to the address space of the process */ + shm_base = + mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + + if (shm_base == MAP_FAILED) { + + close(shm_fd); + shm_fd = -1; + + fprintf(stderr, "mmap() failed\n"); + send_forkserver_error(FS_ERROR_MMAP); + exit(2); + + } + + __afl_area_ptr = shm_base; +#else + u32 shm_id = atoi(id_str); + + __afl_area_ptr = shmat(shm_id, 0, 0); + +#endif + + if (__afl_area_ptr == (void *)-1) { + + send_forkserver_error(FS_ERROR_SHMAT); + exit(1); + + } + + /* Write something into the bitmap so that the parent doesn't give up */ + + __afl_area_ptr[0] = 1; + + } + +} + +/* Fork server logic. */ +inline static void __afl_start_forkserver(void) { + + u8 tmp[4] = {0, 0, 0, 0}; + u32 status = 0; + + if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) + status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); + if (status) status |= (FS_OPT_ENABLED); + memcpy(tmp, &status, 4); + + /* Phone home and tell the parent that we're OK. */ + if (write(FORKSRV_FD + 1, tmp, 4) != 4) do_exit = 1; + // fprintf(stderr, "write0 %d\n", do_exit); + +} + +inline static u32 __afl_next_testcase(u8 *buf, u32 max_len) { + + s32 status; + + /* Wait for parent by reading from the pipe. Abort if read fails. */ + if (read(FORKSRV_FD, &status, 4) != 4) do_exit = 1; + // fprintf(stderr, "read %d\n", do_exit); + + /* we have a testcase - read it if we read from stdin */ + if (use_stdin) { + + if ((status = read(0, buf, max_len)) <= 0) exit(-1); + + } else + + status = 1; + // fprintf(stderr, "stdin: %d %d\n", use_stdin, status); + + /* report that we are starting the target */ + if (write(FORKSRV_FD + 1, &pid, 4) != 4) do_exit = 1; + // fprintf(stderr, "write1 %d\n", do_exit); + + __afl_area_ptr[0] = 1; // put something in the map + + return status; + +} + +inline static void __afl_end_testcase(int status) { + + if (write(FORKSRV_FD + 1, &status, 4) != 4) do_exit = 1; + // fprintf(stderr, "write2 %d\n", do_exit); + if (do_exit) exit(0); + +} + +#ifdef __aarch64__ + #define SHADOW(addr) \ + ((uint64_t *)(((uintptr_t)addr & 0xfffffffffffffff8) - \ + MEMORY_MAP_DECREMENT - \ + ((uintptr_t)addr & 0x7) * 0x10000000000)) +#else + #define SHADOW(addr) \ + ((uint32_t *)(((uintptr_t)addr & 0xfffffffffffffffc) - \ + MEMORY_MAP_DECREMENT - \ + ((uintptr_t)addr & 0x3) * 0x10000000000)) +#endif + +void setup_trap_instrumentation(void) { + + library_list_t *lib_base = NULL; + size_t lib_size = 0; + u8 * lib_addr; + char * line = NULL; + size_t nread, len = 0; + char * filename = getenv("AFL_UNTRACER_FILE"); + if (!filename) filename = getenv("TRAPFUZZ_FILE"); + if (!filename) FATAL("AFL_UNTRACER_FILE environment variable not set"); + + FILE *patches = fopen(filename, "r"); + if (!patches) FATAL("Couldn't open AFL_UNTRACER_FILE file %s", filename); + + // Index into the coverage bitmap for the current trap instruction. +#ifdef __aarch64__ + uint64_t bitmap_index = 0; +#else + uint32_t bitmap_index = 0; +#endif + + while ((nread = getline(&line, &len, patches)) != -1) { + + char *end = line + len; + + char *col = strchr(line, ':'); + if (col) { + + // It's a library:size pair + *col++ = 0; + + lib_base = find_library(line); + if (!lib_base) FATAL("Library %s does not appear to be loaded", line); + + // we ignore the defined lib_size + lib_size = strtoul(col, NULL, 16); +#if (__linux__) + if (lib_size < lib_base->addr_end - lib_base->addr_start) + lib_size = lib_base->addr_end - lib_base->addr_start; +#endif + if (lib_size % 0x1000 != 0) + WARNF("Invalid library size 0x%zx. Must be multiple of 0x1000", + lib_size); + + lib_addr = (u8 *)lib_base->addr_start; + + // Make library code writable. + if (mprotect((void *)lib_addr, lib_size, + PROT_READ | PROT_WRITE | PROT_EXEC) != 0) + FATAL("Failed to mprotect library %s writable", line); + + // Create shadow memory. +#ifdef __aarch64__ + for (int i = 0; i < 8; i++) { + +#else + for (int i = 0; i < 4; i++) { + +#endif + + void *shadow_addr = SHADOW(lib_addr + i); + void *shadow = mmap(shadow_addr, lib_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0); + if (debug) + fprintf(stderr, "Shadow: %s %d = %p-%p for %p\n", line, i, shadow, + shadow + lib_size - 1, lib_addr); + if (shadow == MAP_FAILED) FATAL("Failed to mmap shadow memory"); + + } + + // Done, continue with next line. + continue; + + } + + // It's an offset, parse it and do the patching. + unsigned long offset = strtoul(line, NULL, 16); + + if (offset > lib_size) + FATAL("Invalid offset: 0x%lx. Current library is 0x%zx bytes large", + offset, lib_size); + + if (bitmap_index >= __afl_map_size) + FATAL("Too many basic blocks to instrument"); + +#ifdef __arch64__ + uint64_t +#else + uint32_t +#endif + *shadow = SHADOW(lib_addr + offset); + if (*shadow != 0) continue; // skip duplicates + + // Make lookup entry in shadow memory. + +#if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__) || \ + defined(__i386__)) + + // this is for Intel x64 + + uint8_t orig_byte = lib_addr[offset]; + *shadow = (bitmap_index << 8) | orig_byte; + lib_addr[offset] = 0xcc; // replace instruction with debug trap + if (debug) + fprintf(stderr, + "Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %08x\n", + lib_addr, offset, lib_addr + offset, orig_byte, shadow, + bitmap_index, *shadow); + +#elif defined(__aarch64__) + + // this is for aarch64 + + uint32_t *patch_bytes = (uint32_t *)(lib_addr + offset); + uint32_t orig_bytes = *patch_bytes; + *shadow = (bitmap_index << 32) | orig_bytes; + *patch_bytes = 0xd4200000; // replace instruction with debug trap + if (debug) + fprintf(stderr, + "Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %016x\n", + lib_addr, offset, lib_addr + offset, orig_bytes, shadow, + bitmap_index, *shadow); + +#else + // this will be ARM and AARCH64 + // for ARM we will need to identify if the code is in thumb or ARM + #error "non x86_64/aarch64 not supported yet" + //__arm__: + // linux thumb: 0xde01 + // linux arm: 0xe7f001f0 + //__aarch64__: + // linux aarch64: 0xd4200000 +#endif + + bitmap_index++; + + } + + free(line); + fclose(patches); + + // Install signal handler for SIGTRAP. + struct sigaction s; + s.sa_flags = SA_SIGINFO; + s.sa_sigaction = sigtrap_handler; + sigemptyset(&s.sa_mask); + sigaction(SIGTRAP, &s, 0); + + if (debug) fprintf(stderr, "Patched %u locations.\n", bitmap_index); + __afl_map_size = bitmap_index; + if (__afl_map_size % 8) __afl_map_size = (((__afl_map_size + 7) >> 3) << 3); + +} + +/* the signal handler for the traps / debugging interrupts + No debug output here because this would cost speed */ +static void sigtrap_handler(int signum, siginfo_t *si, void *context) { + + uint64_t addr; + // Must re-execute the instruction, so decrement PC by one instruction. + ucontext_t *ctx = (ucontext_t *)context; +#if defined(__APPLE__) && defined(__LP64__) + ctx->uc_mcontext->__ss.__rip -= 1; + addr = ctx->uc_mcontext->__ss.__rip; +#elif defined(__linux__) + #if defined(__x86_64__) || defined(__i386__) + ctx->uc_mcontext.gregs[REG_RIP] -= 1; + addr = ctx->uc_mcontext.gregs[REG_RIP]; + #elif defined(__aarch64__) + ctx->uc_mcontext.pc -= 4; + addr = ctx->uc_mcontext.pc; + #else + #error "Unsupported processor" + #endif +#elif defined(__FreeBSD__) && defined(__LP64__) + ctx->uc_mcontext.mc_rip -= 1; + addr = ctx->uc_mcontext.mc_rip; +#else + #error "Unsupported platform" +#endif + + // fprintf(stderr, "TRAP at context addr = %lx, fault addr = %lx\n", addr, + // si->si_addr); + + // If the trap didn't come from our instrumentation, then we probably will + // just segfault here + uint8_t *faultaddr; + if (unlikely(si->si_addr)) + faultaddr = (u8 *)si->si_addr - 1; + else + faultaddr = (u8 *)addr; + // if (debug) fprintf(stderr, "Shadow location: %p\n", SHADOW(faultaddr)); + uint32_t shadow = *SHADOW(faultaddr); + uint8_t orig_byte = shadow & 0xff; + uint32_t index = shadow >> 8; + + // if (debug) fprintf(stderr, "shadow data: %x, orig_byte %02x, index %d\n", + // shadow, orig_byte, index); + + // Index zero is invalid so that it is still possible to catch actual trap + // instructions in instrumented libraries. + if (unlikely(index == 0)) abort(); + + // Restore original instruction + *faultaddr = orig_byte; + + __afl_area_ptr[index] = 128; + +} + +/* the MAIN function */ +int main(int argc, char *argv[]) { + + (void)personality(ADDR_NO_RANDOMIZE); // disable ASLR + + pid = getpid(); + if (getenv("AFL_DEBUG")) debug = 1; + + /* by default we use stdin, but also a filename can be passed, in this + case the input is argv[1] and we have to disable stdin */ + if (argc > 1) { + + use_stdin = 0; + inputfile = argv[1]; + + } + + // STEP 2: load the library you want to fuzz and lookup the functions, + // inclusive of the cleanup functions + // NOTE: above the main() you have to define the functions! + + void *dl = dlopen("./libtestinstr.so", RTLD_LAZY); + if (!dl) FATAL("could not find target library"); + o_function = dlsym(dl, "testinstr"); + if (!o_function) FATAL("could not resolve target function from library"); + if (debug) fprintf(stderr, "Function address: %p\n", o_function); + + // END STEP 2 + + /* setup instrumentation, shared memory and forkserver */ + breakpoint(); + read_library_information(); + setup_trap_instrumentation(); + __afl_map_shm(); + __afl_start_forkserver(); + + while (1) { + + // instead of fork() we could also use the snapshot lkm or do our own mini + // snapshot feature like in https://github.com/marcinguy/fuzzer + // -> snapshot.c + if ((pid = fork()) == -1) PFATAL("fork failed"); + + if (pid) { + + u32 status; + if (waitpid(pid, &status, 0) < 0) exit(1); + /* report the test case is done and wait for the next */ + __afl_end_testcase(status); + + } else { + + pid = getpid(); + while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) { + + // in this function the fuzz magic happens, this is STEP 3 + fuzz(); + + // we can use _exit which is faster because our target library + // was loaded via dlopen and therefore cannot have deconstructors + // registered. + _exit(0); + + } + + } + + } + + return 0; + +} + +#ifndef _DEBUG +inline +#endif + static void + fuzz(void) { + + // STEP 3: call the function to fuzz, also the functions you might + // need to call to prepare the function and - important! - + // to clean everything up + + // in this example we use the input file, not stdin! + (*o_function)(buf, len); + + // normally you also need to cleanup + //(*o_LibFree)(foo); + + // END STEP 3 + +} + diff --git a/utils/afl_untracer/ghidra_get_patchpoints.java b/utils/afl_untracer/ghidra_get_patchpoints.java new file mode 100644 index 00000000..2a93642b --- /dev/null +++ b/utils/afl_untracer/ghidra_get_patchpoints.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Find patch points for untracer tools (e.g. afl++ utils/afl_untracer) +// +// Copy to ..../Ghidra/Features/Search/ghidra_scripts/ +// Writes the results to ~/Desktop/patches.txt +// +// This is my very first Ghidra script. I am sure this could be done better. +// +//@category Search + +import ghidra.app.script.GhidraScript; +import ghidra.program.model.address.*; +import ghidra.program.model.block.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.symbol.*; +import ghidra.program.model.mem.*; + +import java.io.*; + +public class ghidra_get_patchpoints extends GhidraScript { + + @Override + public void run() throws Exception { + + long segment_start = 0; + Memory memory = currentProgram.getMemory(); + MultEntSubModel model = new MultEntSubModel(currentProgram); + CodeBlockIterator subIter = model.getCodeBlocks(monitor); + BufferedWriter out = new BufferedWriter(new FileWriter(System.getProperty("user.home") + File.separator + "Desktop" + File.separator + "patches.txt")); + + while (subIter.hasNext()) { + + CodeBlock multiEntryBlock = subIter.next(); + SimpleBlockModel basicBlockModel = new SimpleBlockModel(currentProgram); + CodeBlockIterator bbIter = basicBlockModel.getCodeBlocksContaining(multiEntryBlock, monitor); + + while (bbIter.hasNext()) { + + CodeBlock basicBlock = bbIter.next(); + + if (segment_start == 0) { + + Address firstAddr = basicBlock.getFirstStartAddress(); + long firstBlockAddr = firstAddr.getAddressableWordOffset(); + MemoryBlock mb = memory.getBlock(firstAddr); + Address startAddr = mb.getStart(); + Address endAddr = mb.getEnd(); + segment_start = startAddr.getAddressableWordOffset(); + if ((firstBlockAddr - segment_start) >= 0x1000) + segment_start += 0x1000; + long segment_end = endAddr.getAddressableWordOffset(); + long segment_size = segment_end - segment_start; + if ((segment_size % 0x1000) > 0) + segment_size = (((segment_size / 0x1000) + 1) * 0x1000); + out.write(currentProgram.getName() + ":0x" + Long.toHexString(segment_size) + "\n"); + //println("Start: " + Long.toHexString(segment_start)); + //println("End: " + Long.toHexString(segment_end)); + + } + + if (basicBlock.getFirstStartAddress().getAddressableWordOffset() - segment_start > 0) + out.write("0x" + Long.toHexString(basicBlock.getFirstStartAddress().getAddressableWordOffset() - segment_start) + "\n"); + + } + } + + out.close(); + + } +} diff --git a/utils/afl_untracer/ida_get_patchpoints.py b/utils/afl_untracer/ida_get_patchpoints.py new file mode 100644 index 00000000..43cf6d89 --- /dev/null +++ b/utils/afl_untracer/ida_get_patchpoints.py @@ -0,0 +1,62 @@ +# +# IDAPython script for IDA Pro +# Slightly modified from https://github.com/googleprojectzero/p0tools/blob/master/TrapFuzz/findPatchPoints.py +# + +import idautils +import idaapi +import ida_nalt +import idc + +# See https://www.hex-rays.com/products/ida/support/ida74_idapython_no_bc695_porting_guide.shtml + +from os.path import expanduser +home = expanduser("~") + +patchpoints = set() + +max_offset = 0 +for seg_ea in idautils.Segments(): + name = idc.get_segm_name(seg_ea) + #print("Segment: " + name) + if name != "__text" and name != ".text": + continue + + start = idc.get_segm_start(seg_ea) + end = idc.get_segm_end(seg_ea) + first = 0 + subtract_addr = 0 + #print("Start: " + hex(start) + " End: " + hex(end)) + for func_ea in idautils.Functions(start, end): + f = idaapi.get_func(func_ea) + if not f: + continue + for block in idaapi.FlowChart(f): + if start <= block.start_ea < end: + if first == 0: + if block.start_ea >= 0x1000: + subtract_addr = 0x1000 + first = 1 + + max_offset = max(max_offset, block.start_ea) + patchpoints.add(block.start_ea - subtract_addr) + #else: + # print("Warning: broken CFG?") + +# Round up max_offset to page size +size = max_offset +rem = size % 0x1000 +if rem != 0: + size += 0x1000 - rem + +print("Writing to " + home + "/Desktop/patches.txt") + +with open(home + "/Desktop/patches.txt", "w") as f: + f.write(ida_nalt.get_root_filename() + ':' + hex(size) + '\n') + f.write('\n'.join(map(hex, sorted(patchpoints)))) + f.write('\n') + +print("Done, found {} patchpoints".format(len(patchpoints))) + +# For headless script running remove the comment from the next line +#ida_pro.qexit() diff --git a/utils/afl_untracer/libtestinstr.c b/utils/afl_untracer/libtestinstr.c new file mode 100644 index 00000000..96b1cf21 --- /dev/null +++ b/utils/afl_untracer/libtestinstr.c @@ -0,0 +1,35 @@ +/* + american fuzzy lop++ - a trivial program to test the build + -------------------------------------------------------- + Originally written by Michal Zalewski + Copyright 2014 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + http://www.apache.org/licenses/LICENSE-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +void testinstr(char *buf, int len) { + + if (len < 1) return; + buf[len] = 0; + + // we support three input cases + if (buf[0] == '0') + printf("Looks like a zero to me!\n"); + else if (buf[0] == '1') + printf("Pretty sure that is a one!\n"); + else + printf("Neither one or zero? How quaint!\n"); + +} + diff --git a/utils/afl_untracer/patches.txt b/utils/afl_untracer/patches.txt new file mode 100644 index 00000000..7e964249 --- /dev/null +++ b/utils/afl_untracer/patches.txt @@ -0,0 +1,34 @@ +libtestinstr.so:0x1000 +0x10 +0x12 +0x20 +0x36 +0x30 +0x40 +0x50 +0x63 +0x6f +0x78 +0x80 +0xa4 +0xb0 +0xb8 +0x100 +0xc0 +0xc9 +0xd7 +0xe3 +0xe8 +0xf8 +0x105 +0x11a +0x135 +0x141 +0x143 +0x14e +0x15a +0x15c +0x168 +0x16a +0x16b +0x170 diff --git a/utils/aflpp_driver/GNUmakefile b/utils/aflpp_driver/GNUmakefile new file mode 100644 index 00000000..c1a087d7 --- /dev/null +++ b/utils/aflpp_driver/GNUmakefile @@ -0,0 +1,46 @@ +ifeq "" "$(LLVM_CONFIG)" + LLVM_CONFIG=llvm-config +endif + +LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null) +ifneq "" "$(LLVM_BINDIR)" + LLVM_BINDIR := $(LLVM_BINDIR)/ +endif + +CFLAGS := -O3 -funroll-loops -g + +all: libAFLDriver.a libAFLQemuDriver.a aflpp_qemu_driver_hook.so + +aflpp_driver.o: aflpp_driver.c + -$(LLVM_BINDIR)clang -I. -I../../include $(CFLAGS) -c aflpp_driver.c + +libAFLDriver.a: aflpp_driver.o + ar ru libAFLDriver.a aflpp_driver.o + cp -vf libAFLDriver.a ../../ + +debug: + $(LLVM_BINDIR)clang -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.o ../../src/afl-performance.c + $(LLVM_BINDIR)clang -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c + #$(LLVM_BINDIR)clang -S -emit-llvm -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.ll ../../src/afl-performance.c + #$(LLVM_BINDIR)clang -S -emit-llvm -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c + ar ru libAFLDriver.a afl-performance.o aflpp_driver.o + +aflpp_qemu_driver.o: aflpp_qemu_driver.c + $(LLVM_BINDIR)clang $(CFLAGS) -O0 -funroll-loops -c aflpp_qemu_driver.c + +libAFLQemuDriver.a: aflpp_qemu_driver.o + ar ru libAFLQemuDriver.a aflpp_qemu_driver.o + cp -vf libAFLQemuDriver.a ../../ + +aflpp_qemu_driver_hook.so: aflpp_qemu_driver_hook.o + $(LLVM_BINDIR)clang -shared aflpp_qemu_driver_hook.o -o aflpp_qemu_driver_hook.so + +aflpp_qemu_driver_hook.o: aflpp_qemu_driver_hook.c + $(LLVM_BINDIR)clang -fPIC $(CFLAGS) -funroll-loops -c aflpp_qemu_driver_hook.c + +test: debug + #clang -S -emit-llvm -D_DEBUG=\"1\" -I../../include -Wl,--allow-multiple-definition -funroll-loops -o aflpp_driver_test.ll aflpp_driver_test.c + afl-clang-fast -D_DEBUG=\"1\" -I../../include -Wl,--allow-multiple-definition -funroll-loops -o aflpp_driver_test aflpp_driver_test.c libAFLDriver.a afl-performance.o + +clean: + rm -f *.o libAFLDriver*.a libAFLQemuDriver.a aflpp_qemu_driver_hook.so *~ core aflpp_driver_test diff --git a/utils/aflpp_driver/Makefile b/utils/aflpp_driver/Makefile new file mode 100644 index 00000000..3666a74d --- /dev/null +++ b/utils/aflpp_driver/Makefile @@ -0,0 +1,2 @@ +all: + @gmake all || echo please install GNUmake diff --git a/utils/aflpp_driver/aflpp_driver.c b/utils/aflpp_driver/aflpp_driver.c new file mode 100644 index 00000000..017aa72b --- /dev/null +++ b/utils/aflpp_driver/aflpp_driver.c @@ -0,0 +1,326 @@ +//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +//===----------------------------------------------------------------------===// + +/* This file allows to fuzz libFuzzer-style target functions + (LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode. + +Usage: +################################################################################ +cat << EOF > test_fuzzer.cc +#include +#include +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + if (size > 0 && data[0] == 'H') + if (size > 1 && data[1] == 'I') + if (size > 2 && data[2] == '!') + __builtin_trap(); + return 0; + +} + +EOF +# Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang. +clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c +# Build afl-llvm-rt.o.c from the AFL distribution. +clang -c -w $AFL_HOME/instrumentation/afl-llvm-rt.o.c +# Build this file, link it with afl-llvm-rt.o.o and the target code. +clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o +# Run AFL: +rm -rf IN OUT; mkdir IN OUT; echo z > IN/z; +$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out +################################################################################ +AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file +specified. If the file does not exist, it is created. This is useful for getting +stack traces (when using ASAN for example) or original error messages on hard +to reproduce bugs. Note that any content written to stderr will be written to +this file instead of stderr's usual location. + +AFL_DRIVER_CLOSE_FD_MASK: Similar to libFuzzer's -close_fd_mask behavior option. +If 1, close stdout at startup. If 2 close stderr; if 3 close both. + +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "cmplog.h" + +#ifdef _DEBUG + #include "hash.h" +#endif + +#ifndef MAP_FIXED_NOREPLACE + #define MAP_FIXED_NOREPLACE 0x100000 +#endif + +#define MAX_DUMMY_SIZE 256000 + +// Platform detection. Copied from FuzzerInternal.h +#ifdef __linux__ + #define LIBFUZZER_LINUX 1 + #define LIBFUZZER_APPLE 0 + #define LIBFUZZER_NETBSD 0 + #define LIBFUZZER_FREEBSD 0 + #define LIBFUZZER_OPENBSD 0 +#elif __APPLE__ + #define LIBFUZZER_LINUX 0 + #define LIBFUZZER_APPLE 1 + #define LIBFUZZER_NETBSD 0 + #define LIBFUZZER_FREEBSD 0 + #define LIBFUZZER_OPENBSD 0 +#elif __NetBSD__ + #define LIBFUZZER_LINUX 0 + #define LIBFUZZER_APPLE 0 + #define LIBFUZZER_NETBSD 1 + #define LIBFUZZER_FREEBSD 0 + #define LIBFUZZER_OPENBSD 0 +#elif __FreeBSD__ + #define LIBFUZZER_LINUX 0 + #define LIBFUZZER_APPLE 0 + #define LIBFUZZER_NETBSD 0 + #define LIBFUZZER_FREEBSD 1 + #define LIBFUZZER_OPENBSD 0 +#elif __OpenBSD__ + #define LIBFUZZER_LINUX 0 + #define LIBFUZZER_APPLE 0 + #define LIBFUZZER_NETBSD 0 + #define LIBFUZZER_FREEBSD 0 + #define LIBFUZZER_OPENBSD 1 +#else + #error "Support for your platform has not been implemented" +#endif + +int __afl_sharedmem_fuzzing = 1; +extern unsigned int * __afl_fuzz_len; +extern unsigned char *__afl_fuzz_ptr; + +// libFuzzer interface is thin, so we don't include any libFuzzer headers. +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); + +// Notify AFL about persistent mode. +static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; +int __afl_persistent_loop(unsigned int); + +// Notify AFL about deferred forkserver. +static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; +void __afl_manual_init(); + +// Use this optionally defined function to output sanitizer messages even if +// user asks to close stderr. +__attribute__((weak)) void __sanitizer_set_report_fd(void *); + +// Keep track of where stderr content is being written to, so that +// dup_and_close_stderr can use the correct one. +static FILE *output_file; + +// Experimental feature to use afl_driver without AFL's deferred mode. +// Needs to run before __afl_auto_init. +__attribute__((constructor(0))) static void __decide_deferred_forkserver(void) { + + if (getenv("AFL_DRIVER_DONT_DEFER")) { + + if (unsetenv("__AFL_DEFER_FORKSRV")) { + + perror("Failed to unset __AFL_DEFER_FORKSRV"); + abort(); + + } + + } + +} + +// If the user asks us to duplicate stderr, then do it. +static void maybe_duplicate_stderr() { + + char *stderr_duplicate_filename = + getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); + + if (!stderr_duplicate_filename) return; + + FILE *stderr_duplicate_stream = + freopen(stderr_duplicate_filename, "a+", stderr); + + if (!stderr_duplicate_stream) { + + fprintf( + stderr, + "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); + abort(); + + } + + output_file = stderr_duplicate_stream; + +} + +// Most of these I/O functions were inspired by/copied from libFuzzer's code. +static void discard_output(int fd) { + + FILE *temp = fopen("/dev/null", "w"); + if (!temp) abort(); + dup2(fileno(temp), fd); + fclose(temp); + +} + +static void close_stdout() { + + discard_output(STDOUT_FILENO); + +} + +// Prevent the targeted code from writing to "stderr" but allow sanitizers and +// this driver to do so. +static void dup_and_close_stderr() { + + int output_fileno = fileno(output_file); + int output_fd = dup(output_fileno); + if (output_fd <= 0) abort(); + FILE *new_output_file = fdopen(output_fd, "w"); + if (!new_output_file) abort(); + if (!__sanitizer_set_report_fd) return; + __sanitizer_set_report_fd((void *)(long int)output_fd); + discard_output(output_fileno); + +} + +// Close stdout and/or stderr if user asks for it. +static void maybe_close_fd_mask() { + + char *fd_mask_str = getenv("AFL_DRIVER_CLOSE_FD_MASK"); + if (!fd_mask_str) return; + int fd_mask = atoi(fd_mask_str); + if (fd_mask & 2) dup_and_close_stderr(); + if (fd_mask & 1) close_stdout(); + +} + +// Define LLVMFuzzerMutate to avoid link failures for targets that use it +// with libFuzzer's LLVMFuzzerCustomMutator. +size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { + + // assert(false && "LLVMFuzzerMutate should not be called from afl_driver"); + return 0; + +} + +// Execute any files provided as parameters. +static int ExecuteFilesOnyByOne(int argc, char **argv) { + + unsigned char *buf = malloc(MAX_FILE); + for (int i = 1; i < argc; i++) { + + int fd = open(argv[i], O_RDONLY); + if (fd == -1) continue; + ssize_t length = read(fd, buf, MAX_FILE); + if (length > 0) { + + printf("Reading %zu bytes from %s\n", length, argv[i]); + LLVMFuzzerTestOneInput(buf, length); + printf("Execution successful.\n"); + + } + + } + + free(buf); + return 0; + +} + +int main(int argc, char **argv) { + + printf( + "======================= INFO =========================\n" + "This binary is built for afl++.\n" + "To run the target function on individual input(s) execute this:\n" + " %s INPUT_FILE1 [INPUT_FILE2 ... ]\n" + "To fuzz with afl-fuzz execute this:\n" + " afl-fuzz [afl-flags] -- %s [-N]\n" + "afl-fuzz will run N iterations before re-spawning the process (default: " + "1000)\n" + "======================================================\n", + argv[0], argv[0]); + + output_file = stderr; + maybe_duplicate_stderr(); + maybe_close_fd_mask(); + if (LLVMFuzzerInitialize) { + + fprintf(stderr, "Running LLVMFuzzerInitialize ...\n"); + LLVMFuzzerInitialize(&argc, &argv); + fprintf(stderr, "continue...\n"); + + } + + // Do any other expensive one-time initialization here. + + uint8_t dummy_input[64] = {0}; + memcpy(dummy_input, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT)); + memcpy(dummy_input + 32, (void *)AFL_DEFER_FORKSVR, + sizeof(AFL_DEFER_FORKSVR)); + int N = INT_MAX; + if (argc == 2 && argv[1][0] == '-') + N = atoi(argv[1] + 1); + else if (argc == 2 && (N = atoi(argv[1])) > 0) + printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N); + else if (argc > 1) { + + __afl_sharedmem_fuzzing = 0; + __afl_manual_init(); + return ExecuteFilesOnyByOne(argc, argv); + + } + + assert(N > 0); + + // if (!getenv("AFL_DRIVER_DONT_DEFER")) + __afl_manual_init(); + + // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization + // on the first execution of LLVMFuzzerTestOneInput is ignored. + LLVMFuzzerTestOneInput(dummy_input, 1); + + int num_runs = 0; + while (__afl_persistent_loop(N)) { + +#ifdef _DEBUG + fprintf(stderr, "CLIENT crc: %016llx len: %u\n", + hash64(__afl_fuzz_ptr, *__afl_fuzz_len, 0xa5b35705), + *__afl_fuzz_len); + fprintf(stderr, "RECV:"); + for (int i = 0; i < *__afl_fuzz_len; i++) + fprintf(stderr, "%02x", __afl_fuzz_ptr[i]); + fprintf(stderr, "\n"); +#endif + if (*__afl_fuzz_len) { + + num_runs++; + LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len); + + } + + } + + printf("%s: successfully executed %d input(s)\n", argv[0], num_runs); + +} + diff --git a/utils/aflpp_driver/aflpp_driver_test.c b/utils/aflpp_driver/aflpp_driver_test.c new file mode 100644 index 00000000..b4ff6bc6 --- /dev/null +++ b/utils/aflpp_driver/aflpp_driver_test.c @@ -0,0 +1,32 @@ +#include +#include +#include + +#include "hash.h" + +void __attribute__((noinline)) crashme(const uint8_t *Data, size_t Size) { + + if (Size < 5) return; + + if (Data[0] == 'F') + if (Data[1] == 'A') + if (Data[2] == '$') + if (Data[3] == '$') + if (Data[4] == '$') abort(); + +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + + if (Size) + fprintf(stderr, "FUNC crc: %016llx len: %lu\n", + hash64((u8 *)Data, (unsigned int)Size, + (unsigned long long int)0xa5b35705), + Size); + + crashme(Data, Size); + + return 0; + +} + diff --git a/utils/aflpp_driver/aflpp_qemu_driver.c b/utils/aflpp_driver/aflpp_qemu_driver.c new file mode 100644 index 00000000..4f3e5f71 --- /dev/null +++ b/utils/aflpp_driver/aflpp_qemu_driver.c @@ -0,0 +1,38 @@ +#include +#include +#include + +// libFuzzer interface is thin, so we don't include any libFuzzer headers. +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); + +static const size_t kMaxAflInputSize = 1 * 1024 * 1024; +static uint8_t AflInputBuf[kMaxAflInputSize]; + +void __attribute__((noinline)) afl_qemu_driver_stdin_input(void) { + + size_t l = read(0, AflInputBuf, kMaxAflInputSize); + LLVMFuzzerTestOneInput(AflInputBuf, l); + +} + +int main(int argc, char **argv) { + + if (LLVMFuzzerInitialize) LLVMFuzzerInitialize(&argc, &argv); + // Do any other expensive one-time initialization here. + + if (getenv("AFL_QEMU_DRIVER_NO_HOOK")) { + + afl_qemu_driver_stdin_input(); + + } else { + + uint8_t dummy_input[1024000] = {0}; + LLVMFuzzerTestOneInput(dummy_input, 1); + + } + + return 0; + +} + diff --git a/utils/aflpp_driver/aflpp_qemu_driver_hook.c b/utils/aflpp_driver/aflpp_qemu_driver_hook.c new file mode 100644 index 00000000..823cc42d --- /dev/null +++ b/utils/aflpp_driver/aflpp_qemu_driver_hook.c @@ -0,0 +1,22 @@ +#include +#include + +#define g2h(x) ((void *)((unsigned long)(x) + guest_base)) + +#define REGS_RDI 7 +#define REGS_RSI 6 + +void afl_persistent_hook(uint64_t *regs, uint64_t guest_base, + uint8_t *input_buf, uint32_t input_len) { + + memcpy(g2h(regs[REGS_RDI]), input_buf, input_len); + regs[REGS_RSI] = input_len; + +} + +int afl_persistent_hook_init(void) { + + return 1; + +} + diff --git a/utils/analysis_scripts/queue2csv.sh b/utils/analysis_scripts/queue2csv.sh new file mode 100755 index 00000000..2528b438 --- /dev/null +++ b/utils/analysis_scripts/queue2csv.sh @@ -0,0 +1,122 @@ +#!/bin/bash + +test -z "$1" -o -z "$2" -o "$1" = "-h" -o "$1" = "-hh" -o "$1" = "--help" -o '!' -d "$1" && { + echo "Syntax: [-n] $0 out-directory file.csv [\"tools/target --opt @@\"]" + echo Option -n will suppress the CSV header. + echo If the target execution command is supplied then also edge coverage is gathered. + exit 1 +} + +function getval() { + VAL="" + if [ "$file" != "${file/$1/}" ]; then + TMP="${file/*$1:/}" + VAL="${TMP/,*/}" + fi +} + +SKIP= +if [ "$1" = "-n" ]; then + SKIP=1 + shift +fi + +test -n "$4" && { echo "Error: too many commandline options. Target command and options including @@ have to be passed within \"\"!"; exit 1; } + +test -d "$1"/queue && OUT="$1/queue" || OUT="$1" + +OK=`ls $OUT/id:000000,time:0,orig:* 2> /dev/null` +if [ -n "$OK" ]; then + LISTCMD="ls $OUT/id:"* +else + LISTCMD="ls -tr $OUT/" +fi + +ID=;SRC=;TIME=;OP=;POS=;REP=;EDGES=;EDGES_TOTAL=; +DIR="$OUT/../stats" +rm -rf "$DIR" +> "$2" || exit 1 +mkdir "$DIR" || exit 1 +> "$DIR/../edges.txt" || exit 1 + +{ + + if [ -z "$SKIP" ]; then + echo "time;\"filename\";id;src;new_cov;edges;total_edges;\"op\";pos;rep;unique_edges" + fi + + $LISTCMD | grep -v ,sync: | sed 's/.*id:/id:/g' | while read file; do + + if [ -n "$3" ]; then + + TMP=${3/@@/$OUT/$file} + + if [ "$TMP" = "$3" ]; then + + cat "$OUT/$file" | afl-showmap -o "$DIR/$file" -q -- $3 >/dev/null 2>&1 + + else + + afl-showmap -o "$DIR/$file" -q -- $TMP >/dev/null 2>&1 + + fi + + { cat "$DIR/$file" | sed 's/:.*//' ; cat "$DIR/../edges.txt" ; } | sort -nu > $DIR/../edges.txt.tmp + mv $DIR/../edges.txt.tmp $DIR/../edges.txt + EDGES=$(cat "$DIR/$file" | wc -l) + EDGES_TOTAL=$(cat "$DIR/../edges.txt" | wc -l) + + fi + + getval id; ID="$VAL" + getval src; SRC="$VAL" + getval time; TIME="$VAL" + getval op; OP="$VAL" + getval pos; POS="$VAL" + getval rep; REP="$VAL" + if [ "$file" != "${file/+cov/}" ]; then + COV=1 + else + COV="" + fi + + if [ -n "$3" -a -s "$DIR/../edges.txt" ]; then + echo "$TIME;\"$file\";$ID;$SRC;$COV;$EDGES;$EDGES_TOTAL;\"$OP\";$POS;$REP;UNIQUE$file" + else + echo "$TIME;\"$file\";$ID;$SRC;$COV;;;\"$OP\";$POS;$REP;" + fi + + done + +} | tee "$DIR/../queue.csv" > "$2" || exit 1 + +if [ -n "$3" -a -s "$DIR/../edges.txt" ]; then + + cat "$DIR/"* | sed 's/:.*//' | sort -n | uniq -c | egrep '^[ \t]*1 ' | awk '{print$2}' > $DIR/../unique.txt + + if [ -s "$DIR/../unique.txt" ]; then + + ls "$DIR/id:"* | grep -v ",sync:" |sed 's/.*\/id:/id:/g' | while read file; do + + CNT=$(sed 's/:.*//' "$DIR/$file" | tee "$DIR/../tmp.txt" | wc -l) + DIFF=$(diff -u "$DIR/../tmp.txt" "$DIR/../unique.txt" | egrep '^-[0-9]' | wc -l) + UNIQUE=$(($CNT - $DIFF)) + sed -i "s/;UNIQUE$file/;$UNIQUE/" "$DIR/../queue.csv" "$2" + + done + + rm -f "$DIR/../tmp.txt" + + else + + sed -i 's/;UNIQUE.*/;/' "$DIR/../queue.csv" "$2" + + fi + +fi + +mv "$DIR/../queue.csv" "$DIR/queue.csv" +if [ -e "$DIR/../edges.txt" ]; then mv "$DIR/../edges.txt" "$DIR/edges.txt"; fi +if [ -e "$DIR/../unique.txt" ]; then mv "$DIR/../unique.txt" "$DIR/unique.txt"; fi + +echo "Created $2" diff --git a/utils/argv_fuzzing/Makefile b/utils/argv_fuzzing/Makefile new file mode 100644 index 00000000..5a0ac6e6 --- /dev/null +++ b/utils/argv_fuzzing/Makefile @@ -0,0 +1,58 @@ +# +# american fuzzy lop++ - argvfuzz +# -------------------------------- +# +# Copyright 2019-2020 Kjell Braden +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +.PHONY: all install clean + +PREFIX ?= /usr/local +BIN_PATH = $(PREFIX)/bin +HELPER_PATH = $(PREFIX)/lib/afl + +CFLAGS = -fPIC -Wall -Wextra +LDFLAGS = -shared + +UNAME_SAYS_LINUX=$(shell uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?) +UNAME_SAYS_LINUX:sh=uname | grep -E '^Linux|^GNU' >/dev/null; echo $$? + +_LDFLAGS_ADD=$(UNAME_SAYS_LINUX:1=) +LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-ldl) +LDFLAGS += $(LDFLAGS_ADD) + +# on gcc for arm there is no -m32, but -mbe32 +M32FLAG = -m32 +M64FLAG = -m64 + +CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?) +CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$? +CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?) +CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$? + +_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER) +__M32FLAG=$(_M32FLAG:00=-mbe32) +___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32) +M32FLAG=$(___M32FLAG) + +all: argvfuzz32.so argvfuzz64.so + +argvfuzz32.so: argvfuzz.c + -@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "argvfuzz32 build failure (that's fine)" + +argvfuzz64.so: argvfuzz.c + -@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "argvfuzz64 build failure (that's fine)" + +install: argvfuzz32.so argvfuzz64.so + install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ + if [ -f argvfuzz32.so ]; then set -e; install -m 755 argvfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi + if [ -f argvfuzz64.so ]; then set -e; install -m 755 argvfuzz64.so $(DESTDIR)$(HELPER_PATH)/; fi + +clean: + rm -f argvfuzz32.so argvfuzz64.so diff --git a/utils/argv_fuzzing/README.md b/utils/argv_fuzzing/README.md new file mode 100644 index 00000000..fa8cad80 --- /dev/null +++ b/utils/argv_fuzzing/README.md @@ -0,0 +1,16 @@ +# argvfuzz + +afl supports fuzzing file inputs or stdin. When source is available, +`argv-fuzz-inl.h` can be used to change `main()` to build argv from stdin. + +`argvfuzz` tries to provide the same functionality for binaries. When loaded +using `LD_PRELOAD`, it will hook the call to `__libc_start_main` and replace +argv using the same logic of `argv-fuzz-inl.h`. + +A few conditions need to be fulfilled for this mechanism to work correctly: + +1. As it relies on hooking the loader, it cannot work on static binaries. +2. If the target binary does not use the default libc's `_start` implementation + (crt1.o), the hook may not run. +3. The hook will replace argv with pointers to `.data` of `argvfuzz.so`. If the + target binary expects argv to be living on the stack, things may go wrong. diff --git a/utils/argv_fuzzing/argv-fuzz-inl.h b/utils/argv_fuzzing/argv-fuzz-inl.h new file mode 100644 index 00000000..c15c0271 --- /dev/null +++ b/utils/argv_fuzzing/argv-fuzz-inl.h @@ -0,0 +1,90 @@ +/* + american fuzzy lop++ - sample argv fuzzing wrapper + ------------------------------------------------ + + Originally written by Michal Zalewski + + Copyright 2015 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This file shows a simple way to fuzz command-line parameters with stock + afl-fuzz. To use, add: + + #include "/path/to/argv-fuzz-inl.h" + + ...to the file containing main(), ideally placing it after all the + standard includes. Next, put AFL_INIT_ARGV(); near the very beginning of + main(). + + This will cause the program to read NUL-delimited input from stdin and + put it in argv[]. Two subsequent NULs terminate the array. Empty + params are encoded as a lone 0x02. Lone 0x02 can't be generated, but + that shouldn't matter in real life. + + If you would like to always preserve argv[0], use this instead: + AFL_INIT_SET0("prog_name"); + +*/ + +#ifndef _HAVE_ARGV_FUZZ_INL +#define _HAVE_ARGV_FUZZ_INL + +#include + +#define AFL_INIT_ARGV() \ + do { \ + \ + argv = afl_init_argv(&argc); \ + \ + } while (0) + +#define AFL_INIT_SET0(_p) \ + do { \ + \ + argv = afl_init_argv(&argc); \ + argv[0] = (_p); \ + if (!argc) argc = 1; \ + \ + } while (0) + +#define MAX_CMDLINE_LEN 100000 +#define MAX_CMDLINE_PAR 50000 + +static char **afl_init_argv(int *argc) { + + static char in_buf[MAX_CMDLINE_LEN]; + static char *ret[MAX_CMDLINE_PAR]; + + char *ptr = in_buf; + int rc = 0; + + if (read(0, in_buf, MAX_CMDLINE_LEN - 2) < 0) {} + + while (*ptr && rc < MAX_CMDLINE_PAR) { + + ret[rc] = ptr; + if (ret[rc][0] == 0x02 && !ret[rc][1]) ret[rc]++; + rc++; + + while (*ptr) + ptr++; + ptr++; + + } + + *argc = rc; + + return ret; + +} + +#undef MAX_CMDLINE_LEN +#undef MAX_CMDLINE_PAR + +#endif /* !_HAVE_ARGV_FUZZ_INL */ + diff --git a/utils/argv_fuzzing/argvfuzz.c b/utils/argv_fuzzing/argvfuzz.c new file mode 100644 index 00000000..4251ca4c --- /dev/null +++ b/utils/argv_fuzzing/argvfuzz.c @@ -0,0 +1,49 @@ +/* + american fuzzy lop++ - LD_PRELOAD for fuzzing argv in binaries + ------------------------------------------------------------ + + Copyright 2019-2020 Kjell Braden + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + */ + +#define _GNU_SOURCE /* for RTLD_NEXT */ +#include +#include +#include +#include +#include "argv-fuzz-inl.h" + +int __libc_start_main(int (*main)(int, char **, char **), int argc, char **argv, + void (*init)(void), void (*fini)(void), + void (*rtld_fini)(void), void *stack_end) { + + int (*orig)(int (*main)(int, char **, char **), int argc, char **argv, + void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), + void *stack_end); + int sub_argc; + char **sub_argv; + + (void)argc; + (void)argv; + + orig = dlsym(RTLD_NEXT, __func__); + + if (!orig) { + + fprintf(stderr, "hook did not find original %s: %s\n", __func__, dlerror()); + exit(EXIT_FAILURE); + + } + + sub_argv = afl_init_argv(&sub_argc); + + return orig(main, sub_argc, sub_argv, init, fini, rtld_fini, stack_end); + +} + diff --git a/utils/asan_cgroups/limit_memory.sh b/utils/asan_cgroups/limit_memory.sh new file mode 100755 index 00000000..1f0f04ad --- /dev/null +++ b/utils/asan_cgroups/limit_memory.sh @@ -0,0 +1,157 @@ +#!/usr/bin/env bash +# +# american fuzzy lop++ - limit memory using cgroups +# ----------------------------------------------- +# +# Written by Samir Khakimov and +# David A. Wheeler +# +# Edits to bring the script in line with afl-cmin and other companion scripts +# by Michal Zalewski. All bugs are my fault. +# +# Copyright 2015 Institute for Defense Analyses. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# This tool allows the amount of actual memory allocated to a program +# to be limited on Linux systems using cgroups, instead of the traditional +# setrlimit() API. This helps avoid the address space problems discussed in +# docs/notes_for_asan.md. +# +# Important: the limit covers *both* afl-fuzz and the fuzzed binary. In some +# hopefully rare circumstances, afl-fuzz could be killed before the fuzzed +# task. +# + +echo "cgroup tool for afl-fuzz by and " +echo + +unset NEW_USER +MEM_LIMIT="50" + +while getopts "+u:m:" opt; do + + case "$opt" in + + "u") + NEW_USER="$OPTARG" + ;; + + "m") + MEM_LIMIT="$[OPTARG]" + ;; + + "?") + exit 1 + ;; + + esac + +done + +if [ "$MEM_LIMIT" -lt "5" ]; then + echo "[-] Error: malformed or dangerously low value of -m." 1>&2 + exit 1 +fi + +shift $((OPTIND-1)) + +TARGET_BIN="$1" + +if [ "$TARGET_BIN" = "" -o "$NEW_USER" = "" ]; then + + cat 1>&2 <<_EOF_ +Usage: $0 [ options ] -- /path/to/afl-fuzz [ ...afl options... ] + +Required parameters: + + -u user - run the fuzzer as a specific user after setting up limits + +Optional parameters: + + -m megs - set memory limit to a specified value ($MEM_LIMIT MB) + +This tool configures cgroups-based memory limits for a fuzzing job to simplify +the task of fuzzing ASAN or MSAN binaries. You would normally want to use it in +conjunction with '-m none' passed to the afl-fuzz binary itself, say: + + $0 -u joe ./afl-fuzz -i input -o output -m none /path/to/target + +_EOF_ + + exit 1 + +fi + +# Basic sanity checks + +if [ ! "`uname -s`" = "Linux" ]; then + echo "[-] Error: this tool does not support non-Linux systems." 1>&2 + exit 1 +fi + +if [ ! "`id -u`" = "0" ]; then + echo "[-] Error: you need to run this script as root (sorry!)." 1>&2 + exit 1 +fi + +if ! type cgcreate 2>/dev/null 1>&2; then + + echo "[-] Error: you need to install cgroup tools first." 1>&2 + + if type apt-get 2>/dev/null 1>&2; then + echo " (Perhaps 'apt-get install cgroup-bin' will work.)" 1>&2 + elif type yum 2>/dev/null 1>&2; then + echo " (Perhaps 'yum install libcgroup-tools' will work.)" 1>&2 + fi + + exit 1 + +fi + +if ! id -u "$NEW_USER" 2>/dev/null 1>&2; then + echo "[-] Error: user '$NEW_USER' does not seem to exist." 1>&2 + exit 1 +fi + +# Create a new cgroup path if necessary... We used PID-keyed groups to keep +# parallel afl-fuzz tasks separate from each other. + +CID="afl-$NEW_USER-$$" + +CPATH="/sys/fs/cgroup/memory/$CID" + +if [ ! -d "$CPATH" ]; then + + cgcreate -a "$NEW_USER" -g memory:"$CID" || exit 1 + +fi + +# Set the appropriate limit... + +if [ -f "$CPATH/memory.memsw.limit_in_bytes" ]; then + + echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" 2>/dev/null + echo "${MEM_LIMIT}M" > "$CPATH/memory.memsw.limit_in_bytes" || exit 1 + echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1 + +elif grep -qE 'partition|file' /proc/swaps; then + + echo "[-] Error: your system requires swap to be disabled first (swapoff -a)." 1>&2 + exit 1 + +else + + echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1 + +fi + +# All right. At this point, we can just run the command. + +cgexec -g "memory:$CID" su -c "$*" "$NEW_USER" + +cgdelete -g "memory:$CID" diff --git a/utils/bash_shellshock/shellshock-fuzz.diff b/utils/bash_shellshock/shellshock-fuzz.diff new file mode 100644 index 00000000..3fa05bf8 --- /dev/null +++ b/utils/bash_shellshock/shellshock-fuzz.diff @@ -0,0 +1,59 @@ +This patch shows a very simple way to find post-Shellshock bugs in bash, as +discussed here: + + http://lcamtuf.blogspot.com/2014/10/bash-bug-how-we-finally-cracked.html + +In essence, it shows a way to fuzz environmental variables. Instructions: + +1) Download bash 4.3, apply this patch, compile with: + + CC=/path/to/afl-gcc ./configure + make clean all + + Note that the harness puts the fuzzed output in $TEST_VARIABLE. With + Florian's Shellshock patch (bash43-028), this is no longer passed down + to the parser. + +2) Create and cd to an empty directory, put the compiled bash binary in + there, and run these commands: + + mkdir in_dir + echo -n '() { a() { a; }; : >b; }' >in_dir/script.txt + +3) Run the fuzzer with: + + /path/to/afl-fuzz -d -i in_dir -o out_dir ./bash -c : + + The -d parameter is advisable only if the tested shell is fairly slow + or if you are in a hurry; will cover more ground faster, but + less systematically. + +4) Watch for crashes in out_dir/crashes/. Also watch for any new files + created in cwd if you're interested in non-crash RCEs (files will be + created whenever the shell executes "foo>bar" or something like + that). You can correlate their creation date with new entries in + out_dir/queue/. + + You can also modify the bash binary to directly check for more subtle + fault conditions, or use the synthesized entries in out_dir/queue/ + as a seed for other, possibly slower or more involved testing regimes. + + Expect several hours to get decent coverage. + +--- bash-4.3/shell.c.orig 2014-01-14 14:04:32.000000000 +0100 ++++ bash-4.3/shell.c 2015-04-30 05:56:46.000000000 +0200 +@@ -371,6 +371,14 @@ + env = environ; + #endif /* __OPENNT */ + ++ { ++ ++ static char val[1024 * 16]; ++ read(0, val, sizeof(val) - 1); ++ setenv("TEST_VARIABLE", val, 1); ++ ++ } ++ + USE_VAR(argc); + USE_VAR(argv); + USE_VAR(env); diff --git a/utils/canvas_harness/canvas_harness.html b/utils/canvas_harness/canvas_harness.html new file mode 100644 index 00000000..a37b6937 --- /dev/null +++ b/utils/canvas_harness/canvas_harness.html @@ -0,0 +1,170 @@ + + + + + +
    + +
    + + + +

    Results

    + +
      + + + + diff --git a/utils/clang_asm_normalize/as b/utils/clang_asm_normalize/as new file mode 100755 index 00000000..45537cae --- /dev/null +++ b/utils/clang_asm_normalize/as @@ -0,0 +1,75 @@ +#!/bin/sh +# +# american fuzzy lop++ - clang assembly normalizer +# ---------------------------------------------- +# +# Originally written by Michal Zalewski +# The idea for this wrapper comes from Ryan Govostes. +# +# Copyright 2013, 2014 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# This 'as' wrapper should allow you to instrument unruly, hand-written +# assembly with afl-as. +# +# Usage: +# +# export AFL_REAL_PATH=/path/to/directory/with/afl-as/ +# AFL_PATH=/path/to/this/directory/ make clean all + +if [ "$#" -lt "2" ]; then + echo "[-] Error: this utility can't be called directly." 1>&2 + exit 1 +fi + +if [ "$AFL_REAL_PATH" = "" ]; then + echo "[-] Error: AFL_REAL_PATH not set!" 1>&2 + exit 1 +fi + +if [ ! -x "$AFL_REAL_PATH/afl-as" ]; then + echo "[-] Error: AFL_REAL_PATH does not contain the 'afl-as' binary." 1>&2 + exit 1 +fi + +unset __AFL_AS_CMDLINE __AFL_FNAME + +while [ ! "$#" = "0" ]; do + + if [ "$#" = "1" ]; then + __AFL_FNAME="$1" + else + __AFL_AS_CMDLINE="${__AFL_AS_CMDLINE} $1" + fi + + shift + +done + +test "$TMPDIR" = "" && TMPDIR=/tmp + +TMPFILE=`mktemp $TMPDIR/.afl-XXXXXXXXXX.s` + +test "$TMPFILE" = "" && exit 1 + +clang -cc1as -filetype asm -output-asm-variant 0 "${__AFL_FNAME}" >"$TMPFILE" + +ERR="$?" + +if [ ! "$ERR" = "0" ]; then + rm -f "$TMPFILE" + exit $ERR +fi + +"$AFL_REAL_PATH/afl-as" ${__AFL_AS_CMDLINE} "$TMPFILE" + +ERR="$?" + +rm -f "$TMPFILE" + +exit "$ERR" diff --git a/utils/crash_triage/triage_crashes.sh b/utils/crash_triage/triage_crashes.sh new file mode 100755 index 00000000..bf763cba --- /dev/null +++ b/utils/crash_triage/triage_crashes.sh @@ -0,0 +1,115 @@ +#!/bin/sh +# +# american fuzzy lop++ - crash triage utility +# ----------------------------------------- +# +# Originally written by Michal Zalewski +# +# Copyright 2013, 2014, 2017 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Note that this assumes that the targeted application reads from stdin +# and requires no other cmdline parameters. Modify as needed if this is +# not the case. +# +# Note that on OpenBSD, you may need to install a newer version of gdb +# (e.g., from ports). You can set GDB=/some/path to point to it if +# necessary. +# + +echo "crash triage utility for afl-fuzz by Michal Zalewski" +echo + +ulimit -v 100000 2>/dev/null +ulimit -d 100000 2>/dev/null + +if [ "$#" -lt "2" ]; then + echo "Usage: $0 /path/to/afl_output_dir /path/to/tested_binary [...target params...]" 1>&2 + echo 1>&2 + exit 1 +fi + +DIR="$1" +BIN="$2" +shift +shift + +if [ "$AFL_ALLOW_TMP" = "" ]; then + + echo "$DIR" | grep -qE '^(/var)?/tmp/' + T1="$?" + + echo "$BIN" | grep -qE '^(/var)?/tmp/' + T2="$?" + + if [ "$T1" = "0" -o "$T2" = "0" ]; then + echo "[-] Error: do not use shared /tmp or /var/tmp directories with this script." 1>&2 + exit 1 + fi + +fi + +if + [ "$GDB" = "" ]; then + GDB=gdb +fi + +if [ ! -f "$BIN" -o ! -x "$BIN" ]; then + echo "[-] Error: binary '$2' not found or is not executable." 1>&2 + exit 1 +fi + +if [ ! -d "$DIR/queue" ]; then + echo "[-] Error: directory '$1' not found or not created by afl-fuzz." 1>&2 + exit 1 +fi + +CCOUNT=$((`ls -- "$DIR/crashes" 2>/dev/null | wc -l`)) + +if [ "$CCOUNT" = "0" ]; then + echo "No crashes recorded in the target directory - nothing to be done." + exit 0 +fi + +echo + +for crash in $DIR/crashes/id:*; do + + id=`basename -- "$crash" | cut -d, -f1 | cut -d: -f2` + sig=`basename -- "$crash" | cut -d, -f2 | cut -d: -f2` + + # Grab the args, converting @@ to $crash + + use_args="" + use_stdio=1 + + for a in $@; do + + if [ "$a" = "@@" ] ; then + use_args="$use_args $crash" + unset use_stdio + else + use_args="$use_args $a" + fi + + done + + # Strip the trailing space + use_args="${use_args# }" + + echo "+++ ID $id, SIGNAL $sig +++" + echo + + if [ "$use_stdio" = "1" ]; then + $GDB --batch -q --ex "r $use_args <$crash" --ex 'back' --ex 'disass $pc, $pc+16' --ex 'info reg' --ex 'quit' "$BIN" 0 + +#define INITIAL_GROWTH_SIZE (64) + +#define RAND_BELOW(limit) (rand() % (limit)) + +/* Use in a struct: creates a name_buf and a name_size variable. */ +#define BUF_VAR(type, name) \ + type * name##_buf; \ + size_t name##_size; +/* this fills in `&structptr->something_buf, &structptr->something_size`. */ +#define BUF_PARAMS(struct, name) \ + (void **)&struct->name##_buf, &struct->name##_size + +typedef struct { + +} afl_t; + +static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) { + + static s8 interesting_8[] = {INTERESTING_8}; + static s16 interesting_16[] = {INTERESTING_8, INTERESTING_16}; + static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32}; + + switch (RAND_BELOW(12)) { + + case 0: { + + /* Flip a single bit somewhere. Spooky! */ + + s32 bit_idx = ((RAND_BELOW(end - begin) + begin) << 3) + RAND_BELOW(8); + + out_buf[bit_idx >> 3] ^= 128 >> (bit_idx & 7); + + break; + + } + + case 1: { + + /* Set byte to interesting value. */ + + u8 val = interesting_8[RAND_BELOW(sizeof(interesting_8))]; + out_buf[(RAND_BELOW(end - begin) + begin)] = val; + + break; + + } + + case 2: { + + /* Set word to interesting value, randomly choosing endian. */ + + if (end - begin < 2) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 1) break; + + switch (RAND_BELOW(2)) { + + case 0: + *(u16 *)(out_buf + byte_idx) = + interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]; + break; + case 1: + *(u16 *)(out_buf + byte_idx) = + SWAP16(interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]); + break; + + } + + break; + + } + + case 3: { + + /* Set dword to interesting value, randomly choosing endian. */ + + if (end - begin < 4) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 3) break; + + switch (RAND_BELOW(2)) { + + case 0: + *(u32 *)(out_buf + byte_idx) = + interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]; + break; + case 1: + *(u32 *)(out_buf + byte_idx) = + SWAP32(interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]); + break; + + } + + break; + + } + + case 4: { + + /* Set qword to interesting value, randomly choosing endian. */ + + if (end - begin < 8) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 7) break; + + switch (RAND_BELOW(2)) { + + case 0: + *(u64 *)(out_buf + byte_idx) = + (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]; + break; + case 1: + *(u64 *)(out_buf + byte_idx) = SWAP64( + (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]); + break; + + } + + break; + + } + + case 5: { + + /* Randomly subtract from byte. */ + + out_buf[(RAND_BELOW(end - begin) + begin)] -= 1 + RAND_BELOW(ARITH_MAX); + + break; + + } + + case 6: { + + /* Randomly add to byte. */ + + out_buf[(RAND_BELOW(end - begin) + begin)] += 1 + RAND_BELOW(ARITH_MAX); + + break; + + } + + case 7: { + + /* Randomly subtract from word, random endian. */ + + if (end - begin < 2) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 1) break; + + if (RAND_BELOW(2)) { + + *(u16 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX); + + } else { + + u16 num = 1 + RAND_BELOW(ARITH_MAX); + + *(u16 *)(out_buf + byte_idx) = + SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) - num); + + } + + break; + + } + + case 8: { + + /* Randomly add to word, random endian. */ + + if (end - begin < 2) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 1) break; + + if (RAND_BELOW(2)) { + + *(u16 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX); + + } else { + + u16 num = 1 + RAND_BELOW(ARITH_MAX); + + *(u16 *)(out_buf + byte_idx) = + SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) + num); + + } + + break; + + } + + case 9: { + + /* Randomly subtract from dword, random endian. */ + + if (end - begin < 4) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 3) break; + + if (RAND_BELOW(2)) { + + *(u32 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX); + + } else { + + u32 num = 1 + RAND_BELOW(ARITH_MAX); + + *(u32 *)(out_buf + byte_idx) = + SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) - num); + + } + + break; + + } + + case 10: { + + /* Randomly add to dword, random endian. */ + + if (end - begin < 4) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 3) break; + + if (RAND_BELOW(2)) { + + *(u32 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX); + + } else { + + u32 num = 1 + RAND_BELOW(ARITH_MAX); + + *(u32 *)(out_buf + byte_idx) = + SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) + num); + + } + + break; + + } + + case 11: { + + /* Just set a random byte to a random value. Because, + why not. We use XOR with 1-255 to eliminate the + possibility of a no-op. */ + + out_buf[(RAND_BELOW(end - begin) + begin)] ^= 1 + RAND_BELOW(255); + + break; + + } + + } + +} + +/* This function calculates the next power of 2 greater or equal its argument. + @return The rounded up power of 2 (if no overflow) or 0 on overflow. +*/ +static inline size_t next_pow2(size_t in) { + + if (in == 0 || in > (size_t)-1) + return 0; /* avoid undefined behaviour under-/overflow */ + size_t out = in - 1; + out |= out >> 1; + out |= out >> 2; + out |= out >> 4; + out |= out >> 8; + out |= out >> 16; + return out + 1; + +} + +/* This function makes sure *size is > size_needed after call. + It will realloc *buf otherwise. + *size will grow exponentially as per: + https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/ + Will return NULL and free *buf if size_needed is <1 or realloc failed. + @return For convenience, this function returns *buf. + */ +static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) { + + /* No need to realloc */ + if (likely(size_needed && *size >= size_needed)) return *buf; + + /* No initial size was set */ + if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE; + + /* grow exponentially */ + size_t next_size = next_pow2(size_needed); + + /* handle overflow */ + if (!next_size) { next_size = size_needed; } + + /* alloc */ + *buf = realloc(*buf, next_size); + *size = *buf ? next_size : 0; + + return *buf; + +} + +/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */ +static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2, + size_t *size2) { + + void * scratch_buf = *buf1; + size_t scratch_size = *size1; + *buf1 = *buf2; + *size1 = *size2; + *buf2 = scratch_buf; + *size2 = scratch_size; + +} + +#undef INITIAL_GROWTH_SIZE + +#endif + diff --git a/utils/custom_mutators/example.c b/utils/custom_mutators/example.c new file mode 100644 index 00000000..23add128 --- /dev/null +++ b/utils/custom_mutators/example.c @@ -0,0 +1,376 @@ +/* + New Custom Mutator for AFL++ + Written by Khaled Yakdan + Andrea Fioraldi + Shengtuo Hu + Dominik Maier +*/ + +// You need to use -I /path/to/AFLplusplus/include +#include "custom_mutator_helpers.h" + +#include +#include +#include +#include + +#define DATA_SIZE (100) + +static const char *commands[] = { + + "GET", + "PUT", + "DEL", + +}; + +typedef struct my_mutator { + + afl_t *afl; + + // any additional data here! + size_t trim_size_current; + int trimmming_steps; + int cur_step; + + // Reused buffers: + BUF_VAR(u8, fuzz); + BUF_VAR(u8, data); + BUF_VAR(u8, havoc); + BUF_VAR(u8, trim); + BUF_VAR(u8, post_process); + +} my_mutator_t; + +/** + * Initialize this custom mutator + * + * @param[in] afl a pointer to the internal state object. Can be ignored for + * now. + * @param[in] seed A seed for this mutator - the same seed should always mutate + * in the same way. + * @return Pointer to the data object this custom mutator instance should use. + * There may be multiple instances of this mutator in one afl-fuzz run! + * Return NULL on error. + */ +my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { + + srand(seed); // needed also by surgical_havoc_mutate() + + my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); + if (!data) { + + perror("afl_custom_init alloc"); + return NULL; + + } + + data->afl = afl; + + return data; + +} + +/** + * Perform custom mutations on a given input + * + * (Optional for now. Required in the future) + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param[in] buf Pointer to input data to be mutated + * @param[in] buf_size Size of input data + * @param[out] out_buf the buffer we will work on. we can reuse *buf. NULL on + * error. + * @param[in] add_buf Buffer containing the additional test case + * @param[in] add_buf_size Size of the additional test case + * @param[in] max_size Maximum size of the mutated output. The mutation must not + * produce data larger than max_size. + * @return Size of the mutated output. + */ +size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, + u8 **out_buf, uint8_t *add_buf, + size_t add_buf_size, // add_buf can be NULL + size_t max_size) { + + // Make sure that the packet size does not exceed the maximum size expected by + // the fuzzer + size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_size; + + // maybe_grow is optimized to be quick for reused buffers. + u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), mutated_size); + if (!mutated_out) { + + *out_buf = NULL; + perror("custom mutator allocation (maybe_grow)"); + return 0; /* afl-fuzz will very likely error out after this. */ + + } + + // Randomly select a command string to add as a header to the packet + memcpy(mutated_out, commands[rand() % 3], 3); + + // Mutate the payload of the packet + int i; + for (i = 0; i < 8; ++i) { + + // Randomly perform one of the (no len modification) havoc mutations + surgical_havoc_mutate(mutated_out, 3, mutated_size); + + } + + *out_buf = mutated_out; + return mutated_size; + +} + +/** + * A post-processing function to use right before AFL writes the test case to + * disk in order to execute the target. + * + * (Optional) If this functionality is not needed, simply don't define this + * function. + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param[in] buf Buffer containing the test case to be executed + * @param[in] buf_size Size of the test case + * @param[out] out_buf Pointer to the buffer containing the test case after + * processing. External library should allocate memory for out_buf. + * The buf pointer may be reused (up to the given buf_size); + * @return Size of the output buffer after processing or the needed amount. + * A return of 0 indicates an error. + */ +size_t afl_custom_post_process(my_mutator_t *data, uint8_t *buf, + size_t buf_size, uint8_t **out_buf) { + + uint8_t *post_process_buf = + maybe_grow(BUF_PARAMS(data, post_process), buf_size + 5); + if (!post_process_buf) { + + perror("custom mutator realloc failed."); + *out_buf = NULL; + return 0; + + } + + memcpy(post_process_buf + 5, buf, buf_size); + post_process_buf[0] = 'A'; + post_process_buf[1] = 'F'; + post_process_buf[2] = 'L'; + post_process_buf[3] = '+'; + post_process_buf[4] = '+'; + + *out_buf = post_process_buf; + + return buf_size + 5; + +} + +/** + * This method is called at the start of each trimming operation and receives + * the initial buffer. It should return the amount of iteration steps possible + * on this input (e.g. if your input has n elements and you want to remove + * them one by one, return n, if you do a binary search, return log(n), + * and so on...). + * + * If your trimming algorithm doesn't allow you to determine the amount of + * (remaining) steps easily (esp. while running), then you can alternatively + * return 1 here and always return 0 in post_trim until you are finished and + * no steps remain. In that case, returning 1 in post_trim will end the + * trimming routine. The whole current index/max iterations stuff is only used + * to show progress. + * + * (Optional) + * + * @param data pointer returned in afl_custom_init for this fuzz case + * @param buf Buffer containing the test case + * @param buf_size Size of the test case + * @return The amount of possible iteration steps to trim the input. + * negative on error. + */ +int32_t afl_custom_init_trim(my_mutator_t *data, uint8_t *buf, + size_t buf_size) { + + // We simply trim once + data->trimmming_steps = 1; + + data->cur_step = 0; + + if (!maybe_grow(BUF_PARAMS(data, trim), buf_size)) { + + perror("init_trim grow"); + return -1; + + } + + memcpy(data->trim_buf, buf, buf_size); + + data->trim_size_current = buf_size; + + return data->trimmming_steps; + +} + +/** + * This method is called for each trimming operation. It doesn't have any + * arguments because we already have the initial buffer from init_trim and we + * can memorize the current state in *data. This can also save + * reparsing steps for each iteration. It should return the trimmed input + * buffer, where the returned data must not exceed the initial input data in + * length. Returning anything that is larger than the original data (passed + * to init_trim) will result in a fatal abort of AFLFuzz. + * + * (Optional) + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param[out] out_buf Pointer to the buffer containing the trimmed test case. + * External library should allocate memory for out_buf. + * AFL++ will not release the memory after saving the test case. + * Keep a ref in *data. + * *out_buf = NULL is treated as error. + * @return Pointer to the size of the trimmed test case + */ +size_t afl_custom_trim(my_mutator_t *data, uint8_t **out_buf) { + + *out_buf = data->trim_buf; + + // Remove the last byte of the trimming input + return data->trim_size_current - 1; + +} + +/** + * This method is called after each trim operation to inform you if your + * trimming step was successful or not (in terms of coverage). If you receive + * a failure here, you should reset your input to the last known good state. + * + * (Optional) + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param success Indicates if the last trim operation was successful. + * @return The next trim iteration index (from 0 to the maximum amount of + * steps returned in init_trim). negative ret on failure. + */ +int32_t afl_custom_post_trim(my_mutator_t *data, int success) { + + if (success) { + + ++data->cur_step; + return data->cur_step; + + } + + return data->trimmming_steps; + +} + +/** + * Perform a single custom mutation on a given input. + * This mutation is stacked with the other muatations in havoc. + * + * (Optional) + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param[in] buf Pointer to the input data to be mutated and the mutated + * output + * @param[in] buf_size Size of input data + * @param[out] out_buf The output buffer. buf can be reused, if the content + * fits. *out_buf = NULL is treated as error. + * @param[in] max_size Maximum size of the mutated output. The mutation must + * not produce data larger than max_size. + * @return Size of the mutated output. + */ +size_t afl_custom_havoc_mutation(my_mutator_t *data, u8 *buf, size_t buf_size, + u8 **out_buf, size_t max_size) { + + if (buf_size == 0) { + + *out_buf = maybe_grow(BUF_PARAMS(data, havoc), 1); + if (!*out_buf) { + + perror("custom havoc: maybe_grow"); + return 0; + + } + + **out_buf = rand() % 256; + buf_size = 1; + + } else { + + // We reuse buf here. It's legal and faster. + *out_buf = buf; + + } + + size_t victim = rand() % buf_size; + (*out_buf)[victim] += rand() % 10; + + return buf_size; + +} + +/** + * Return the probability (in percentage) that afl_custom_havoc_mutation + * is called in havoc. By default it is 6 %. + * + * (Optional) + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @return The probability (0-100). + */ +uint8_t afl_custom_havoc_mutation_probability(my_mutator_t *data) { + + return 5; // 5 % + +} + +/** + * Determine whether the fuzzer should fuzz the queue entry or not. + * + * (Optional) + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param filename File name of the test case in the queue entry + * @return Return True(1) if the fuzzer will fuzz the queue entry, and + * False(0) otherwise. + */ +uint8_t afl_custom_queue_get(my_mutator_t *data, const uint8_t *filename) { + + return 1; + +} + +/** + * Allow for additional analysis (e.g. calling a different tool that does a + * different kind of coverage and saves this for the custom mutator). + * + * (Optional) + * + * @param data pointer returned in afl_custom_init for this fuzz case + * @param filename_new_queue File name of the new queue entry + * @param filename_orig_queue File name of the original queue entry + */ +void afl_custom_queue_new_entry(my_mutator_t * data, + const uint8_t *filename_new_queue, + const uint8_t *filename_orig_queue) { + + /* Additional analysis on the original or new test case */ + +} + +/** + * Deinitialize everything + * + * @param data The data ptr from afl_custom_init + */ +void afl_custom_deinit(my_mutator_t *data) { + + free(data->post_process_buf); + free(data->havoc_buf); + free(data->data_buf); + free(data->fuzz_buf); + free(data->trim_buf); + free(data); + +} + diff --git a/utils/custom_mutators/example.py b/utils/custom_mutators/example.py new file mode 100644 index 00000000..cf659e5a --- /dev/null +++ b/utils/custom_mutators/example.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python +# encoding: utf-8 +''' +Example Python Module for AFLFuzz + +@author: Christian Holler (:decoder) + +@license: + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + +@contact: choller@mozilla.com +''' + +import random + + +COMMANDS = [ + b"GET", + b"PUT", + b"DEL", + b"AAAAAAAAAAAAAAAAA", +] + + +def init(seed): + ''' + Called once when AFLFuzz starts up. Used to seed our RNG. + + @type seed: int + @param seed: A 32-bit random value + ''' + random.seed(seed) + + +def deinit(): + pass + + +def fuzz(buf, add_buf, max_size): + ''' + Called per fuzzing iteration. + + @type buf: bytearray + @param buf: The buffer that should be mutated. + + @type add_buf: bytearray + @param add_buf: A second buffer that can be used as mutation source. + + @type max_size: int + @param max_size: Maximum size of the mutated output. The mutation must not + produce data larger than max_size. + + @rtype: bytearray + @return: A new bytearray containing the mutated data + ''' + ret = bytearray(100) + + ret[:3] = random.choice(COMMANDS) + + return ret + +# Uncomment and implement the following methods if you want to use a custom +# trimming algorithm. See also the documentation for a better API description. + +# def init_trim(buf): +# ''' +# Called per trimming iteration. +# +# @type buf: bytearray +# @param buf: The buffer that should be trimmed. +# +# @rtype: int +# @return: The maximum number of trimming steps. +# ''' +# global ... +# +# # Initialize global variables +# +# # Figure out how many trimming steps are possible. +# # If this is not possible for your trimming, you can +# # return 1 instead and always return 0 in post_trim +# # until you are done (then you return 1). +# +# return steps +# +# def trim(): +# ''' +# Called per trimming iteration. +# +# @rtype: bytearray +# @return: A new bytearray containing the trimmed data. +# ''' +# global ... +# +# # Implement the actual trimming here +# +# return bytearray(...) +# +# def post_trim(success): +# ''' +# Called after each trimming operation. +# +# @type success: bool +# @param success: Indicates if the last trim operation was successful. +# +# @rtype: int +# @return: The next trim index (0 to max number of steps) where max +# number of steps indicates the trimming is done. +# ''' +# global ... +# +# if not success: +# # Restore last known successful input, determine next index +# else: +# # Just determine the next index, based on what was successfully +# # removed in the last step +# +# return next_index +# +# def post_process(buf): +# ''' +# Called just before the execution to write the test case in the format +# expected by the target +# +# @type buf: bytearray +# @param buf: The buffer containing the test case to be executed +# +# @rtype: bytearray +# @return: The buffer containing the test case after +# ''' +# return buf +# +# def havoc_mutation(buf, max_size): +# ''' +# Perform a single custom mutation on a given input. +# +# @type buf: bytearray +# @param buf: The buffer that should be mutated. +# +# @type max_size: int +# @param max_size: Maximum size of the mutated output. The mutation must not +# produce data larger than max_size. +# +# @rtype: bytearray +# @return: A new bytearray containing the mutated data +# ''' +# return mutated_buf +# +# def havoc_mutation_probability(): +# ''' +# Called for each `havoc_mutation`. Return the probability (in percentage) +# that `havoc_mutation` is called in havoc. Be default it is 6%. +# +# @rtype: int +# @return: The probability (0-100) +# ''' +# return prob +# +# def queue_get(filename): +# ''' +# Called at the beginning of each fuzz iteration to determine whether the +# test case should be fuzzed +# +# @type filename: str +# @param filename: File name of the test case in the current queue entry +# +# @rtype: bool +# @return: Return True if the custom mutator decides to fuzz the test case, +# and False otherwise +# ''' +# return True +# +# def queue_new_entry(filename_new_queue, filename_orig_queue): +# ''' +# Called after adding a new test case to the queue +# +# @type filename_new_queue: str +# @param filename_new_queue: File name of the new queue entry +# +# @type filename_orig_queue: str +# @param filename_orig_queue: File name of the original queue entry +# ''' +# pass diff --git a/utils/custom_mutators/post_library_gif.so.c b/utils/custom_mutators/post_library_gif.so.c new file mode 100644 index 00000000..ac10f409 --- /dev/null +++ b/utils/custom_mutators/post_library_gif.so.c @@ -0,0 +1,165 @@ +/* + american fuzzy lop++ - postprocessor library example + -------------------------------------------------- + + Originally written by Michal Zalewski + Edited by Dominik Maier, 2020 + + Copyright 2015 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Postprocessor libraries can be passed to afl-fuzz to perform final cleanup + of any mutated test cases - for example, to fix up checksums in PNG files. + + Please heed the following warnings: + + 1) In almost all cases, it is more productive to comment out checksum logic + in the targeted binary (as shown in ../libpng_no_checksum/). One possible + exception is the process of fuzzing binary-only software in QEMU mode. + + 2) The use of postprocessors for anything other than checksums is + questionable and may cause more harm than good. AFL is normally pretty good + about dealing with length fields, magic values, etc. + + 3) Postprocessors that do anything non-trivial must be extremely robust to + gracefully handle malformed data and other error conditions - otherwise, + they will crash and take afl-fuzz down with them. Be wary of reading past + *len and of integer overflows when calculating file offsets. + + In other words, THIS IS PROBABLY NOT WHAT YOU WANT - unless you really, + honestly know what you're doing =) + + With that out of the way: the postprocessor library is passed to afl-fuzz + via AFL_POST_LIBRARY. The library must be compiled with: + + gcc -shared -Wall -O3 post_library.so.c -o post_library.so + + AFL will call the afl_custom_post_process() function for every mutated output + buffer. From there, you have three choices: + + 1) If you don't want to modify the test case, simply set `*out_buf = in_buf` + and return the original `len`. + + 2) If you want to skip this test case altogether and have AFL generate a + new one, return 0 or set `*out_buf = NULL`. + Use this sparingly - it's faster than running the target program + with patently useless inputs, but still wastes CPU time. + + 3) If you want to modify the test case, allocate an appropriately-sized + buffer, move the data into that buffer, make the necessary changes, and + then return the new pointer as out_buf. Return an appropriate len + afterwards. + + Note that the buffer will *not* be freed for you. To avoid memory leaks, + you need to free it or reuse it on subsequent calls (as shown below). + + *** Feel free to reuse the original 'in_buf' BUFFER and return it. *** + + Aight. The example below shows a simple postprocessor that tries to make + sure that all input files start with "GIF89a". + + PS. If you don't like C, you can try out the unix-based wrapper from + Ben Nagy instead: https://github.com/bnagy/aflfix + + */ + +#include +#include +#include + +/* Header that must be present at the beginning of every test case: */ + +#define HEADER "GIF89a" + +typedef struct post_state { + + unsigned char *buf; + size_t size; + +} post_state_t; + +void *afl_custom_init(void *afl) { + + post_state_t *state = malloc(sizeof(post_state_t)); + if (!state) { + + perror("malloc"); + return NULL; + + } + + state->buf = calloc(sizeof(unsigned char), 4096); + if (!state->buf) { + + free(state); + perror("calloc"); + return NULL; + + } + + return state; + +} + +/* The actual postprocessor routine called by afl-fuzz: */ + +size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf, + unsigned int len, unsigned char **out_buf) { + + /* Skip execution altogether for buffers shorter than 6 bytes (just to + show how it's done). We can trust len to be sane. */ + + if (len < strlen(HEADER)) return 0; + + /* Do nothing for buffers that already start with the expected header. */ + + if (!memcmp(in_buf, HEADER, strlen(HEADER))) { + + *out_buf = in_buf; + return len; + + } + + /* Allocate memory for new buffer, reusing previous allocation if + possible. */ + + *out_buf = realloc(data->buf, len); + + /* If we're out of memory, the most graceful thing to do is to return the + original buffer and give up on modifying it. Let AFL handle OOM on its + own later on. */ + + if (!*out_buf) { + + *out_buf = in_buf; + return len; + + } + + /* Copy the original data to the new location. */ + + memcpy(*out_buf, in_buf, len); + + /* Insert the new header. */ + + memcpy(*out_buf, HEADER, strlen(HEADER)); + + /* Return the new len. It hasn't changed, so it's just len. */ + + return len; + +} + +/* Gets called afterwards */ +void afl_custom_deinit(post_state_t *data) { + + free(data->buf); + free(data); + +} + diff --git a/utils/custom_mutators/post_library_png.so.c b/utils/custom_mutators/post_library_png.so.c new file mode 100644 index 00000000..941f7e55 --- /dev/null +++ b/utils/custom_mutators/post_library_png.so.c @@ -0,0 +1,163 @@ +/* + american fuzzy lop++ - postprocessor for PNG + ------------------------------------------ + + Originally written by Michal Zalewski + + Copyright 2015 Google Inc. All rights reserved. + Adapted to the new API, 2020 by Dominik Maier + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + See post_library.so.c for a general discussion of how to implement + postprocessors. This specific postprocessor attempts to fix up PNG + checksums, providing a slightly more complicated example than found + in post_library.so.c. + + Compile with: + + gcc -shared -Wall -O3 post_library_png.so.c -o post_library_png.so -lz + + */ + +#include +#include +#include +#include +#include + +#include + +/* A macro to round an integer up to 4 kB. */ + +#define UP4K(_i) ((((_i) >> 12) + 1) << 12) + +typedef struct post_state { + + unsigned char *buf; + size_t size; + +} post_state_t; + +void *afl_custom_init(void *afl) { + + post_state_t *state = malloc(sizeof(post_state_t)); + if (!state) { + + perror("malloc"); + return NULL; + + } + + state->buf = calloc(sizeof(unsigned char), 4096); + if (!state->buf) { + + free(state); + perror("calloc"); + return NULL; + + } + + return state; + +} + +size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf, + unsigned int len, + const unsigned char **out_buf) { + + unsigned char *new_buf = (unsigned char *)in_buf; + unsigned int pos = 8; + + /* Don't do anything if there's not enough room for the PNG header + (8 bytes). */ + + if (len < 8) { + + *out_buf = in_buf; + return len; + + } + + /* Minimum size of a zero-length PNG chunk is 12 bytes; if we + don't have that, we can bail out. */ + + while (pos + 12 <= len) { + + unsigned int chunk_len, real_cksum, file_cksum; + + /* Chunk length is the first big-endian dword in the chunk. */ + + chunk_len = ntohl(*(uint32_t *)(in_buf + pos)); + + /* Bail out if chunk size is too big or goes past EOF. */ + + if (chunk_len > 1024 * 1024 || pos + 12 + chunk_len > len) break; + + /* Chunk checksum is calculated for chunk ID (dword) and the actual + payload. */ + + real_cksum = htonl(crc32(0, in_buf + pos + 4, chunk_len + 4)); + + /* The in-file checksum is the last dword past the chunk data. */ + + file_cksum = *(uint32_t *)(in_buf + pos + 8 + chunk_len); + + /* If the checksums do not match, we need to fix the file. */ + + if (real_cksum != file_cksum) { + + /* First modification? Make a copy of the input buffer. Round size + up to 4 kB to minimize the number of reallocs needed. */ + + if (new_buf == in_buf) { + + if (len <= data->size) { + + new_buf = data->buf; + + } else { + + new_buf = realloc(data->buf, UP4K(len)); + if (!new_buf) { + + *out_buf = in_buf; + return len; + + } + + data->buf = new_buf; + data->size = UP4K(len); + memcpy(new_buf, in_buf, len); + + } + + } + + *(uint32_t *)(new_buf + pos + 8 + chunk_len) = real_cksum; + + } + + /* Skip the entire chunk and move to the next one. */ + + pos += 12 + chunk_len; + + } + + *out_buf = new_buf; + return len; + +} + +/* Gets called afterwards */ +void afl_custom_deinit(post_state_t *data) { + + free(data->buf); + free(data); + +} + diff --git a/utils/custom_mutators/simple-chunk-replace.py b/utils/custom_mutators/simple-chunk-replace.py new file mode 100644 index 00000000..df2f4ca7 --- /dev/null +++ b/utils/custom_mutators/simple-chunk-replace.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# encoding: utf-8 +''' +Simple Chunk Cross-Over Replacement Module for AFLFuzz + +@author: Christian Holler (:decoder) + +@license: + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + +@contact: choller@mozilla.com +''' + +import random + + +def init(seed): + ''' + Called once when AFLFuzz starts up. Used to seed our RNG. + + @type seed: int + @param seed: A 32-bit random value + ''' + # Seed our RNG + random.seed(seed) + + +def fuzz(buf, add_buf, max_size): + ''' + Called per fuzzing iteration. + + @type buf: bytearray + @param buf: The buffer that should be mutated. + + @type add_buf: bytearray + @param add_buf: A second buffer that can be used as mutation source. + + @type max_size: int + @param max_size: Maximum size of the mutated output. The mutation must not + produce data larger than max_size. + + @rtype: bytearray + @return: A new bytearray containing the mutated data + ''' + # Make a copy of our input buffer for returning + ret = bytearray(buf) + + # Take a random fragment length between 2 and 32 (or less if add_buf is shorter) + fragment_len = random.randint(1, min(len(add_buf), 32)) + + # Determine a random source index where to take the data chunk from + rand_src_idx = random.randint(0, len(add_buf) - fragment_len) + + # Determine a random destination index where to put the data chunk + rand_dst_idx = random.randint(0, len(buf)) + + # Make the chunk replacement + ret[rand_dst_idx:rand_dst_idx + fragment_len] = add_buf[rand_src_idx:rand_src_idx + fragment_len] + + # Return data + return ret diff --git a/utils/custom_mutators/simple_example.c b/utils/custom_mutators/simple_example.c new file mode 100644 index 00000000..d888ec1f --- /dev/null +++ b/utils/custom_mutators/simple_example.c @@ -0,0 +1,74 @@ +// This simple example just creates random buffer <= 100 filled with 'A' +// needs -I /path/to/AFLplusplus/include +#include "custom_mutator_helpers.h" + +#include +#include +#include +#include + +#ifndef _FIXED_CHAR + #define _FIXED_CHAR 0x41 +#endif + +typedef struct my_mutator { + + afl_t *afl; + + // Reused buffers: + BUF_VAR(u8, fuzz); + +} my_mutator_t; + +my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { + + srand(seed); + my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); + if (!data) { + + perror("afl_custom_init alloc"); + return NULL; + + } + + data->afl = afl; + + return data; + +} + +size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, + u8 **out_buf, uint8_t *add_buf, + size_t add_buf_size, // add_buf can be NULL + size_t max_size) { + + int size = (rand() % 100) + 1; + if (size > max_size) size = max_size; + u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), size); + if (!mutated_out) { + + *out_buf = NULL; + perror("custom mutator allocation (maybe_grow)"); + return 0; /* afl-fuzz will very likely error out after this. */ + + } + + memset(mutated_out, _FIXED_CHAR, size); + + *out_buf = mutated_out; + return size; + +} + +/** + * Deinitialize everything + * + * @param data The data ptr from afl_custom_init + */ +void afl_custom_deinit(my_mutator_t *data) { + + free(data->fuzz_buf); + free(data); + +} + diff --git a/utils/custom_mutators/wrapper_afl_min.py b/utils/custom_mutators/wrapper_afl_min.py new file mode 100644 index 00000000..ecb03b55 --- /dev/null +++ b/utils/custom_mutators/wrapper_afl_min.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python + +from XmlMutatorMin import XmlMutatorMin + +# Default settings (production mode) + +__mutator__ = None +__seed__ = "RANDOM" +__log__ = False +__log_file__ = "wrapper.log" + + +# AFL functions +def log(text): + """ + Logger + """ + + global __seed__ + global __log__ + global __log_file__ + + if __log__: + with open(__log_file__, "a") as logf: + logf.write("[%s] %s\n" % (__seed__, text)) + + +def init(seed): + """ + Called once when AFL starts up. Seed is used to identify the AFL instance in log files + """ + + global __mutator__ + global __seed__ + + # Get the seed + __seed__ = seed + + # Create a global mutation class + try: + __mutator__ = XmlMutatorMin(__seed__, verbose=__log__) + log("init(): Mutator created") + except RuntimeError as e: + log("init(): Can't create mutator: %s" % e.message) + + +def fuzz(buf, add_buf, max_size): + """ + Called for each fuzzing iteration. + """ + + global __mutator__ + + # Do we have a working mutator object? + if __mutator__ is None: + log("fuzz(): Can't fuzz, no mutator available") + return buf + + # Try to use the AFL buffer + via_buffer = True + + # Interpret the AFL buffer (an array of bytes) as a string + if via_buffer: + try: + buf_str = str(buf) + log("fuzz(): AFL buffer converted to a string") + except Exception: + via_buffer = False + log("fuzz(): Can't convert AFL buffer to a string") + + # Load XML from the AFL string + if via_buffer: + try: + __mutator__.init_from_string(buf_str) + log("fuzz(): Mutator successfully initialized with AFL buffer (%d bytes)" % len(buf_str)) + except Exception: + via_buffer = False + log("fuzz(): Can't initialize mutator with AFL buffer") + + # If init from AFL buffer wasn't succesful + if not via_buffer: + log("fuzz(): Returning unmodified AFL buffer") + return buf + + # Sucessful initialization -> mutate + try: + __mutator__.mutate(max=5) + log("fuzz(): Input mutated") + except Exception: + log("fuzz(): Can't mutate input => returning buf") + return buf + + # Convert mutated data to a array of bytes + try: + data = bytearray(__mutator__.save_to_string()) + log("fuzz(): Mutated data converted as bytes") + except Exception: + log("fuzz(): Can't convert mutated data to bytes => returning buf") + return buf + + # Everything went fine, returning mutated content + log("fuzz(): Returning %d bytes" % len(data)) + return data + + +# Main (for debug) +if __name__ == '__main__': + + __log__ = True + __log_file__ = "/dev/stdout" + __seed__ = "RANDOM" + + init(__seed__) + + in_1 = bytearray("ffffzzzzzzzzzzzz") + in_2 = bytearray("") + out = fuzz(in_1, in_2) + print(out) diff --git a/utils/defork/Makefile b/utils/defork/Makefile new file mode 100644 index 00000000..e8240dba --- /dev/null +++ b/utils/defork/Makefile @@ -0,0 +1,64 @@ +# +# american fuzzy lop++ - defork +# ---------------------------------- +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +.PHONY: all install clean + +PREFIX ?= /usr/local +BIN_PATH = $(PREFIX)/bin +HELPER_PATH = $(PREFIX)/lib/afl + +CFLAGS = -fPIC -Wall -Wextra +LDFLAGS = -shared + +UNAME_SAYS_LINUX=$(shell uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?) +UNAME_SAYS_LINUX:sh=uname | grep -E '^Linux|^GNU' >/dev/null; echo $$? + +_LDFLAGS_ADD=$(UNAME_SAYS_LINUX:1=) +LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-ldl) +LDFLAGS += $(LDFLAGS_ADD) + +# on gcc for arm there is no -m32, but -mbe32 +M32FLAG = -m32 +M64FLAG = -m64 + +CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?) +CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$? +CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?) +CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$? + +_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER) +__M32FLAG=$(_M32FLAG:00=-mbe32) +___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32) +M32FLAG=$(___M32FLAG) +#ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" +# ifneq (,$(findstring arm, "$(shell $(CC) -v 2>&1 >/dev/null)")) +# M32FLAG = -mbe32 +# endif +#endif + +all: defork32.so defork64.so + +defork32.so: defork.c + -@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "defork32 build failure (that's fine)" + +defork64.so: defork.c + -@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "defork64 build failure (that's fine)" + +install: defork32.so defork64.so + install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ + if [ -f defork32.so ]; then set -e; install -m 755 defork32.so $(DESTDIR)$(HELPER_PATH)/; fi + if [ -f defork64.so ]; then set -e; install -m 755 defork64.so $(DESTDIR)$(HELPER_PATH)/; fi + +target: + ../../afl-clang forking_target.c -o forking_target -Wall -Wextra -Werror + +clean: + rm -f defork32.so defork64.so forking_target diff --git a/utils/defork/README.md b/utils/defork/README.md new file mode 100644 index 00000000..7e950323 --- /dev/null +++ b/utils/defork/README.md @@ -0,0 +1,11 @@ +# defork + +when the target forks, this breaks all normal fuzzing runs. +Sometimes, though, it is enough to just run the child process. +If this is the case, then this LD_PRELOAD library will always return 0 on fork, +the target will belive it is running as the child, post-fork. + +This is defork.c from the amazing preeny project +https://github.com/zardus/preeny + +It is altered for afl++ to work with its fork-server: the initial fork will go through, the second fork will be blocked. diff --git a/utils/defork/defork.c b/utils/defork/defork.c new file mode 100644 index 00000000..f71d1124 --- /dev/null +++ b/utils/defork/defork.c @@ -0,0 +1,50 @@ +#define __GNU_SOURCE +#include +#include +#include +#include + +#include "../../include/config.h" + +/* we want to fork once (for the afl++ forkserver), + then immediately return as child on subsequent forks. */ +static bool forked = 0; + +pid_t (*original_fork)(void); + +/* In case we are not running in afl, we use a dummy original_fork */ +static pid_t nop(void) { + + return 0; + +} + +__attribute__((constructor)) void preeny_fork_orig() { + + if (getenv(SHM_ENV_VAR)) { + + printf("defork: running in AFL++. Allowing forkserver.\n"); + original_fork = dlsym(RTLD_NEXT, "socket"); + + } else { + + printf("defork: no AFL++ detected. Disabling fork from the start.\n"); + original_fork = &nop; + + } + +} + +pid_t fork(void) { + + /* If we forked before, or if we're in the child (pid==0), + we don't want to fork anymore, else, we are still in the forkserver. + The forkserver parent needs to fork infinite times, each child should never + fork again. This can be written without branches and I hate myself for it. + */ + pid_t ret = !forked && original_fork(); + forked = !ret; + return ret; + +} + diff --git a/utils/defork/forking_target.c b/utils/defork/forking_target.c new file mode 100644 index 00000000..628d23c9 --- /dev/null +++ b/utils/defork/forking_target.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +/* This is an example target for defork.c - fuzz using +``` +mkdir in; echo a > ./in/a +AFL_PRELOAD=./defork64.so ../../afl-fuzz -i in -o out -- ./forking_target @@ +``` +*/ + +int main(int argc, char **argv) { + + if (argc < 2) { + + printf("Example tool to test defork.\nUsage ./forking_target \n"); + return -1; + + } + + pid_t pid = fork(); + if (pid == 0) { + + printf("We're in the child.\n"); + FILE *f = fopen(argv[1], "r"); + char buf[4096]; + fread(buf, 1, 4096, f); + fclose(f); + uint32_t offset = buf[100] + (buf[101] << 8); + char test_val = buf[offset]; + return test_val < 100; + + } else if (pid < 0) { + + perror("fork"); + return -1; + + } else { + + printf("We are in the parent - defork didn't work! :( (pid=%d)\n", + (int)pid); + + } + + return 0; + +} + diff --git a/utils/distributed_fuzzing/sync_script.sh b/utils/distributed_fuzzing/sync_script.sh new file mode 100755 index 00000000..b28ff6cd --- /dev/null +++ b/utils/distributed_fuzzing/sync_script.sh @@ -0,0 +1,97 @@ +#!/bin/sh +# +# american fuzzy lop++ - fuzzer synchronization tool +# -------------------------------------------------- +# +# Originally written by Michal Zalewski +# +# Copyright 2014 Google Inc. All rights reserved. +# Copyright 2019-2020 AFLplusplus Project. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# To make this script work: +# +# - Edit FUZZ_HOSTS, FUZZ_DOMAIN, FUZZ_USER, and SYNC_DIR to reflect your +# environment. +# +# - Make sure that the system you are running this on can log into FUZZ_HOSTS +# without a password (authorized_keys or otherwise). +# +# - Make sure that every fuzzer is running with -o pointing to SYNC_DIR and -S +# that consists of its local host name, followed by an underscore, and then +# by some host-local fuzzer ID. +# + +# Hosts to synchronize the data across. +FUZZ_HOSTS='host1 host2 host3 host4' + +# Domain for all hosts +FUZZ_DOMAIN='example.com' + +# Remote user for SSH +FUZZ_USER=bob + +# Directory to synchronize +SYNC_DIR='/home/bob/sync_dir' + +# We only capture -M main nodes, set the name to your chosen naming scheme +MAIN_NAME='main' + +# Interval (seconds) between sync attempts (eg one hour) +SYNC_INTERVAL=$((60 * 60)) + +if [ "$AFL_ALLOW_TMP" = "" ]; then + + if [ "$PWD" = "/tmp" -o "$PWD" = "/var/tmp" ]; then + echo "[-] Error: do not use shared /tmp or /var/tmp directories with this script." 1>&2 + exit 1 + fi + +fi + +rm -rf .sync_tmp 2>/dev/null +mkdir .sync_tmp || exit 1 + +while :; do + + # Pull data in... + + for host in $FUZZ_HOSTS; do + + echo "[*] Retrieving data from ${host}.${FUZZ_DOMAIN}..." + + ssh -o 'passwordauthentication no' ${FUZZ_USER}@${host}.$FUZZ_DOMAIN \ + "cd '$SYNC_DIR' && tar -czf - ${host}_${MAIN_NAME}*/" > ".sync_tmp/${host}.tgz" + + done + + # Distribute data. For large fleets, see tips in the docs/ directory. + + for dst_host in $FUZZ_HOSTS; do + + echo "[*] Distributing data to ${dst_host}.${FUZZ_DOMAIN}..." + + for src_host in $FUZZ_HOSTS; do + + test "$src_host" = "$dst_host" && continue + + echo " Sending fuzzer data from ${src_host}.${FUZZ_DOMAIN}..." + + ssh -o 'passwordauthentication no' ${FUZZ_USER}@$dst_host \ + "cd '$SYNC_DIR' && tar -xkzf - " < ".sync_tmp/${src_host}.tgz" + + done + + done + + echo "[+] Done. Sleeping for $SYNC_INTERVAL seconds (Ctrl-C to quit)." + + sleep $SYNC_INTERVAL + +done + diff --git a/utils/libpng_no_checksum/libpng-nocrc.patch b/utils/libpng_no_checksum/libpng-nocrc.patch new file mode 100644 index 00000000..0a3793a0 --- /dev/null +++ b/utils/libpng_no_checksum/libpng-nocrc.patch @@ -0,0 +1,15 @@ +--- pngrutil.c.orig 2014-06-12 03:35:16.000000000 +0200 ++++ pngrutil.c 2014-07-01 05:08:31.000000000 +0200 +@@ -268,7 +268,11 @@ + if (need_crc != 0) + { + crc = png_get_uint_32(crc_bytes); +- return ((int)(crc != png_ptr->crc)); ++ ++ if (crc != png_ptr->crc) ++ fprintf(stderr, "NOTE: CRC in the file is 0x%08x, change to 0x%08x\n", crc, png_ptr->crc); ++ ++ return ((int)(1 != 1)); + } + + else diff --git a/utils/persistent_mode/Makefile b/utils/persistent_mode/Makefile new file mode 100644 index 00000000..6fa1c30e --- /dev/null +++ b/utils/persistent_mode/Makefile @@ -0,0 +1,10 @@ +all: + afl-clang-fast -o persistent_demo persistent_demo.c + afl-clang-fast -o persistent_demo_new persistent_demo_new.c + AFL_DONT_OPTIMIZE=1 afl-clang-fast -o test-instr test-instr.c + +document: + AFL_DONT_OPTIMIZE=1 afl-clang-fast -D_AFL_DOCUMENT_MUTATIONS -o test-instr test-instr.c + +clean: + rm -f persistent_demo persistent_demo_new test-instr diff --git a/utils/persistent_mode/persistent_demo.c b/utils/persistent_mode/persistent_demo.c new file mode 100644 index 00000000..4cedc32c --- /dev/null +++ b/utils/persistent_mode/persistent_demo.c @@ -0,0 +1,112 @@ +/* + american fuzzy lop++ - persistent mode example + -------------------------------------------- + + Originally written by Michal Zalewski + + Copyright 2015 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This file demonstrates the high-performance "persistent mode" that may be + suitable for fuzzing certain fast and well-behaved libraries, provided that + they are stateless or that their internal state can be easily reset + across runs. + + To make this work, the library and this shim need to be compiled in LLVM + mode using afl-clang-fast (other compiler wrappers will *not* work). + + */ + +#include +#include +#include +#include +#include + +/* Main entry point. */ + +int main(int argc, char **argv) { + + ssize_t len; /* how much input did we read? */ + char buf[100]; /* Example-only buffer, you'd replace it with other global or + local variables appropriate for your use case. */ + + /* The number passed to __AFL_LOOP() controls the maximum number of + iterations before the loop exits and the program is allowed to + terminate normally. This limits the impact of accidental memory leaks + and similar hiccups. */ + + __AFL_INIT(); + while (__AFL_LOOP(1000)) { + + /*** PLACEHOLDER CODE ***/ + + /* STEP 1: Fully re-initialize all critical variables. In our example, this + involves zeroing buf[], our input buffer. */ + + memset(buf, 0, 100); + + /* STEP 2: Read input data. When reading from stdin, no special preparation + is required. When reading from a named file, you need to close + the old descriptor and reopen the file first! + + Beware of reading from buffered FILE* objects such as stdin. Use + raw file descriptors or call fopen() / fdopen() in every pass. */ + + len = read(0, buf, 100); + + /* STEP 3: This is where we'd call the tested library on the read data. + We just have some trivial inline code that faults on 'foo!'. */ + + /* do we have enough data? */ + if (len < 8) continue; + + if (buf[0] == 'f') { + + printf("one\n"); + if (buf[1] == 'o') { + + printf("two\n"); + if (buf[2] == 'o') { + + printf("three\n"); + if (buf[3] == '!') { + + printf("four\n"); + if (buf[4] == '!') { + + printf("five\n"); + if (buf[5] == '!') { + + printf("six\n"); + abort(); + + } + + } + + } + + } + + } + + } + + /*** END PLACEHOLDER CODE ***/ + + } + + /* Once the loop is exited, terminate normally - AFL will restart the process + when this happens, with a clean slate when it comes to allocated memory, + leftover file descriptors, etc. */ + + return 0; + +} + diff --git a/utils/persistent_mode/persistent_demo_new.c b/utils/persistent_mode/persistent_demo_new.c new file mode 100644 index 00000000..a29792ff --- /dev/null +++ b/utils/persistent_mode/persistent_demo_new.c @@ -0,0 +1,117 @@ +/* + american fuzzy lop++ - persistent mode example + -------------------------------------------- + + Originally written by Michal Zalewski + + Copyright 2015 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This file demonstrates the high-performance "persistent mode" that may be + suitable for fuzzing certain fast and well-behaved libraries, provided that + they are stateless or that their internal state can be easily reset + across runs. + + To make this work, the library and this shim need to be compiled in LLVM + mode using afl-clang-fast (other compiler wrappers will *not* work). + + */ + +#include +#include +#include +#include +#include + +/* this lets the source compile without afl-clang-fast/lto */ +#ifndef __AFL_FUZZ_TESTCASE_LEN + +ssize_t fuzz_len; +unsigned char fuzz_buf[1024000]; + + #define __AFL_FUZZ_TESTCASE_LEN fuzz_len + #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf + #define __AFL_FUZZ_INIT() void sync(void); + #define __AFL_LOOP(x) \ + ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0) + #define __AFL_INIT() sync() + +#endif + +__AFL_FUZZ_INIT(); + +/* Main entry point. */ + +int main(int argc, char **argv) { + + ssize_t len; /* how much input did we read? */ + unsigned char *buf; /* test case buffer pointer */ + + /* The number passed to __AFL_LOOP() controls the maximum number of + iterations before the loop exits and the program is allowed to + terminate normally. This limits the impact of accidental memory leaks + and similar hiccups. */ + + __AFL_INIT(); + buf = __AFL_FUZZ_TESTCASE_BUF; // this must be assigned before __AFL_LOOP! + + while (__AFL_LOOP(1000)) { // increase if you have good stability + + len = __AFL_FUZZ_TESTCASE_LEN; // do not use the macro directly in a call! + + fprintf(stderr, "input: %zd \"%s\"\n", len, buf); + + /* do we have enough data? */ + if (len < 8) continue; + + if (strcmp((char *)buf, "thisisateststring") == 0) printf("teststring\n"); + + if (buf[0] == 'f') { + + printf("one\n"); + if (buf[1] == 'o') { + + printf("two\n"); + if (buf[2] == 'o') { + + printf("three\n"); + if (buf[3] == '!') { + + printf("four\n"); + if (buf[4] == '!') { + + printf("five\n"); + if (buf[6] == '!') { + + printf("six\n"); + abort(); + + } + + } + + } + + } + + } + + } + + /*** END PLACEHOLDER CODE ***/ + + } + + /* Once the loop is exited, terminate normally - AFL will restart the process + when this happens, with a clean slate when it comes to allocated memory, + leftover file descriptors, etc. */ + + return 0; + +} + diff --git a/utils/persistent_mode/test-instr.c b/utils/persistent_mode/test-instr.c new file mode 100644 index 00000000..a6188b22 --- /dev/null +++ b/utils/persistent_mode/test-instr.c @@ -0,0 +1,69 @@ +/* + american fuzzy lop++ - a trivial program to test the build + -------------------------------------------------------- + Originally written by Michal Zalewski + Copyright 2014 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + http://www.apache.org/licenses/LICENSE-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +__AFL_FUZZ_INIT(); + +int main(int argc, char **argv) { + + __AFL_INIT(); + unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; + + while (__AFL_LOOP(2147483647)) { // MAX_INT if you have 100% stability + + unsigned int len = __AFL_FUZZ_TESTCASE_LEN; + +#ifdef _AFL_DOCUMENT_MUTATIONS + static unsigned int counter = 0; + char fn[32]; + sprintf(fn, "%09u:test-instr", counter); + int fd_doc = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd_doc >= 0) { + + if (write(fd_doc, buf, len) != __afl_fuzz_len) { + + fprintf(stderr, "write of mutation file failed: %s\n", fn); + unlink(fn); + + } + + close(fd_doc); + + } + + counter++; +#endif + + // fprintf(stderr, "len: %u\n", len); + + if (!len) continue; + + if (buf[0] == '0') + printf("Looks like a zero to me!\n"); + else if (buf[0] == '1') + printf("Pretty sure that is a one!\n"); + else + printf("Neither one or zero? How quaint!\n"); + + } + + return 0; + +} + diff --git a/utils/qemu_persistent_hook/Makefile b/utils/qemu_persistent_hook/Makefile new file mode 100644 index 00000000..85db1b46 --- /dev/null +++ b/utils/qemu_persistent_hook/Makefile @@ -0,0 +1,6 @@ +all: + $(CC) -no-pie test.c -o test + $(CC) -fPIC -shared read_into_rdi.c -o read_into_rdi.so + +clean: + rm -rf in out test read_into_rdi.so diff --git a/utils/qemu_persistent_hook/README.md b/utils/qemu_persistent_hook/README.md new file mode 100644 index 00000000..3f908c22 --- /dev/null +++ b/utils/qemu_persistent_hook/README.md @@ -0,0 +1,19 @@ +# QEMU persistent hook example + +Compile the test binary and the library: + +``` +make +``` + +Fuzz with: + +``` +export AFL_QEMU_PERSISTENT_ADDR=0x$(nm test | grep "T target_func" | awk '{print $1}') +export AFL_QEMU_PERSISTENT_HOOK=./read_into_rdi.so + +mkdir in +echo 0000 > in/in + +../../afl-fuzz -Q -i in -o out -- ./test +``` diff --git a/utils/qemu_persistent_hook/read_into_rdi.c b/utils/qemu_persistent_hook/read_into_rdi.c new file mode 100644 index 00000000..f4a8ae59 --- /dev/null +++ b/utils/qemu_persistent_hook/read_into_rdi.c @@ -0,0 +1,34 @@ +#include "../../qemu_mode/qemuafl/qemuafl/api.h" + +#include +#include + +void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base, + uint8_t *input_buf, uint32_t input_buf_len) { +\ +#define g2h(x) ((void *)((unsigned long)(x) + guest_base)) +#define h2g(x) ((uint64_t)(x)-guest_base) + + // In this example the register RDI is pointing to the memory location + // of the target buffer, and the length of the input is in RSI. + // This can be seen with a debugger, e.g. gdb (and "disass main") + + printf("Placing input into 0x%lx\n", regs->rdi); + + if (input_buf_len > 1024) input_buf_len = 1024; + memcpy(g2h(regs->rdi), input_buf, input_buf_len); + regs->rsi = input_buf_len; + +#undef g2h +#undef h2g + +} + +int afl_persistent_hook_init(void) { + + // 1 for shared memory input (faster), 0 for normal input (you have to use + // read(), input_buf will be NULL) + return 1; + +} + diff --git a/utils/qemu_persistent_hook/test.c b/utils/qemu_persistent_hook/test.c new file mode 100644 index 00000000..afeff202 --- /dev/null +++ b/utils/qemu_persistent_hook/test.c @@ -0,0 +1,35 @@ +#include + +int target_func(unsigned char *buf, int size) { + + printf("buffer:%p, size:%p\n", buf, size); + switch (buf[0]) { + + case 1: + if (buf[1] == '\x44') { puts("a"); } + break; + case 0xff: + if (buf[2] == '\xff') { + + if (buf[1] == '\x44') { puts("b"); } + + } + + break; + default: + break; + + } + + return 1; + +} + +char data[1024]; + +int main() { + + target_func(data, 1024); + +} + diff --git a/utils/socket_fuzzing/Makefile b/utils/socket_fuzzing/Makefile new file mode 100644 index 00000000..9476e2d5 --- /dev/null +++ b/utils/socket_fuzzing/Makefile @@ -0,0 +1,61 @@ +# +# american fuzzy lop++ - socket_fuzz +# ---------------------------------- +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +.PHONY: all install clean + +PREFIX ?= /usr/local +BIN_PATH = $(PREFIX)/bin +HELPER_PATH = $(PREFIX)/lib/afl + +CFLAGS = -fPIC -Wall -Wextra +LDFLAGS = -shared + +UNAME_SAYS_LINUX=$(shell uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?) +UNAME_SAYS_LINUX:sh=uname | grep -E '^Linux|^GNU' >/dev/null; echo $$? + +_LDFLAGS_ADD=$(UNAME_SAYS_LINUX:1=) +LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-ldl) +LDFLAGS += $(LDFLAGS_ADD) + +# on gcc for arm there is no -m32, but -mbe32 +M32FLAG = -m32 +M64FLAG = -m64 + +CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?) +CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$? +CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?) +CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$? + +_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER) +__M32FLAG=$(_M32FLAG:00=-mbe32) +___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32) +M32FLAG=$(___M32FLAG) +#ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" +# ifneq (,$(findstring arm, "$(shell $(CC) -v 2>&1 >/dev/null)")) +# M32FLAG = -mbe32 +# endif +#endif + +all: socketfuzz32.so socketfuzz64.so + +socketfuzz32.so: socketfuzz.c + -@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "socketfuzz32 build failure (that's fine)" + +socketfuzz64.so: socketfuzz.c + -@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "socketfuzz64 build failure (that's fine)" + +install: socketfuzz32.so socketfuzz64.so + install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ + if [ -f socketfuzz32.so ]; then set -e; install -m 755 socketfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi + if [ -f socketfuzz64.so ]; then set -e; install -m 755 socketfuzz64.so $(DESTDIR)$(HELPER_PATH)/; fi + +clean: + rm -f socketfuzz32.so socketfuzz64.so diff --git a/utils/socket_fuzzing/README.md b/utils/socket_fuzzing/README.md new file mode 100644 index 00000000..79f28bea --- /dev/null +++ b/utils/socket_fuzzing/README.md @@ -0,0 +1,11 @@ +# socketfuzz + +when you want to fuzz a network service and you can not/do not want to modify +the source (or just have a binary), then this LD_PRELOAD library will allow +for sending input to stdin which the target binary will think is coming from +a network socket. + +This is desock_dup.c from the amazing preeny project +https://github.com/zardus/preeny + +It is packaged in afl++ to have it at hand if needed diff --git a/utils/socket_fuzzing/socketfuzz.c b/utils/socket_fuzzing/socketfuzz.c new file mode 100644 index 00000000..3ec8383b --- /dev/null +++ b/utils/socket_fuzzing/socketfuzz.c @@ -0,0 +1,110 @@ +/* + * This is desock_dup.c from the amazing preeny project + * https://github.com/zardus/preeny + * + * It is packaged in afl++ to have it at hand if needed + * + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include // +#include // +#include // +#include // +#include +#include +#include +#include +#include +#include +#include +//#include "logging.h" // switche from preeny_info() to fprintf(stderr, "Info: " + +// +// originals +// +int (*original_close)(int); +int (*original_dup2)(int, int); +__attribute__((constructor)) void preeny_desock_dup_orig() { + + original_close = dlsym(RTLD_NEXT, "close"); + original_dup2 = dlsym(RTLD_NEXT, "dup2"); + +} + +int close(int sockfd) { + + if (sockfd <= 2) { + + fprintf(stderr, "Info: Disabling close on %d\n", sockfd); + return 0; + + } else { + + return original_close(sockfd); + + } + +} + +int dup2(int old, int new) { + + if (new <= 2) { + + fprintf(stderr, "Info: Disabling dup from %d to %d\n", old, new); + return 0; + + } else { + + return original_dup2(old, new); + + } + +} + +int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { + + (void)sockfd; + (void)addr; + (void)addrlen; + fprintf(stderr, "Info: Emulating accept on %d\n", sockfd); + return 0; + +} + +int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + + (void)sockfd; + (void)addr; + (void)addrlen; + fprintf(stderr, "Info: Emulating bind on port %d\n", + ntohs(((struct sockaddr_in *)addr)->sin_port)); + return 0; + +} + +int listen(int sockfd, int backlog) { + + (void)sockfd; + (void)backlog; + return 0; + +} + +int setsockopt(int sockfd, int level, int optid, const void *optdata, + socklen_t optdatalen) { + + (void)sockfd; + (void)level; + (void)optid; + (void)optdata; + (void)optdatalen; + return 0; + +} + -- cgit 1.4.1 From 1890d7b9cf5a32368c0700ef790f97087b948e1f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 2 Dec 2020 15:03:21 +0100 Subject: very complete runtime lookup rewrite --- src/afl-cc.c | 197 ++++++++++++++++++++++++++++---------------------- test/test-llvm-lto.sh | 2 +- 2 files changed, 113 insertions(+), 86 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index cc9854b6..2b5ae7c3 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -106,20 +106,42 @@ u8 *getthecwd() { } -/* Try to find the runtime libraries. If that fails, abort. */ +/* Try to find a specific runtime we need, returns NULL on fail. */ + +/* + in find_object() we look here: + + 1. if obj_path is already set we look there first + 2. then we check the $AFL_PATH environment variable location if set + 3. next we check argv[0] if has path information and use it + a) we also check ../lib/ + 4. if 3. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and + FreeBSD with procfs + a) and check here in ../lib/ too + 5. we look into the AFL_PATH define (usually /usr/local/lib/afl) + 6. we finally try the current directory + + if this all fail - we fail. +*/ static u8 *find_object(u8 *obj, u8 *argv0) { u8 *afl_path = getenv("AFL_PATH"); u8 *slash = NULL, *tmp; + if (obj_path) { + + tmp = alloc_printf("%s/%s", obj_path, obj); + + if (!access(tmp, R_OK)) { return tmp; } + + ck_free(tmp); + + } + if (afl_path) { -#ifdef __ANDROID__ tmp = alloc_printf("%s/%s", afl_path, obj); -#else - tmp = alloc_printf("%s/%s", afl_path, obj); -#endif if (!access(tmp, R_OK)) { @@ -132,125 +154,117 @@ static u8 *find_object(u8 *obj, u8 *argv0) { } - if (argv0) slash = strrchr(argv0, '/'); + if (argv0) { - if (slash) { + slash = strrchr(argv0, '/'); - u8 *dir; + if (slash) { - *slash = 0; - dir = ck_strdup(argv0); - *slash = '/'; + u8 *dir = ck_strdup(argv0); -#ifdef __ANDROID__ - tmp = alloc_printf("%s/%s", dir, obj); -#else - tmp = alloc_printf("%s/%s", dir, obj); -#endif + slash = strrchr(dir, '/'); + *slash = 0; - if (!access(tmp, R_OK)) { + tmp = alloc_printf("%s/%s", dir, obj); - obj_path = dir; - return tmp; + if (!access(tmp, R_OK)) { - } + obj_path = dir; + return tmp; - ck_free(tmp); - ck_free(dir); + } - } + ck_free(tmp); + tmp = alloc_printf("%s/../lib/%s", dir, obj); - tmp = alloc_printf("%s/%s", AFL_PATH, obj); -#ifdef __ANDROID__ - if (!access(tmp, R_OK)) { + if (!access(tmp, R_OK)) { -#else - if (!access(tmp, R_OK)) { + u8 *dir2 = alloc_printf("%s/../lib", dir); + obj_path = dir2; + ck_free(dir); + return tmp; -#endif + } - obj_path = AFL_PATH; - return tmp; + ck_free(tmp); + ck_free(dir); - } + } else { - ck_free(tmp); - return NULL; + char *procname = NULL; +#if defined(__FreeBSD__) || defined(__DragonFly__) + procname = "/proc/curproc/file"; + #elsif defined(__linux__) || defined(__ANDROID__) + procname = "/proc/self/exe"; + #elsif defined(__NetBSD__) + procname = "/proc/curproc/exe"; +#endif + if (procname) { -} + char exepath[PATH_MAX]; + ssize_t exepath_len = readlink(procname, exepath, sizeof(exepath)); + if (exepath_len > 0 && exepath_len < PATH_MAX) { -/* Try to find the runtime libraries. If that fails, abort. */ + exepath[exepath_len] = 0; + slash = strrchr(exepath, '/'); -static void find_obj(u8 *argv0) { + if (slash) { - u8 *afl_path = getenv("AFL_PATH"); - u8 *slash, *tmp; + *slash = 0; + tmp = alloc_printf("%s/%s", exepath, obj); - if (afl_path) { + if (!access(tmp, R_OK)) { -#ifdef __ANDROID__ - tmp = alloc_printf("%s/afl-compiler-rt.so", afl_path); -#else - tmp = alloc_printf("%s/afl-compiler-rt.o", afl_path); -#endif + u8 *dir = alloc_printf("%s/../lib/", exepath); + obj_path = dir; + return tmp; - if (!access(tmp, R_OK)) { + } - obj_path = afl_path; - ck_free(tmp); - return; + ck_free(tmp); + tmp = alloc_printf("%s/../lib/%s", exepath, obj); - } + if (!access(tmp, R_OK)) { - ck_free(tmp); + u8 *dir = alloc_printf("%s/../lib/", exepath); + obj_path = dir; + return tmp; - } - - slash = strrchr(argv0, '/'); + } - if (slash) { + } - u8 *dir; + } - *slash = 0; - dir = ck_strdup(argv0); - *slash = '/'; + } -#ifdef __ANDROID__ - tmp = alloc_printf("%s/afl-compiler-rt.so", dir); -#else - tmp = alloc_printf("%s/afl-compiler-rt.o", dir); -#endif + } - if (!access(tmp, R_OK)) { + } - obj_path = dir; - ck_free(tmp); - return; + tmp = alloc_printf("%s/%s", AFL_PATH, obj); - } + if (!access(tmp, R_OK)) { - ck_free(tmp); - ck_free(dir); + obj_path = AFL_PATH; + return tmp; } -#ifdef __ANDROID__ - if (!access(AFL_PATH "/afl-compiler-rt.so", R_OK)) { + ck_free(tmp); -#else - if (!access(AFL_PATH "/afl-compiler-rt.o", R_OK)) { + tmp = alloc_printf("./%s", obj); -#endif + if (!access(tmp, R_OK)) { - obj_path = AFL_PATH; - return; + obj_path = "."; + return tmp; } - FATAL( - "Unable to find 'afl-compiler-rt.o' or 'afl-llvm-pass.so'. Please set " - "AFL_PATH"); + ck_free(tmp); + + return NULL; } @@ -360,8 +374,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode == GCC_PLUGIN) { - char *fplugin_arg = - alloc_printf("-fplugin=%s", find_object("afl-gcc-pass.so", argvnull)); + char *fplugin_arg = alloc_printf("-fplugin=%s/afl-gcc-pass.so", obj_path); cc_params[cc_par_cnt++] = fplugin_arg; } @@ -1594,10 +1607,24 @@ int main(int argc, char **argv, char **envp) { if (!be_quiet && cmplog_mode) printf("CmpLog mode by \n"); -#ifndef __ANDROID__ - find_obj(argv[0]); +#ifdef __ANDROID__ + ptr = find_object("afl-compiler-rt.so", argv[0]); +#else + ptr = find_object("afl-compiler-rt.o", argv[0]); #endif + if (debug) { DEBUGF("obj_path=%s\n", obj_path); } + + if (!ptr) { + + FATAL( + "Unable to find 'afl-compiler-rt.o'. Please set the AFL_PATH " + "environment variable."); + + } + + ck_free(ptr); + edit_params(argc, argv, envp); if (debug) { diff --git a/test/test-llvm-lto.sh b/test/test-llvm-lto.sh index e10f4cf7..d0b8f8fc 100755 --- a/test/test-llvm-lto.sh +++ b/test/test-llvm-lto.sh @@ -57,7 +57,7 @@ test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && { CODE=1 } rm -f test-compcov test.out instrumentlist.txt - ../afl-clang-lto -o test-persistent ../utils/persistent_mode/persistent_mode.c > /dev/null 2>&1 + ../afl-clang-lto -o test-persistent ../utils/persistent_mode/persistent_demo.c > /dev/null 2>&1 test -e test-persistent && { echo foo | ../afl-showmap -m none -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] llvm_mode LTO persistent mode feature works correctly" -- cgit 1.4.1 From 0f803c63dfb1dafdef3bfe1b43674157efcd7107 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 2 Dec 2020 15:08:08 +0100 Subject: move debug print --- src/afl-cc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 2b5ae7c3..854359e2 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1613,8 +1613,6 @@ int main(int argc, char **argv, char **envp) { ptr = find_object("afl-compiler-rt.o", argv[0]); #endif - if (debug) { DEBUGF("obj_path=%s\n", obj_path); } - if (!ptr) { FATAL( @@ -1623,6 +1621,8 @@ int main(int argc, char **argv, char **envp) { } + if (debug) { DEBUGF("rt=%s obj_path=%s\n", ptr, obj_path); } + ck_free(ptr); edit_params(argc, argv, envp); -- cgit 1.4.1 From a2e2fae840e9946c7994ac6807bed8496d71af56 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Thu, 3 Dec 2020 14:43:06 +0100 Subject: AFL_CRASH_EXITCODE env var added, u8->bool --- .gitignore | 1 + afl-cmin | 5 +++-- docs/Changelog.md | 4 +++- docs/env_variables.md | 7 +++++++ include/afl-fuzz.h | 7 ++++--- include/common.h | 2 +- include/envs.h | 1 + include/forkserver.h | 21 +++++++++++++-------- src/afl-analyze.c | 4 ++-- src/afl-common.c | 4 ++-- src/afl-forkserver.c | 22 +++++++++++++++------- src/afl-fuzz-init.c | 27 +++++++++++++++++++++++++-- src/afl-fuzz-state.c | 7 +++++++ src/afl-fuzz.c | 26 ++++++++++++++++++++++++-- src/afl-showmap.c | 19 +++++++++++++++++++ src/afl-tmin.c | 32 +++++++++++++++++++++++++------- 16 files changed, 152 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/.gitignore b/.gitignore index 97f99bf6..82a81605 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.pyc *.dSYM as +a.out ld in out diff --git a/afl-cmin b/afl-cmin index 91ed8d6d..b3b1ead8 100755 --- a/afl-cmin +++ b/afl-cmin @@ -116,11 +116,12 @@ function usage() { "For additional tips, please consult README.md\n" \ "\n" \ "Environment variables used:\n" \ +"AFL_ALLOW_TMP: allow unsafe use of input/output directories under {/var}/tmp\n" \ +"AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" \ +"AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" \ "AFL_KEEP_TRACES: leave the temporary /.traces directory\n" \ "AFL_PATH: path for the afl-showmap binary\n" \ "AFL_SKIP_BIN_CHECK: skip check for target binary\n" \ -"AFL_ALLOW_TMP: allow unsafe use of input/output directories under {/var}/tmp\n" -"AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" exit 1 } diff --git a/docs/Changelog.md b/docs/Changelog.md index fd30c7b0..02728f10 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -60,8 +60,10 @@ sending a mail to . - Our afl++ Grammar-Mutator is now better integrated into custom_mutators/ - added INTROSPECTION support for custom modules - python fuzz function was not optional, fixed - - unicornafl synced with upstream (arm64 fix, better rust bindings) + - some python mutator speed improvements + - unicornafl synced with upstream version 1.02 (fixes, better rust bindings) - renamed AFL_DEBUG_CHILD_OUTPUT to AFL_DEBUG_CHILD + - added AFL_CRASH_EXITCODE env variable to treat a child exitcode as crash ### Version ++2.68c (release) diff --git a/docs/env_variables.md b/docs/env_variables.md index ada89257..e203055f 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -428,6 +428,13 @@ checks or alter some of the more exotic semantics of the tool: matches your StatsD server. Available flavors are `dogstatsd`, `librato`, `signalfx` and `influxdb`. + - Setting `AFL_CRASH_EXITCODE` sets the exit code afl treats as crash. + For example, if `AFL_CRASH_EXITCODE='-1'` is set, each input resulting + in an `-1` return code (i.e. `exit(-1)` got called), will be treated + as if a crash had ocurred. + This may be beneficial if you look for higher-level faulty conditions in which your + target still exits gracefully. + - Outdated environment variables that are not supported anymore: `AFL_DEFER_FORKSRV` `AFL_PERSISTENT` diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 933af65d..62d76323 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -144,8 +144,8 @@ struct queue_entry { u8 *fname; /* File name for the test case */ u32 len; /* Input length */ - u8 cal_failed, /* Calibration failed? */ - trim_done, /* Trimmed? */ + u8 cal_failed; /* Calibration failed? */ + bool trim_done, /* Trimmed? */ was_fuzzed, /* historical, but needed for MOpt */ passed_det, /* Deterministic stages passed? */ has_new_cov, /* Triggers new coverage? */ @@ -368,7 +368,8 @@ typedef struct afl_env_vars { u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload, *afl_max_det_extras, *afl_statsd_host, *afl_statsd_port, - *afl_statsd_tags_flavor, *afl_testcache_size, *afl_testcache_entries; + *afl_crash_exitcode, *afl_statsd_tags_flavor, *afl_testcache_size, + *afl_testcache_entries; } afl_env_vars_t; diff --git a/include/common.h b/include/common.h index c364ade0..6e5039d8 100644 --- a/include/common.h +++ b/include/common.h @@ -38,7 +38,7 @@ #define STRINGIFY_VAL_SIZE_MAX (16) -void detect_file_args(char **argv, u8 *prog_in, u8 *use_stdin); +void detect_file_args(char **argv, u8 *prog_in, bool *use_stdin); void check_environment_vars(char **env); char **argv_cpy_dup(int argc, char **argv); diff --git a/include/envs.h b/include/envs.h index 3aa05cb5..43c87148 100644 --- a/include/envs.h +++ b/include/envs.h @@ -32,6 +32,7 @@ static char *afl_environment_variables[] = { "AFL_CODE_START", "AFL_COMPCOV_BINNAME", "AFL_COMPCOV_LEVEL", + "AFL_CRASH_EXITCODE", "AFL_CUSTOM_MUTATOR_LIBRARY", "AFL_CUSTOM_MUTATOR_ONLY", "AFL_CXX", diff --git a/include/forkserver.h b/include/forkserver.h index 300ecffc..5d5c728f 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -37,9 +37,7 @@ typedef struct afl_forkserver { /* a program that includes afl-forkserver needs to define these */ - u8 uses_asan; /* Target uses ASAN? */ u8 *trace_bits; /* SHM with instrumentation bitmap */ - u8 use_stdin; /* use stdin for sending data */ s32 fsrv_pid, /* PID of the fork server */ child_pid, /* PID of the fuzzed program */ @@ -53,8 +51,6 @@ typedef struct afl_forkserver { fsrv_ctl_fd, /* Fork server control pipe (write) */ fsrv_st_fd; /* Fork server status pipe (read) */ - u8 no_unlink; /* do not unlink cur_input */ - u32 exec_tmout; /* Configurable exec timeout (ms) */ u32 init_tmout; /* Configurable init timeout (ms) */ u32 map_size; /* map size used by the target */ @@ -73,13 +69,22 @@ typedef struct afl_forkserver { u8 last_kill_signal; /* Signal that killed the child */ - u8 use_shmem_fuzz; /* use shared mem for test cases */ + bool use_shmem_fuzz; /* use shared mem for test cases */ + + bool support_shmem_fuzz; /* set by afl-fuzz */ + + bool use_fauxsrv; /* Fauxsrv for non-forking targets? */ + + bool qemu_mode; /* if running in qemu mode or not */ + + bool use_stdin; /* use stdin for sending data */ - u8 support_shmem_fuzz; /* set by afl-fuzz */ + bool no_unlink; /* do not unlink cur_input */ - u8 use_fauxsrv; /* Fauxsrv for non-forking targets? */ + bool uses_asan; /* Target uses ASAN? */ - u8 qemu_mode; /* if running in qemu mode or not */ + bool uses_crash_exitcode; /* Custom crash exitcode specified? */ + u8 crash_exitcode; /* The crash exitcode specified */ u32 *shmem_fuzz_len; /* length of the fuzzing test case */ diff --git a/src/afl-analyze.c b/src/afl-analyze.c index c8acebb3..2780deff 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -78,9 +78,9 @@ static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */ static s32 dev_null_fd = -1; /* FD to /dev/null */ -static u8 edges_only, /* Ignore hit counts? */ +static bool edges_only, /* Ignore hit counts? */ use_hex_offsets, /* Show hex offsets? */ - use_stdin = 1; /* Use stdin for program input? */ + use_stdin = true; /* Use stdin for program input? */ static volatile u8 stop_soon, /* Ctrl-C pressed? */ child_timed_out; /* Child timed out? */ diff --git a/src/afl-common.c b/src/afl-common.c index 8cf1a444..ed0b0e53 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -46,7 +46,7 @@ u8 be_quiet = 0; u8 *doc_path = ""; u8 last_intr = 0; -void detect_file_args(char **argv, u8 *prog_in, u8 *use_stdin) { +void detect_file_args(char **argv, u8 *prog_in, bool *use_stdin) { u32 i = 0; u8 cwd[PATH_MAX]; @@ -63,7 +63,7 @@ void detect_file_args(char **argv, u8 *prog_in, u8 *use_stdin) { if (!prog_in) { FATAL("@@ syntax is not supported by this tool."); } - *use_stdin = 0; + *use_stdin = false; if (prog_in[0] != 0) { // not afl-showmap special case diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 01ef1d9e..20117c1d 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -76,8 +76,8 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->dev_urandom_fd = -1; /* Settings */ - fsrv->use_stdin = 1; - fsrv->no_unlink = 0; + fsrv->use_stdin = true; + fsrv->no_unlink = false; fsrv->exec_tmout = EXEC_TIMEOUT; fsrv->init_tmout = EXEC_TIMEOUT * FORK_WAIT_MULT; fsrv->mem_limit = MEM_LIMIT; @@ -86,8 +86,11 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { /* exec related stuff */ fsrv->child_pid = -1; fsrv->map_size = get_map_size(); - fsrv->use_fauxsrv = 0; - fsrv->last_run_timed_out = 0; + fsrv->use_fauxsrv = false; + fsrv->last_run_timed_out = false; + + fsrv->uses_crash_exitcode = false; + fsrv->uses_asan = false; fsrv->init_child_func = fsrv_exec_child; @@ -109,6 +112,8 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->dev_urandom_fd = from->dev_urandom_fd; fsrv_to->out_fd = from->out_fd; // not sure this is a good idea fsrv_to->no_unlink = from->no_unlink; + fsrv_to->uses_crash_exitcode = from->uses_crash_exitcode; + fsrv_to->crash_exitcode = from->crash_exitcode; // These are forkserver specific. fsrv_to->out_dir_fd = -1; @@ -1136,10 +1141,13 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, } - /* A somewhat nasty hack for MSAN, which doesn't support abort_on_error and - must use a special exit code. */ + /* MSAN in uses_asan mode uses a special exit code as it doesn't support + abort_on_error. + On top, a user may specify a custom AFL_CRASH_EXITCODE. Handle both here. */ - if (fsrv->uses_asan && WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) { + if ((fsrv->uses_asan && WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) || + (fsrv->uses_crash_exitcode && + WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode)) { fsrv->last_kill_signal = 0; return FSRV_RUN_CRASH; diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 0360cdb0..6707340b 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -868,7 +868,19 @@ void perform_dry_run(afl_state_t *afl) { if (skip_crashes) { - WARNF("Test case results in a crash (skipping)"); + if (afl->fsrv.uses_crash_exitcode) { + + WARNF( + "Test case results in a crash or AFL_CRASH_EXITCODE %d " + "(skipping)", + (int)(s8)afl->fsrv.crash_exitcode); + + } else { + + WARNF("Test case results in a crash (skipping)"); + + } + q->cal_failed = CAL_CHANCES; ++cal_failures; break; @@ -954,7 +966,18 @@ void perform_dry_run(afl_state_t *afl) { #undef MSG_ULIMIT_USAGE #undef MSG_FORK_ON_APPLE - WARNF("Test case '%s' results in a crash, skipping", fn); + if (afl->fsrv.uses_crash_exitcode) { + + WARNF( + "Test case '%s' results in a crash or AFL_CRASH_EXITCODE %d, " + "skipping", + fn, (int)(s8)afl->fsrv.crash_exitcode); + + } else { + + WARNF("Test case '%s' results in a crash, skipping", fn); + + } /* Remove from fuzzing queue but keep for splicing */ diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 489d4e53..73b94466 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -394,6 +394,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_statsd_tags_flavor = (u8 *)get_afl_env(afl_environment_variables[i]); + } else if (!strncmp(env, "AFL_CRASH_EXITCODE", + + afl_environment_variable_len)) { + + afl->afl_env.afl_crash_exitcode = + (u8 *)get_afl_env(afl_environment_variables[i]); + } } else { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index b91d862d..eb5e9307 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -26,6 +26,7 @@ #include "afl-fuzz.h" #include "cmplog.h" #include +#include #ifndef USEMMAP #include #include @@ -165,6 +166,7 @@ static void usage(u8 *argv0, int more_help) { "AFL_AUTORESUME: resume fuzzing if directory specified by -o already exists\n" "AFL_BENCH_JUST_ONE: run the target just once\n" "AFL_BENCH_UNTIL_CRASH: exit soon when the first crashing input has been found\n" + "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" "AFL_CUSTOM_MUTATOR_LIBRARY: lib with afl_custom_fuzz() to mutate inputs\n" "AFL_CUSTOM_MUTATOR_ONLY: avoid AFL++'s internal mutators\n" "AFL_CYCLE_SCHEDULES: after completing a cycle, switch to a different -p schedule\n" @@ -702,7 +704,7 @@ int main(int argc, char **argv_orig, char **envp) { case 'N': /* Unicorn mode */ if (afl->no_unlink) { FATAL("Multiple -N options not supported"); } - afl->fsrv.no_unlink = afl->no_unlink = 1; + afl->fsrv.no_unlink = (afl->no_unlink = true); break; @@ -1135,6 +1137,23 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->afl_env.afl_crash_exitcode) { + + long exitcode = strtol(afl->afl_env.afl_crash_exitcode, NULL, 10); + if ((!exitcode && (errno == EINVAL || errno == ERANGE)) || + exitcode < -127 || exitcode > 128) { + + FATAL("Invalid crash exitcode, expected -127 to 128, but got %s", + afl->afl_env.afl_crash_exitcode); + + } + + afl->fsrv.uses_crash_exitcode = true; + // WEXITSTATUS is 8 bit unsigned + afl->fsrv.crash_exitcode = (u8)exitcode; + + } + if (afl->non_instrumented_mode == 2 && afl->no_forkserver) { FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); @@ -1486,9 +1505,12 @@ int main(int argc, char **argv_orig, char **envp) { cull_queue(afl); - if (!afl->pending_not_fuzzed) + if (!afl->pending_not_fuzzed) { + FATAL("We need at least on valid input seed that does not crash!"); + } + show_init_stats(afl); if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl); diff --git a/src/afl-showmap.c b/src/afl-showmap.c index a8e7d3f9..e07e76c8 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -667,6 +667,8 @@ static void usage(u8 *argv0) { "AFL_CMIN_CRASHES_ONLY: (cmin_mode) only write tuples for crashing " "inputs\n" "AFL_CMIN_ALLOW_ANY: (cmin_mode) write tuples for crashing inputs also\n" + "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as " + "crash\n" "AFL_DEBUG: enable extra developer output\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the " "size\n" @@ -1090,6 +1092,23 @@ int main(int argc, char **argv_orig, char **envp) { } + if (getenv("AFL_CRASH_EXITCODE")) { + + long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10); + if ((!exitcode && (errno == EINVAL || errno == ERANGE)) || + exitcode < -127 || exitcode > 128) { + + FATAL("Invalid crash exitcode, expected -127 to 128, but got %s", + getenv("AFL_CRASH_EXITCODE")); + + } + + fsrv->uses_crash_exitcode = true; + // WEXITSTATUS is 8 bit unsigned + fsrv->crash_exitcode = (u8)exitcode; + + } + afl_fsrv_start(fsrv, use_argv, &stop_soon, (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) diff --git a/src/afl-tmin.c b/src/afl-tmin.c index e4fb068d..b9045551 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -841,17 +842,17 @@ static void usage(u8 *argv0) { "For additional tips, please consult %s/README.md.\n\n" "Environment variables used:\n" - "TMPDIR: directory to use for temporary input files\n" - "ASAN_OPTIONS: custom settings for ASAN\n" - " (must contain abort_on_error=1 and symbolize=0)\n" - "MSAN_OPTIONS: custom settings for MSAN\n" - " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n" + "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" + "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_TMIN_EXACT: require execution paths to match for crashing inputs\n" - "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" - + "ASAN_OPTIONS: custom settings for ASAN\n" + " (must contain abort_on_error=1 and symbolize=0)\n" + "MSAN_OPTIONS: custom settings for MSAN\n" + " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n" + "TMPDIR: directory to use for temporary input files\n" , argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); exit(1); @@ -1122,6 +1123,23 @@ int main(int argc, char **argv_orig, char **envp) { } + if (getenv("AFL_CRASH_EXITCODE")) { + + long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10); + if ((!exitcode && (errno == EINVAL || errno == ERANGE)) || + exitcode < -127 || exitcode > 128) { + + FATAL("Invalid crash exitcode, expected -127 to 128, but got %s", + getenv("AFL_CRASH_EXITCODE")); + + } + + fsrv->uses_crash_exitcode = true; + // WEXITSTATUS is 8 bit unsigned + fsrv->crash_exitcode = (u8)exitcode; + + } + shm_fuzz = ck_alloc(sizeof(sharedmem_t)); /* initialize cmplog_mode */ -- cgit 1.4.1 From 295ddaf96b411b41017e609b6b3537db78147dfa Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 3 Dec 2020 15:19:10 +0100 Subject: fix for afl-cc --- src/afl-cc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 854359e2..cc4f44f5 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -114,10 +114,10 @@ u8 *getthecwd() { 1. if obj_path is already set we look there first 2. then we check the $AFL_PATH environment variable location if set 3. next we check argv[0] if has path information and use it - a) we also check ../lib/ + a) we also check ../lib/afl 4. if 3. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and FreeBSD with procfs - a) and check here in ../lib/ too + a) and check here in ../lib/afl too 5. we look into the AFL_PATH define (usually /usr/local/lib/afl) 6. we finally try the current directory @@ -175,11 +175,11 @@ static u8 *find_object(u8 *obj, u8 *argv0) { } ck_free(tmp); - tmp = alloc_printf("%s/../lib/%s", dir, obj); + tmp = alloc_printf("%s/../lib/afl/%s", dir, obj); if (!access(tmp, R_OK)) { - u8 *dir2 = alloc_printf("%s/../lib", dir); + u8 *dir2 = alloc_printf("%s/../lib/afl", dir); obj_path = dir2; ck_free(dir); return tmp; @@ -215,18 +215,18 @@ static u8 *find_object(u8 *obj, u8 *argv0) { if (!access(tmp, R_OK)) { - u8 *dir = alloc_printf("%s/../lib/", exepath); + u8 *dir = alloc_printf("%s", exepath); obj_path = dir; return tmp; } ck_free(tmp); - tmp = alloc_printf("%s/../lib/%s", exepath, obj); + tmp = alloc_printf("%s/../lib/afl/%s", exepath, obj); if (!access(tmp, R_OK)) { - u8 *dir = alloc_printf("%s/../lib/", exepath); + u8 *dir = alloc_printf("%s/../lib/afl/", exepath); obj_path = dir; return tmp; -- cgit 1.4.1 From a19b3022d93195d3703817c728817d7e071e89fe Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Thu, 3 Dec 2020 19:22:44 +0100 Subject: afl_custom_describe api added --- docs/Changelog.md | 10 ++++--- include/afl-fuzz.h | 14 +++++++++ src/afl-fuzz-bitmap.c | 63 +++++++++++++++++++++++++++++--------- src/afl-fuzz-mutators.c | 80 ++++++++++++++++++++++++++++++++++++++++--------- src/afl-fuzz-one.c | 9 ++++-- unicorn_mode/unicornafl | 2 +- 6 files changed, 143 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 02728f10..5201eb8b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -22,18 +22,18 @@ sending a mail to . a schedule performance score, which is much better that the previous walk the whole queue approach. Select the old mode with -Z (auto enabled with -M) - - rpc.statsd support by Edznux, thanks a lot! + - rpc.statsd support, for stats and charts, by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) - not specifying -M or -S will now auto-set "-S default" - reading testcases from -i now descends into subdirectories - - allow up to 4 times the -x command line option - - loaded extras now have a duplicate protection + - allow the -x command line option up to 4 times + - loaded extras now have a duplication protection - If test cases are too large we do a partial read on the maximum supported size - longer seeds with the same trace information will now be ignored for fuzzing but still be used for splicing - crashing seeds are now not prohibiting a run anymore but are - skipped. They are used for splicing though. + skipped - they are used for splicing, though - update MOpt for expanded havoc modes - setting the env var AFL_NO_AUTODICT will not load an LTO autodictionary - added NO_SPLICING compile option and makefile define @@ -42,6 +42,8 @@ sending a mail to . - print special compile time options used in help output - when using -c cmplog, one of the childs was not killed, fixed - somewhere we broke -n dumb fuzzing, fixed + - added afl_custom_describe to the custom mutator API to allow for easy + mutation reproduction on crashing inputs - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 62d76323..92465e7e 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -798,6 +798,20 @@ struct custom_mutator { size_t (*afl_custom_fuzz)(void *data, u8 *buf, size_t buf_size, u8 **out_buf, u8 *add_buf, size_t add_buf_size, size_t max_size); + /** + * Describe the current testcase, generated by the last mutation. + * This will be called, for example, to give the written testcase a name + * after a crash ocurred. It can help to reproduce crashing mutations. + * + * (Optional) + * + * @param data pointer returned in afl_custom_init for this fuzz case + * @param[in] max_size Maximum size of the mutated output. The mutation must + * not produce data larger than max_size. + * @return A valid ptr to a 0-terminated string, or NULL on error. + */ + const char *(*afl_custom_describe)(void *data, size_t max_size); + /** * A post-processing function to use right before AFL writes the test case to * disk in order to execute the target. diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 2d14b04e..a78bf374 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -425,7 +425,7 @@ void minimize_bits(afl_state_t *afl, u8 *dst, u8 *src) { /* Construct a file name for a new test case, capturing the operation that led to its discovery. Returns a ptr to afl->describe_op_buf_256. */ -u8 *describe_op(afl_state_t *afl, u8 hnb) { +u8 *describe_op(afl_state_t *afl, u8 new_bits) { u8 *ret = afl->describe_op_buf_256; @@ -445,29 +445,64 @@ u8 *describe_op(afl_state_t *afl, u8 hnb) { sprintf(ret + strlen(ret), ",time:%llu", get_cur_time() - afl->start_time); - sprintf(ret + strlen(ret), ",op:%s", afl->stage_short); + if (afl->current_custom_fuzz && + afl->current_custom_fuzz->afl_custom_describe) { - if (afl->stage_cur_byte >= 0) { + /* We are currently in a custom mutator that supports afl_custom_describe, + * use it! */ - sprintf(ret + strlen(ret), ",pos:%d", afl->stage_cur_byte); + size_t len_current = strlen(ret); + ret[len_current++] = ','; + ret[len_current++] = '\0'; - if (afl->stage_val_type != STAGE_VAL_NONE) { + size_t size_left = + sizeof(afl->describe_op_buf_256) - len_current - strlen(",+cov") - 2; + assert(size_left > 0); - sprintf(ret + strlen(ret), ",val:%s%+d", - (afl->stage_val_type == STAGE_VAL_BE) ? "be:" : "", - afl->stage_cur_val); + const char *custom_description = + afl->current_custom_fuzz->afl_custom_describe( + afl->current_custom_fuzz->data, size_left); + if (!custom_description || !custom_description[0]) { + + DEBUGF("Error getting a description from afl_custom_describe"); + /* Take the stage name as description fallback */ + sprintf(ret + len_current, "op:%s", afl->stage_short); + + } else { + + /* We got a proper custom description, use it */ + strncat(ret + len_current, custom_description, size_left); } } else { - sprintf(ret + strlen(ret), ",rep:%d", afl->stage_cur_val); + /* Normal testcase descriptions start here */ + sprintf(ret + strlen(ret), ",op:%s", afl->stage_short); + + if (afl->stage_cur_byte >= 0) { + + sprintf(ret + strlen(ret), ",pos:%d", afl->stage_cur_byte); + + if (afl->stage_val_type != STAGE_VAL_NONE) { + + sprintf(ret + strlen(ret), ",val:%s%+d", + (afl->stage_val_type == STAGE_VAL_BE) ? "be:" : "", + afl->stage_cur_val); + + } + + } else { + + sprintf(ret + strlen(ret), ",rep:%d", afl->stage_cur_val); + + } } } - if (hnb == 2) { strcat(ret, ",+cov"); } + if (new_bits == 2) { strcat(ret, ",+cov"); } return ret; @@ -540,7 +575,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (unlikely(len == 0)) { return 0; } u8 *queue_fn = ""; - u8 hnb = '\0'; + u8 new_bits = '\0'; s32 fd; u8 keeping = 0, res; u64 cksum = 0; @@ -566,7 +601,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { /* Keep only if there are new bits in the map, add to queue for future fuzzing, etc. */ - if (!(hnb = has_new_bits(afl, afl->virgin_bits))) { + if (!(new_bits = has_new_bits(afl, afl->virgin_bits))) { if (unlikely(afl->crash_mode)) { ++afl->total_crashes; } return 0; @@ -576,7 +611,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { #ifndef SIMPLE_FILES queue_fn = alloc_printf("%s/queue/id:%06u,%s", afl->out_dir, - afl->queued_paths, describe_op(afl, hnb)); + afl->queued_paths, describe_op(afl, new_bits)); #else @@ -619,7 +654,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { #endif - if (hnb == 2) { + if (new_bits == 2) { afl->queue_top->has_new_cov = 1; ++afl->queued_with_cov; diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index 1d14f657..0c85458e 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -151,7 +151,11 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { /* Mutator */ /* "afl_custom_init", optional for backward compatibility */ mutator->afl_custom_init = dlsym(dh, "afl_custom_init"); - if (!mutator->afl_custom_init) FATAL("Symbol 'afl_custom_init' not found."); + if (!mutator->afl_custom_init) { + + FATAL("Symbol 'afl_custom_init' not found."); + + } /* "afl_custom_fuzz" or "afl_custom_mutator", required */ mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_fuzz"); @@ -161,49 +165,74 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { WARNF("Symbol 'afl_custom_fuzz' not found. Try 'afl_custom_mutator'."); mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_mutator"); - if (!mutator->afl_custom_fuzz) + if (!mutator->afl_custom_fuzz) { + WARNF("Symbol 'afl_custom_mutator' not found."); + } + } /* "afl_custom_introspection", optional */ #ifdef INTROSPECTION mutator->afl_custom_introspection = dlsym(dh, "afl_custom_introspection"); - if (!mutator->afl_custom_introspection) + if (!mutator->afl_custom_introspection) { + ACTF("optional symbol 'afl_custom_introspection' not found."); + + } + #endif /* "afl_custom_fuzz_count", optional */ mutator->afl_custom_fuzz_count = dlsym(dh, "afl_custom_fuzz_count"); - if (!mutator->afl_custom_fuzz_count) + if (!mutator->afl_custom_fuzz_count) { + ACTF("optional symbol 'afl_custom_fuzz_count' not found."); + } + /* "afl_custom_deinit", optional for backward compatibility */ mutator->afl_custom_deinit = dlsym(dh, "afl_custom_deinit"); - if (!mutator->afl_custom_deinit) + if (!mutator->afl_custom_deinit) { + FATAL("Symbol 'afl_custom_deinit' not found."); + } + /* "afl_custom_post_process", optional */ mutator->afl_custom_post_process = dlsym(dh, "afl_custom_post_process"); - if (!mutator->afl_custom_post_process) + if (!mutator->afl_custom_post_process) { + ACTF("optional symbol 'afl_custom_post_process' not found."); + } + u8 notrim = 0; /* "afl_custom_init_trim", optional */ mutator->afl_custom_init_trim = dlsym(dh, "afl_custom_init_trim"); - if (!mutator->afl_custom_init_trim) + if (!mutator->afl_custom_init_trim) { + ACTF("optional symbol 'afl_custom_init_trim' not found."); + } + /* "afl_custom_trim", optional */ mutator->afl_custom_trim = dlsym(dh, "afl_custom_trim"); - if (!mutator->afl_custom_trim) + if (!mutator->afl_custom_trim) { + ACTF("optional symbol 'afl_custom_trim' not found."); + } + /* "afl_custom_post_trim", optional */ mutator->afl_custom_post_trim = dlsym(dh, "afl_custom_post_trim"); - if (!mutator->afl_custom_post_trim) + if (!mutator->afl_custom_post_trim) { + ACTF("optional symbol 'afl_custom_post_trim' not found."); + } + if (notrim) { mutator->afl_custom_init_trim = NULL; @@ -217,31 +246,54 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { /* "afl_custom_havoc_mutation", optional */ mutator->afl_custom_havoc_mutation = dlsym(dh, "afl_custom_havoc_mutation"); - if (!mutator->afl_custom_havoc_mutation) + if (!mutator->afl_custom_havoc_mutation) { + ACTF("optional symbol 'afl_custom_havoc_mutation' not found."); + } + /* "afl_custom_havoc_mutation", optional */ mutator->afl_custom_havoc_mutation_probability = dlsym(dh, "afl_custom_havoc_mutation_probability"); - if (!mutator->afl_custom_havoc_mutation_probability) + if (!mutator->afl_custom_havoc_mutation_probability) { + ACTF("optional symbol 'afl_custom_havoc_mutation_probability' not found."); + } + /* "afl_custom_queue_get", optional */ mutator->afl_custom_queue_get = dlsym(dh, "afl_custom_queue_get"); - if (!mutator->afl_custom_queue_get) + if (!mutator->afl_custom_queue_get) { + ACTF("optional symbol 'afl_custom_queue_get' not found."); + } + /* "afl_custom_queue_new_entry", optional */ mutator->afl_custom_queue_new_entry = dlsym(dh, "afl_custom_queue_new_entry"); - if (!mutator->afl_custom_queue_new_entry) + if (!mutator->afl_custom_queue_new_entry) { + ACTF("optional symbol 'afl_custom_queue_new_entry' not found"); + } + + /* "afl_custom_describe", optional */ + mutator->afl_custom_describe = dlsym(dh, "afl_custom_describe"); + if (!mutator->afl_custom_describe) { + + ACTF("Symbol 'afl_custom_describe' not found."); + + } + OKF("Custom mutator '%s' installed successfully.", fn); /* Initialize the custom mutator */ - if (mutator->afl_custom_init) + if (mutator->afl_custom_init) { + mutator->data = mutator->afl_custom_init(afl, rand_below(afl, 0xFFFFFFFF)); + } + mutator->stacked_custom = (mutator && mutator->afl_custom_havoc_mutation); mutator->stacked_custom_prob = 6; // like one of the default mutations in havoc diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 0adc3719..ca48f72a 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1790,11 +1790,16 @@ custom_mutator_stage: afl->current_custom_fuzz = el; - if (el->afl_custom_fuzz_count) + if (el->afl_custom_fuzz_count) { + afl->stage_max = el->afl_custom_fuzz_count(el->data, out_buf, len); - else + + } else { + afl->stage_max = saved_max; + } + has_custom_fuzz = true; afl->stage_short = el->name_short; diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl index f44ec48f..8cca4801 160000 --- a/unicorn_mode/unicornafl +++ b/unicorn_mode/unicornafl @@ -1 +1 @@ -Subproject commit f44ec48f8d5929f243522c1152b5b3c0985a5548 +Subproject commit 8cca4801adb767dce7cf72202d7d25bdb420cf7d -- cgit 1.4.1 From 1f34b9f8e185998e4c9c4b96b0c1878b6615115a Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 4 Dec 2020 05:28:36 +0100 Subject: added python mutator, documentation --- docs/custom_mutators.md | 11 +++++++++++ include/afl-fuzz.h | 36 +++++++++++++++++++----------------- src/afl-fuzz-python.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index 6e16ba0f..6d3c9f38 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -34,6 +34,7 @@ C/C++: void *afl_custom_init(afl_state_t *afl, unsigned int seed); unsigned int afl_custom_fuzz_count(void *data, const unsigned char *buf, size_t buf_size); size_t afl_custom_fuzz(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf, unsigned char *add_buf, size_t add_buf_size, size_t max_size); +const char *afl_custom_describe(void *data, size_t max_description_len); size_t afl_custom_post_process(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf); int afl_custom_init_trim(void *data, unsigned char *buf, size_t buf_size); size_t afl_custom_trim(void *data, unsigned char **out_buf); @@ -57,6 +58,9 @@ def fuzz_count(buf, add_buf, max_size): def fuzz(buf, add_buf, max_size): return mutated_out +def describe(max_description_length): + return "description_of_current_mutation" + def post_process(buf): return out_buf @@ -112,6 +116,13 @@ def introspection(): You would only skip this if `post_process` is used to fix checksums etc. so you are using it e.g. as a post processing library. +- `describe` (optional): + + When this function is called, is shall describe the current testcase, + generated by the last mutation. This will be called, for example, + to give the written testcase a name after a crash ocurred. + Using it can help to reproduce crashing mutations. + - `havoc_mutation` and `havoc_mutation_probability` (optional): `havoc_mutation` performs a single custom mutation on a given input. This diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 92465e7e..4efa1a6c 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -312,6 +312,7 @@ enum { /* 10 */ PY_FUNC_QUEUE_GET, /* 11 */ PY_FUNC_QUEUE_NEW_ENTRY, /* 12 */ PY_FUNC_INTROSPECTION, + /* 13 */ PY_FUNC_DESCRIBE, PY_FUNC_COUNT }; @@ -755,7 +756,7 @@ struct custom_mutator { * When afl-fuzz was compiled with INTROSPECTION=1 then custom mutators can * also give introspection information back with this function. * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @return pointer to a text string (const char*) */ const char *(*afl_custom_introspection)(void *data); @@ -771,7 +772,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param buf Buffer containing the test case * @param buf_size Size of the test case * @return The amount of fuzzes to perform on this queue entry, 0 = skip @@ -783,7 +784,7 @@ struct custom_mutator { * * (Optional for now. Required in the future) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param[in] buf Pointer to the input data to be mutated and the mutated * output * @param[in] buf_size Size of the input/output data @@ -805,12 +806,13 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case - * @param[in] max_size Maximum size of the mutated output. The mutation must - * not produce data larger than max_size. - * @return A valid ptr to a 0-terminated string, or NULL on error. + * @param data pointer returned by afl_customm_init for this custom mutator + * @paramp[in] max_description_len maximum size avaliable for the description. + * A longer return string is legal, but will be truncated. + * @return A valid ptr to a 0-terminated string. + * An empty or NULL return will result in a default description */ - const char *(*afl_custom_describe)(void *data, size_t max_size); + const char *(*afl_custom_describe)(void *data, size_t max_description_len); /** * A post-processing function to use right before AFL writes the test case to @@ -819,7 +821,7 @@ struct custom_mutator { * (Optional) If this functionality is not needed, simply don't define this * function. * - * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param[in] data pointer returned in afl_custom_init by this custom mutator * @param[in] buf Buffer containing the test case to be executed * @param[in] buf_size Size of the test case * @param[out] out_buf Pointer to the buffer storing the test case after @@ -846,7 +848,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param buf Buffer containing the test case * @param buf_size Size of the test case * @return The amount of possible iteration steps to trim the input. @@ -865,7 +867,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param[out] out_buf Pointer to the buffer containing the trimmed test case. * The library can reuse a buffer for each call * and will have to free the buf (for example in deinit) @@ -880,7 +882,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param success Indicates if the last trim operation was successful. * @return The next trim iteration index (from 0 to the maximum amount of * steps returned in init_trim). Negative on error. @@ -893,7 +895,7 @@ struct custom_mutator { * * (Optional) * - * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param[in] data pointer returned in afl_custom_init by this custom mutator * @param[in] buf Pointer to the input data to be mutated and the mutated * output * @param[in] buf_size Size of input data @@ -912,7 +914,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @return The probability (0-100). */ u8 (*afl_custom_havoc_mutation_probability)(void *data); @@ -922,7 +924,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param filename File name of the test case in the queue entry * @return Return True(1) if the fuzzer will fuzz the queue entry, and * False(0) otherwise. @@ -935,7 +937,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param filename_new_queue File name of the new queue entry * @param filename_orig_queue File name of the original queue entry. This * argument can be NULL while initializing the fuzzer @@ -945,7 +947,7 @@ struct custom_mutator { /** * Deinitialize the custom mutator. * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator */ void (*afl_custom_deinit)(void *data); diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index 9ac4403b..8760194c 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -111,6 +111,37 @@ static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, } +static const char *custom_describe_py(void * py_mutator, + size_t max_description_len) { + + PyObject *py_args, *py_value; + + py_args = PyTuple_New(1); + + PyLong_FromSize_t(max_description_len); + + /* add_buf */ + py_value = PyLong_FromSize_t(max_description_len); + if (!py_value) { + + Py_DECREF(py_args); + FATAL("Failed to convert arguments"); + + } + + PyTuple_SetItem(py_args, 0, py_value); + + py_value = PyObject_CallObject( + ((py_mutator_t *)py_mutator)->py_functions[PY_FUNC_DESCRIBE], py_args); + + Py_DECREF(py_args); + + if (py_value != NULL) { return PyBytes_AsString(py_value); } + + return NULL; + +} + static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { (void)afl; @@ -156,6 +187,8 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "fuzz"); if (!py_functions[PY_FUNC_FUZZ]) py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "mutate"); + py_functions[PY_FUNC_DESCRIBE] = + PyObject_GetAttrString(py_module, "describe"); py_functions[PY_FUNC_FUZZ_COUNT] = PyObject_GetAttrString(py_module, "fuzz_count"); if (!py_functions[PY_FUNC_FUZZ]) @@ -342,6 +375,12 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, if (py_functions[PY_FUNC_FUZZ]) { mutator->afl_custom_fuzz = fuzz_py; } + if (py_functions[PY_FUNC_DESCRIBE]) { + + mutator->afl_custom_describe = custom_describe_py; + + } + if (py_functions[PY_FUNC_POST_PROCESS]) { mutator->afl_custom_post_process = post_process_py; -- cgit 1.4.1 From 1dbefc14eae4f7a189851785aa3f0982af4236f2 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 4 Dec 2020 14:25:18 +0100 Subject: fixed bugs in custom_describe, reported by wizche --- include/afl-fuzz.h | 2 +- src/afl-fuzz-bitmap.c | 21 +++++++++++++-------- src/afl-fuzz-run.c | 3 ++- 3 files changed, 16 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 4efa1a6c..bdf44def 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1023,7 +1023,7 @@ void classify_counts(afl_forkserver_t *); void init_count_class16(void); void minimize_bits(afl_state_t *, u8 *, u8 *); #ifndef SIMPLE_FILES -u8 *describe_op(afl_state_t *, u8); +u8 *describe_op(afl_state_t *, u8, size_t); #endif u8 save_if_interesting(afl_state_t *, void *, u32, u8); u8 has_new_bits(afl_state_t *, u8 *); diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index a78bf374..f920efa4 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -425,8 +425,10 @@ void minimize_bits(afl_state_t *afl, u8 *dst, u8 *src) { /* Construct a file name for a new test case, capturing the operation that led to its discovery. Returns a ptr to afl->describe_op_buf_256. */ -u8 *describe_op(afl_state_t *afl, u8 new_bits) { +u8 *describe_op(afl_state_t *afl, u8 new_bits, size_t max_description_len) { + size_t real_max_len = + MIN(max_description_len, sizeof(afl->describe_op_buf_256)); u8 *ret = afl->describe_op_buf_256; if (unlikely(afl->syncing_party)) { @@ -453,10 +455,9 @@ u8 *describe_op(afl_state_t *afl, u8 new_bits) { size_t len_current = strlen(ret); ret[len_current++] = ','; - ret[len_current++] = '\0'; + ret[len_current] = '\0'; - size_t size_left = - sizeof(afl->describe_op_buf_256) - len_current - strlen(",+cov") - 2; + size_t size_left = real_max_len - len_current - strlen(",+cov") - 2; assert(size_left > 0); const char *custom_description = @@ -504,6 +505,8 @@ u8 *describe_op(afl_state_t *afl, u8 new_bits) { if (new_bits == 2) { strcat(ret, ",+cov"); } + assert(strlen(ret) <= max_description_len); + return ret; } @@ -610,8 +613,9 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { #ifndef SIMPLE_FILES - queue_fn = alloc_printf("%s/queue/id:%06u,%s", afl->out_dir, - afl->queued_paths, describe_op(afl, new_bits)); + queue_fn = alloc_printf( + "%s/queue/id:%06u,%s", afl->out_dir, afl->queued_paths, + describe_op(afl, new_bits, NAME_MAX - strlen("id:000000,"))); #else @@ -777,7 +781,8 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { #ifndef SIMPLE_FILES snprintf(fn, PATH_MAX, "%s/hangs/id:%06llu,%s", afl->out_dir, - afl->unique_hangs, describe_op(afl, 0)); + afl->unique_hangs, + describe_op(afl, 0, NAME_MAX - strlen("id:000000,"))); #else @@ -822,7 +827,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { snprintf(fn, PATH_MAX, "%s/crashes/id:%06llu,sig:%02u,%s", afl->out_dir, afl->unique_crashes, afl->fsrv.last_kill_signal, - describe_op(afl, 0)); + describe_op(afl, 0, NAME_MAX - strlen("id:000000,sig:00,"))); #else diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index b716b8c8..5948d83a 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -79,7 +79,8 @@ write_to_testcase(afl_state_t *afl, void *mem, u32 len) { s32 doc_fd; char fn[PATH_MAX]; snprintf(fn, PATH_MAX, "%s/mutations/%09u:%s", afl->out_dir, - afl->document_counter++, describe_op(afl, 0)); + afl->document_counter++, + describe_op(afl, 0, NAME_MAX - strlen("000000000:"))); if ((doc_fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600)) >= 0) { -- cgit 1.4.1 From f8c33f29e8cf7de30604be2c6818dd8d2d0ad3a6 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 5 Dec 2020 09:19:14 +0100 Subject: Typos --- src/afl-cc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index cc4f44f5..fcc10122 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -113,15 +113,15 @@ u8 *getthecwd() { 1. if obj_path is already set we look there first 2. then we check the $AFL_PATH environment variable location if set - 3. next we check argv[0] if has path information and use it + 3. next we check argv[0] if it has path information and use it a) we also check ../lib/afl 4. if 3. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and - FreeBSD with procfs + FreeBSD with procfs) a) and check here in ../lib/afl too 5. we look into the AFL_PATH define (usually /usr/local/lib/afl) 6. we finally try the current directory - if this all fail - we fail. + if all this fails - we fail. */ static u8 *find_object(u8 *obj, u8 *argv0) { -- cgit 1.4.1 From 8f79116a15e53f0ecd4889ebf8980cc0cd85a494 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 5 Dec 2020 09:48:55 +0100 Subject: fix find_object proc search (#elsif -> #elif), optimize static if away --- src/afl-cc.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index fcc10122..1cd53f98 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -189,14 +189,25 @@ static u8 *find_object(u8 *obj, u8 *argv0) { ck_free(tmp); ck_free(dir); - } else { + } + +#if \ + defined(__FreeBSD__) \ +|| defined(__DragonFly__) \ +|| defined(__linux__) \ +|| defined(__ANDROID__) \ +|| defined(__NetBSD__) +#define HAS_PROC_FS 1 +#endif +#ifdef HAS_PROC_FS + else { char *procname = NULL; #if defined(__FreeBSD__) || defined(__DragonFly__) procname = "/proc/curproc/file"; - #elsif defined(__linux__) || defined(__ANDROID__) + #elif defined(__linux__) || defined(__ANDROID__) procname = "/proc/self/exe"; - #elsif defined(__NetBSD__) + #elif defined(__NetBSD__) procname = "/proc/curproc/exe"; #endif if (procname) { @@ -239,6 +250,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { } } +#endif +#undef HAS_PROC_FS } -- cgit 1.4.1 From 4c2e375e22a0b3c88380e6dd51dbac5d2c5ac5ff Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 7 Dec 2020 14:29:59 +0100 Subject: little fixes --- README.md | 2 +- src/afl-cc.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/README.md b/README.md index e2cb04f7..94d5008e 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ behaviours and defaults: * a caching of testcases can now be performed and can be modified by editing config.h for TESTCASE_CACHE or by specifying the env variable `AFL_TESTCACHE_SIZE` (in MB). Good values are between 50-500 (default: 50). - * utils/ got renamed to utils/ + * examples/ got renamed to utils/ ## Contents diff --git a/src/afl-cc.c b/src/afl-cc.c index 1cd53f98..50334139 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -121,7 +121,8 @@ u8 *getthecwd() { 5. we look into the AFL_PATH define (usually /usr/local/lib/afl) 6. we finally try the current directory - if all this fails - we fail. + if all these attempts fail - we return NULL and the caller has to decide + what to do. */ static u8 *find_object(u8 *obj, u8 *argv0) { -- cgit 1.4.1 From e6de85861c4ec8fcc13a7f945bc8b3dfefa193bc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 7 Dec 2020 14:36:04 +0100 Subject: fixes and code format --- instrumentation/README.lto.md | 4 ++-- src/afl-cc.c | 15 ++++++--------- 2 files changed, 8 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/instrumentation/README.lto.md b/instrumentation/README.lto.md index 37d51de8..a2814173 100644 --- a/instrumentation/README.lto.md +++ b/instrumentation/README.lto.md @@ -80,8 +80,8 @@ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - apt-get update && apt-get upgrade -y apt-get install -y clang-12 clang-tools-12 libc++1-12 libc++-12-dev \ libc++abi1-12 libc++abi-12-dev libclang1-12 libclang-12-dev \ - libclang-common-12-dev libclang-cpp11 libclang-cpp11-dev liblld-12 \ - liblld-12-dev liblldb-12 liblldb-12-dev libllvm11 libomp-12-dev \ + libclang-common-12-dev libclang-cpp12 libclang-cpp12-dev liblld-12 \ + liblld-12-dev liblldb-12 liblldb-12-dev libllvm12 libomp-12-dev \ libomp5-12 lld-12 lldb-12 llvm-12 llvm-12-dev llvm-12-runtime llvm-12-tools ``` diff --git a/src/afl-cc.c b/src/afl-cc.c index 50334139..273a9f2f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -192,25 +192,21 @@ static u8 *find_object(u8 *obj, u8 *argv0) { } -#if \ - defined(__FreeBSD__) \ -|| defined(__DragonFly__) \ -|| defined(__linux__) \ -|| defined(__ANDROID__) \ -|| defined(__NetBSD__) -#define HAS_PROC_FS 1 +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__linux__) || \ + defined(__ANDROID__) || defined(__NetBSD__) + #define HAS_PROC_FS 1 #endif #ifdef HAS_PROC_FS else { char *procname = NULL; -#if defined(__FreeBSD__) || defined(__DragonFly__) + #if defined(__FreeBSD__) || defined(__DragonFly__) procname = "/proc/curproc/file"; #elif defined(__linux__) || defined(__ANDROID__) procname = "/proc/self/exe"; #elif defined(__NetBSD__) procname = "/proc/curproc/exe"; -#endif + #endif if (procname) { char exepath[PATH_MAX]; @@ -251,6 +247,7 @@ static u8 *find_object(u8 *obj, u8 *argv0) { } } + #endif #undef HAS_PROC_FS -- cgit 1.4.1 From 06ec5ab3d723bf7f0a2ee76be8b12c09fa870a9d Mon Sep 17 00:00:00 2001 From: Marcel Boehme Date: Mon, 7 Dec 2020 21:32:25 +0000 Subject: Sampling next seed by weight (hit_count, bitmap_size, exec_us) --- include/afl-fuzz.h | 3 ++- src/afl-fuzz-one.c | 6 ++++-- src/afl-fuzz-queue.c | 39 ++++++++++++++++++++++++++++++++------- 3 files changed, 38 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index bdf44def..6ce032df 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -168,7 +168,8 @@ struct queue_entry { u8 *trace_mini; /* Trace bytes, if kept */ u32 tc_ref; /* Trace bytes ref count */ - double perf_score; /* performance score */ + double perf_score, /* performance score */ + weight; u8 *testcase_buf; /* The testcase buffer, if loaded. */ diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index ca48f72a..a48afffb 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -445,8 +445,10 @@ u8 fuzz_one_original(afl_state_t *afl) { if (unlikely(afl->not_on_tty)) { - ACTF("Fuzzing test case #%u (%u total, %llu uniq crashes found)...", - afl->current_entry, afl->queued_paths, afl->unique_crashes); + ACTF("Fuzzing test case #%u (%u total, %llu uniq crashes found, perf_score=%0.0f, exec_us=%llu, hits=%u, map=%u)...", + afl->current_entry, afl->queued_paths, afl->unique_crashes, + afl->queue_cur->perf_score, afl->queue_cur->exec_us, + afl->n_fuzz[afl->queue_cur->n_fuzz_entry], afl->queue_cur->bitmap_size); fflush(stdout); } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index f35b4f57..1e997c55 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -42,6 +42,21 @@ inline u32 select_next_queue_entry(afl_state_t *afl) { } +double compute_weight(afl_state_t *afl, struct queue_entry *q, double avg_exec_us, double avg_bitmap_size) { + + u32 hits = afl->n_fuzz[q->n_fuzz_entry]; + if (hits == 0) hits = 1; + + double weight = 1.0; + weight *= avg_exec_us / q->exec_us; + weight *= log(q->bitmap_size) / avg_bitmap_size; + weight /= log10(hits) + 1; + + if (q->favored) weight *= 5; + + return weight; +} + /* create the alias table that allows weighted random selection - expensive */ void create_alias_table(afl_state_t *afl) { @@ -65,25 +80,35 @@ void create_alias_table(afl_state_t *afl) { memset((void *)afl->alias_table, 0, n * sizeof(u32)); memset((void *)afl->alias_probability, 0, n * sizeof(double)); - double sum = 0; - + double avg_exec_us = 0.0; + double avg_bitmap_size = 0.0; for (i = 0; i < n; i++) { struct queue_entry *q = afl->queue_buf[i]; - - if (!q->disabled) { q->perf_score = calculate_score(afl, q); } - - sum += q->perf_score; + avg_exec_us += q->exec_us; + avg_bitmap_size += log(q->bitmap_size); } + avg_exec_us /= afl->queued_paths; + avg_bitmap_size /= afl->queued_paths; + double sum = 0; for (i = 0; i < n; i++) { struct queue_entry *q = afl->queue_buf[i]; - P[i] = (q->perf_score * n) / sum; + + if (!q->disabled) { + q->weight = compute_weight(afl, q, avg_exec_us, avg_bitmap_size); + q->perf_score = calculate_score(afl, q); + } + + sum += q->weight; } + for (i = 0; i < n; i++) + P[i] = (afl->queue_buf[i]->weight * n) / sum; + int nS = 0, nL = 0, s; for (s = (s32)n - 1; s >= 0; --s) { -- cgit 1.4.1 From 46156957bd120dc8d8bcd9da72f83574902c654f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 8 Dec 2020 11:07:11 +0100 Subject: fix aflfast changes --- include/afl-fuzz.h | 2 +- src/afl-fuzz-one.c | 11 +++-- src/afl-fuzz-queue.c | 73 ++++++++++++++++++++++++---------- utils/aflpp_driver/aflpp_qemu_driver.c | 2 +- 4 files changed, 61 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 6ce032df..2f2d31d3 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -169,7 +169,7 @@ struct queue_entry { u32 tc_ref; /* Trace bytes ref count */ double perf_score, /* performance score */ - weight; + weight; u8 *testcase_buf; /* The testcase buffer, if loaded. */ diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index a48afffb..e6fa6064 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -445,10 +445,13 @@ u8 fuzz_one_original(afl_state_t *afl) { if (unlikely(afl->not_on_tty)) { - ACTF("Fuzzing test case #%u (%u total, %llu uniq crashes found, perf_score=%0.0f, exec_us=%llu, hits=%u, map=%u)...", - afl->current_entry, afl->queued_paths, afl->unique_crashes, - afl->queue_cur->perf_score, afl->queue_cur->exec_us, - afl->n_fuzz[afl->queue_cur->n_fuzz_entry], afl->queue_cur->bitmap_size); + ACTF( + "Fuzzing test case #%u (%u total, %llu uniq crashes found, " + "perf_score=%0.0f, exec_us=%llu, hits=%u, map=%u)...", + afl->current_entry, afl->queued_paths, afl->unique_crashes, + afl->queue_cur->perf_score, afl->queue_cur->exec_us, + likely(afl->n_fuzz) ? afl->n_fuzz[afl->queue_cur->n_fuzz_entry] : 0, + afl->queue_cur->bitmap_size); fflush(stdout); } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 1e997c55..071e4a4c 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -42,7 +42,8 @@ inline u32 select_next_queue_entry(afl_state_t *afl) { } -double compute_weight(afl_state_t *afl, struct queue_entry *q, double avg_exec_us, double avg_bitmap_size) { +double compute_weight(afl_state_t *afl, struct queue_entry *q, + double avg_exec_us, double avg_bitmap_size) { u32 hits = afl->n_fuzz[q->n_fuzz_entry]; if (hits == 0) hits = 1; @@ -55,13 +56,15 @@ double compute_weight(afl_state_t *afl, struct queue_entry *q, double avg_exec_u if (q->favored) weight *= 5; return weight; + } /* create the alias table that allows weighted random selection - expensive */ void create_alias_table(afl_state_t *afl) { - u32 n = afl->queued_paths, i = 0, a, g; + u32 n = afl->queued_paths, i = 0, a, g; + double sum = 0; afl->alias_table = (u32 *)afl_realloc((void **)&afl->alias_table, n * sizeof(u32)); @@ -80,34 +83,62 @@ void create_alias_table(afl_state_t *afl) { memset((void *)afl->alias_table, 0, n * sizeof(u32)); memset((void *)afl->alias_probability, 0, n * sizeof(double)); - double avg_exec_us = 0.0; - double avg_bitmap_size = 0.0; - for (i = 0; i < n; i++) { + if (likely(afl->schedule >= FAST && afl->schedule <= RARE)) { - struct queue_entry *q = afl->queue_buf[i]; - avg_exec_us += q->exec_us; - avg_bitmap_size += log(q->bitmap_size); + double avg_exec_us = 0.0; + double avg_bitmap_size = 0.0; + for (i = 0; i < n; i++) { - } - avg_exec_us /= afl->queued_paths; - avg_bitmap_size /= afl->queued_paths; + struct queue_entry *q = afl->queue_buf[i]; + avg_exec_us += q->exec_us; + avg_bitmap_size += log(q->bitmap_size); - double sum = 0; - for (i = 0; i < n; i++) { + } + + avg_exec_us /= afl->queued_paths; + avg_bitmap_size /= afl->queued_paths; + + for (i = 0; i < n; i++) { - struct queue_entry *q = afl->queue_buf[i]; + struct queue_entry *q = afl->queue_buf[i]; + + if (!q->disabled) { + + q->weight = compute_weight(afl, q, avg_exec_us, avg_bitmap_size); + q->perf_score = calculate_score(afl, q); + + } + + sum += q->weight; - if (!q->disabled) { - q->weight = compute_weight(afl, q, avg_exec_us, avg_bitmap_size); - q->perf_score = calculate_score(afl, q); } - sum += q->weight; + for (i = 0; i < n; i++) { - } + P[i] = (afl->queue_buf[i]->weight * n) / sum; + + } + + } else { + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + + if (!q->disabled) { q->perf_score = calculate_score(afl, q); } + + sum += q->perf_score; - for (i = 0; i < n; i++) - P[i] = (afl->queue_buf[i]->weight * n) / sum; + } + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + P[i] = (q->perf_score * n) / sum; + + } + + } int nS = 0, nL = 0, s; for (s = (s32)n - 1; s >= 0; --s) { diff --git a/utils/aflpp_driver/aflpp_qemu_driver.c b/utils/aflpp_driver/aflpp_qemu_driver.c index cb3b86d0..a0c02833 100644 --- a/utils/aflpp_driver/aflpp_qemu_driver.c +++ b/utils/aflpp_driver/aflpp_qemu_driver.c @@ -7,7 +7,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); __attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); #define kMaxAflInputSize (1 * 1024 * 1024); -static uint8_t AflInputBuf[kMaxAflInputSize]; +static uint8_t AflInputBuf[kMaxAflInputSize]; void __attribute__((noinline)) afl_qemu_driver_stdin_input(void) { -- cgit 1.4.1 From da6cddab904e363775f157ceafa932f3cdaf6121 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 8 Dec 2020 11:30:05 +0100 Subject: fix asserts --- src/afl-fuzz-bitmap.c | 7 ++++--- utils/aflpp_driver/aflpp_qemu_driver.c | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index f920efa4..f1ca7400 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -457,8 +457,8 @@ u8 *describe_op(afl_state_t *afl, u8 new_bits, size_t max_description_len) { ret[len_current++] = ','; ret[len_current] = '\0'; - size_t size_left = real_max_len - len_current - strlen(",+cov") - 2; - assert(size_left > 0); + ssize_t size_left = real_max_len - len_current - strlen(",+cov") - 2; + if (unlikely(size_left <= 0)) FATAL("filename got too long"); const char *custom_description = afl->current_custom_fuzz->afl_custom_describe( @@ -505,7 +505,8 @@ u8 *describe_op(afl_state_t *afl, u8 new_bits, size_t max_description_len) { if (new_bits == 2) { strcat(ret, ",+cov"); } - assert(strlen(ret) <= max_description_len); + if (unlikely(strlen(ret) >= max_description_len)) + FATAL("describe string is too long"); return ret; diff --git a/utils/aflpp_driver/aflpp_qemu_driver.c b/utils/aflpp_driver/aflpp_qemu_driver.c index a0c02833..79de5af6 100644 --- a/utils/aflpp_driver/aflpp_qemu_driver.c +++ b/utils/aflpp_driver/aflpp_qemu_driver.c @@ -6,7 +6,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); __attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); -#define kMaxAflInputSize (1 * 1024 * 1024); +#define kMaxAflInputSize (1 * 1024 * 1024) static uint8_t AflInputBuf[kMaxAflInputSize]; void __attribute__((noinline)) afl_qemu_driver_stdin_input(void) { -- cgit 1.4.1 From 6e61b2345cc35f101bac7594089dc57999f33b89 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 8 Dec 2020 20:33:41 +0100 Subject: more reporting on errors --- instrumentation/afl-compiler-rt.o.c | 25 +++++++++++++++++++++++-- src/afl-forkserver.c | 7 +++++++ utils/persistent_mode/persistent_demo_new.c | 2 +- 3 files changed, 31 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index b07aeb83..e29c4483 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -163,6 +163,12 @@ static void __afl_map_shm_fuzz() { char *id_str = getenv(SHM_FUZZ_ENV_VAR); + if (getenv("AFL_DEBUG")) { + + fprintf(stderr, "DEBUG: fuzzcase shmem %s\n", id_str ? id_str : "none"); + + } + if (id_str) { u8 *map = NULL; @@ -196,6 +202,7 @@ static void __afl_map_shm_fuzz() { if (!map || map == (void *)-1) { perror("Could not access fuzzing shared memory"); + send_forkserver_error(FS_ERROR_SHM_OPEN); exit(1); } @@ -212,6 +219,7 @@ static void __afl_map_shm_fuzz() { } else { fprintf(stderr, "Error: variable for fuzzing shared memory is not set\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); exit(1); } @@ -335,6 +343,8 @@ static void __afl_map_shm(void) { send_forkserver_error(FS_ERROR_MAP_ADDR); else send_forkserver_error(FS_ERROR_MMAP); + perror("shmat for map"); + exit(2); } @@ -349,12 +359,14 @@ static void __afl_map_shm(void) { /* Whooooops. */ - if (__afl_area_ptr == (void *)-1) { + if (!__afl_area_ptr || __afl_area_ptr == (void *)-1) { if (__afl_map_addr) send_forkserver_error(FS_ERROR_MAP_ADDR); else send_forkserver_error(FS_ERROR_SHMAT); + + perror("shmat for map"); _exit(1); } @@ -376,6 +388,7 @@ static void __afl_map_shm(void) { fprintf(stderr, "can not acquire mmap for address %p\n", (void *)__afl_map_addr); + send_forkserver_error(FS_ERROR_SHM_OPEN); exit(1); } @@ -411,6 +424,7 @@ static void __afl_map_shm(void) { if (shm_fd == -1) { fprintf(stderr, "shm_open() failed\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); exit(1); } @@ -424,6 +438,7 @@ static void __afl_map_shm(void) { shm_fd = -1; fprintf(stderr, "mmap() failed\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); exit(2); } @@ -435,7 +450,13 @@ static void __afl_map_shm(void) { __afl_cmp_map = shmat(shm_id, NULL, 0); #endif - if (__afl_cmp_map == (void *)-1) _exit(1); + if (!__afl_cmp_map || __afl_cmp_map == (void *)-1) { + + perror("shmat for cmplog"); + send_forkserver_error(FS_ERROR_SHM_OPEN); + _exit(1); + + } } diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 20117c1d..b1c29ba6 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -1069,6 +1069,13 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, if (fsrv->child_pid <= 0) { if (*stop_soon_p) { return 0; } + + if ((fsrv->child_pid & FS_OPT_ERROR) && + FS_OPT_GET_ERROR(fsrv->child_pid) == FS_ERROR_SHM_OPEN) + FATAL( + "Target reported shared memory access failed (perhaps increase " + "shared memory available)."); + FATAL("Fork server is misbehaving (OOM?)"); } diff --git a/utils/persistent_mode/persistent_demo_new.c b/utils/persistent_mode/persistent_demo_new.c index a29792ff..0d24a51e 100644 --- a/utils/persistent_mode/persistent_demo_new.c +++ b/utils/persistent_mode/persistent_demo_new.c @@ -86,7 +86,7 @@ int main(int argc, char **argv) { if (buf[4] == '!') { printf("five\n"); - if (buf[6] == '!') { + if (buf[5] == '!') { printf("six\n"); abort(); -- cgit 1.4.1 From 5bb51688e4187629b500842a14b9740d12d9829a Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 8 Dec 2020 21:54:18 +0100 Subject: small fixes --- src/afl-forkserver.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index b1c29ba6..38cd529f 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -62,6 +62,7 @@ static void fsrv_exec_child(afl_forkserver_t *fsrv, char **argv) { execv(fsrv->target_path, argv); + WARNF("Execv failed in forkserver."); } /* Initializes the struct */ @@ -526,8 +527,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, falling through. */ *(u32 *)fsrv->trace_bits = EXEC_FAIL_SIG; - fprintf(stderr, "Error: execv to target failed\n"); - exit(1); + FATAL("Error: execv to target failed\n"); } @@ -916,7 +916,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, " estimate the required amount of virtual memory for the " "binary.\n\n" - " - the target was compiled with afl-clang-lto and a constructor " + " - The target was compiled with afl-clang-lto and a constructor " "was\n" " instrumented, recompiling without AFL_LLVM_MAP_ADDR might solve " "your \n" -- cgit 1.4.1 From c70b7ffd80ee95cdf3bf1276bfbd4a590e74d3f1 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 8 Dec 2020 22:42:50 +0100 Subject: fix memory limit issue with cmplog without -m flag --- src/afl-fuzz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 269ce1bf..8a66018d 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -867,7 +867,7 @@ int main(int argc, char **argv_orig, char **envp) { } - if (!mem_limit_given && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260; + if (afl->fsrv.mem_limit && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260; OKF("afl++ is maintained by Marc \"van Hauser\" Heuse, Heiko \"hexcoder\" " "Eißfeldt, Andrea Fioraldi and Dominik Maier"); -- cgit 1.4.1 From 4e96447b43970acf8e43140aebdbccbe49dfd688 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 8 Dec 2020 23:26:08 +0100 Subject: fix showmap output --- src/afl-forkserver.c | 1 + src/afl-showmap.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 38cd529f..5a8e56b2 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -63,6 +63,7 @@ static void fsrv_exec_child(afl_forkserver_t *fsrv, char **argv) { execv(fsrv->target_path, argv); WARNF("Execv failed in forkserver."); + } /* Initializes the struct */ diff --git a/src/afl-showmap.c b/src/afl-showmap.c index e07e76c8..34a4f30d 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -287,6 +287,8 @@ static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem, afl_fsrv_write_to_testcase(fsrv, mem, len); + if (!quiet_mode) { SAYF("-- Program output begins --\n" cRST); } + if (afl_fsrv_run_target(fsrv, fsrv->exec_tmout, &stop_soon) == FSRV_RUN_ERROR) { @@ -711,6 +713,7 @@ int main(int argc, char **argv_orig, char **envp) { case 'C': collect_coverage = 1; + quiet_mode = 1; break; case 'i': @@ -817,7 +820,6 @@ int main(int argc, char **argv_orig, char **envp) { case 'q': - if (quiet_mode) { FATAL("Multiple -q options not supported"); } quiet_mode = 1; break; @@ -1189,7 +1191,7 @@ int main(int argc, char **argv_orig, char **envp) { } - if (!quiet_mode) { + if (!quiet_mode || collect_coverage) { if (!tcnt) { FATAL("No instrumentation detected" cRST); } OKF("Captured %u tuples (highest value %u, total values %llu) in " -- cgit 1.4.1 From a7125c68eb49d35de8aee6926cc0e9a9d839265f Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 8 Dec 2020 23:51:22 +0100 Subject: optimize a bit --- src/afl-sharedmem.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/afl-sharedmem.c b/src/afl-sharedmem.c index 6eb63949..cef908e0 100644 --- a/src/afl-sharedmem.c +++ b/src/afl-sharedmem.c @@ -248,22 +248,26 @@ u8 *afl_shm_init(sharedmem_t *shm, size_t map_size, } - shm_str = alloc_printf("%d", shm->shm_id); + if (!non_instrumented_mode) { - /* If somebody is asking us to fuzz instrumented binaries in non-instrumented - mode, we don't want them to detect instrumentation, since we won't be - sending fork server commands. This should be replaced with better - auto-detection later on, perhaps? */ + shm_str = alloc_printf("%d", shm->shm_id); - if (!non_instrumented_mode) { setenv(SHM_ENV_VAR, shm_str, 1); } + /* If somebody is asking us to fuzz instrumented binaries in non-instrumented + mode, we don't want them to detect instrumentation, since we won't be + sending fork server commands. This should be replaced with better + auto-detection later on, perhaps? */ - ck_free(shm_str); + setenv(SHM_ENV_VAR, shm_str, 1); - if (shm->cmplog_mode) { + ck_free(shm_str); + + } + + if (shm->cmplog_mode && !non_instrumented_mode) { shm_str = alloc_printf("%d", shm->cmplog_shm_id); - if (!non_instrumented_mode) { setenv(CMPLOG_SHM_ENV_VAR, shm_str, 1); } + setenv(CMPLOG_SHM_ENV_VAR, shm_str, 1); ck_free(shm_str); @@ -274,6 +278,7 @@ u8 *afl_shm_init(sharedmem_t *shm, size_t map_size, if (shm->map == (void *)-1 || !shm->map) { shmctl(shm->shm_id, IPC_RMID, NULL); // do not leak shmem + if (shm->cmplog_mode) { shmctl(shm->cmplog_shm_id, IPC_RMID, NULL); // do not leak shmem @@ -291,11 +296,8 @@ u8 *afl_shm_init(sharedmem_t *shm, size_t map_size, if (shm->cmp_map == (void *)-1 || !shm->cmp_map) { shmctl(shm->shm_id, IPC_RMID, NULL); // do not leak shmem - if (shm->cmplog_mode) { - - shmctl(shm->cmplog_shm_id, IPC_RMID, NULL); // do not leak shmem - } + shmctl(shm->cmplog_shm_id, IPC_RMID, NULL); // do not leak shmem PFATAL("shmat() failed"); -- cgit 1.4.1 From d1eb4eeb7fa3d94749dee4e8d625d242dbda1c1f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 9 Dec 2020 08:34:54 +0100 Subject: weighting for explore and exploit --- src/afl-fuzz-queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 071e4a4c..28d560d1 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -83,7 +83,7 @@ void create_alias_table(afl_state_t *afl) { memset((void *)afl->alias_table, 0, n * sizeof(u32)); memset((void *)afl->alias_probability, 0, n * sizeof(double)); - if (likely(afl->schedule >= FAST && afl->schedule <= RARE)) { + if (likely(afl->schedule < RARE)) { double avg_exec_us = 0.0; double avg_bitmap_size = 0.0; -- cgit 1.4.1 From cebbedd238501d7f4326b0494cd60208a92e1645 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 9 Dec 2020 08:58:07 +0100 Subject: fix cmin/tmin potential overflow on too large files --- src/afl-forkserver.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 5a8e56b2..3afb94be 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -960,6 +960,8 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { if (fsrv->shmem_fuzz) { + if (unlikely(len > MAX_FILE)) len = MAX_FILE; + *fsrv->shmem_fuzz_len = len; memcpy(fsrv->shmem_fuzz, buf, len); #ifdef _DEBUG -- cgit 1.4.1 From 39a4fac941177387578ec856aacea2187588fc13 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 9 Dec 2020 11:07:14 +0100 Subject: better examples --- instrumentation/afl-compiler-rt.o.c | 8 ++++---- src/afl-sharedmem.c | 8 ++++---- utils/persistent_mode/persistent_demo.c | 8 +++++++- utils/persistent_mode/persistent_demo_new.c | 8 +++++++- utils/persistent_mode/test-instr.c | 8 +++++++- 5 files changed, 29 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index c29861e6..99dcbb67 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -174,8 +174,8 @@ static void __afl_map_shm_fuzz() { u8 *map = NULL; #ifdef USEMMAP - const char * shm_file_path = id_str; - int shm_fd = -1; + const char *shm_file_path = id_str; + int shm_fd = -1; /* create the shared memory segment as if it was a file */ shm_fd = shm_open(shm_file_path, O_RDWR, 0600); @@ -414,8 +414,8 @@ static void __afl_map_shm(void) { if (id_str) { #ifdef USEMMAP - const char * shm_file_path = id_str; - int shm_fd = -1; + const char * shm_file_path = id_str; + int shm_fd = -1; struct cmp_map *shm_base = NULL; /* create the shared memory segment as if it was a file */ diff --git a/src/afl-sharedmem.c b/src/afl-sharedmem.c index cef908e0..3e671df5 100644 --- a/src/afl-sharedmem.c +++ b/src/afl-sharedmem.c @@ -252,10 +252,10 @@ u8 *afl_shm_init(sharedmem_t *shm, size_t map_size, shm_str = alloc_printf("%d", shm->shm_id); - /* If somebody is asking us to fuzz instrumented binaries in non-instrumented - mode, we don't want them to detect instrumentation, since we won't be - sending fork server commands. This should be replaced with better - auto-detection later on, perhaps? */ + /* If somebody is asking us to fuzz instrumented binaries in + non-instrumented mode, we don't want them to detect instrumentation, + since we won't be sending fork server commands. This should be replaced + with better auto-detection later on, perhaps? */ setenv(SHM_ENV_VAR, shm_str, 1); diff --git a/utils/persistent_mode/persistent_demo.c b/utils/persistent_mode/persistent_demo.c index 4cedc32c..f5e43728 100644 --- a/utils/persistent_mode/persistent_demo.c +++ b/utils/persistent_mode/persistent_demo.c @@ -27,9 +27,15 @@ #include #include #include +#include /* Main entry point. */ +/* To ensure checks are not optimized out it is recommended to disable + code optimization for the fuzzer harness main() */ +#pragma clang optimize off +#pragma GCC optimize("O0") + int main(int argc, char **argv) { ssize_t len; /* how much input did we read? */ @@ -42,7 +48,7 @@ int main(int argc, char **argv) { and similar hiccups. */ __AFL_INIT(); - while (__AFL_LOOP(1000)) { + while (__AFL_LOOP(UINT_MAX)) { /*** PLACEHOLDER CODE ***/ diff --git a/utils/persistent_mode/persistent_demo_new.c b/utils/persistent_mode/persistent_demo_new.c index 0d24a51e..7e694696 100644 --- a/utils/persistent_mode/persistent_demo_new.c +++ b/utils/persistent_mode/persistent_demo_new.c @@ -27,6 +27,7 @@ #include #include #include +#include /* this lets the source compile without afl-clang-fast/lto */ #ifndef __AFL_FUZZ_TESTCASE_LEN @@ -47,6 +48,11 @@ __AFL_FUZZ_INIT(); /* Main entry point. */ +/* To ensure checks are not optimized out it is recommended to disable + code optimization for the fuzzer harness main() */ +#pragma clang optimize off +#pragma GCC optimize("O0") + int main(int argc, char **argv) { ssize_t len; /* how much input did we read? */ @@ -60,7 +66,7 @@ int main(int argc, char **argv) { __AFL_INIT(); buf = __AFL_FUZZ_TESTCASE_BUF; // this must be assigned before __AFL_LOOP! - while (__AFL_LOOP(1000)) { // increase if you have good stability + while (__AFL_LOOP(UINT_MAX)) { // increase if you have good stability len = __AFL_FUZZ_TESTCASE_LEN; // do not use the macro directly in a call! diff --git a/utils/persistent_mode/test-instr.c b/utils/persistent_mode/test-instr.c index a6188b22..6da511de 100644 --- a/utils/persistent_mode/test-instr.c +++ b/utils/persistent_mode/test-instr.c @@ -17,15 +17,21 @@ #include #include #include +#include __AFL_FUZZ_INIT(); +/* To ensure checks are not optimized out it is recommended to disable + code optimization for the fuzzer harness main() */ +#pragma clang optimize off +#pragma GCC optimize("O0") + int main(int argc, char **argv) { __AFL_INIT(); unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; - while (__AFL_LOOP(2147483647)) { // MAX_INT if you have 100% stability + while (__AFL_LOOP(UINT_MAX)) { // if you have 100% stability unsigned int len = __AFL_FUZZ_TESTCASE_LEN; -- cgit 1.4.1 From e5c50037d5029e995c057ae50ece6b1ee87db106 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 9 Dec 2020 12:03:11 +0100 Subject: fix oversight for accessing nfuzz --- src/afl-fuzz-queue.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 28d560d1..84092ff8 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -45,8 +45,15 @@ inline u32 select_next_queue_entry(afl_state_t *afl) { double compute_weight(afl_state_t *afl, struct queue_entry *q, double avg_exec_us, double avg_bitmap_size) { - u32 hits = afl->n_fuzz[q->n_fuzz_entry]; - if (hits == 0) hits = 1; + u32 hits; + + if (likely(afl->schedule >= FAST && afl->schedule < RARE)) { + + hits = afl->n_fuzz[q->n_fuzz_entry]; + if (hits == 0) { hits = 1; } + + } else { hits = 1; } + double weight = 1.0; weight *= avg_exec_us / q->exec_us; -- cgit 1.4.1 From a686c1361ce353c6f47939eb7af1f264f0ce4853 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 10 Dec 2020 15:31:26 +0100 Subject: fix afl-cc library search --- src/afl-cc.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 273a9f2f..14d8e070 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -130,20 +130,27 @@ static u8 *find_object(u8 *obj, u8 *argv0) { u8 *afl_path = getenv("AFL_PATH"); u8 *slash = NULL, *tmp; - if (obj_path) { + /* + if (obj_path) { - tmp = alloc_printf("%s/%s", obj_path, obj); + tmp = alloc_printf("%s/%s", obj_path, obj); - if (!access(tmp, R_OK)) { return tmp; } + if (debug) DEBUGF("Trying %s\n", tmp); - ck_free(tmp); + if (!access(tmp, R_OK)) { return tmp; } - } + ck_free(tmp); + + } + + */ if (afl_path) { tmp = alloc_printf("%s/%s", afl_path, obj); + if (debug) DEBUGF("Trying %s\n", tmp); + if (!access(tmp, R_OK)) { obj_path = afl_path; @@ -168,6 +175,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { tmp = alloc_printf("%s/%s", dir, obj); + if (debug) DEBUGF("Trying %s\n", tmp); + if (!access(tmp, R_OK)) { obj_path = dir; @@ -178,6 +187,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { ck_free(tmp); tmp = alloc_printf("%s/../lib/afl/%s", dir, obj); + if (debug) DEBUGF("Trying %s\n", tmp); + if (!access(tmp, R_OK)) { u8 *dir2 = alloc_printf("%s/../lib/afl", dir); @@ -232,6 +243,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { ck_free(tmp); tmp = alloc_printf("%s/../lib/afl/%s", exepath, obj); + if (debug) DEBUGF("Trying %s\n", tmp); + if (!access(tmp, R_OK)) { u8 *dir = alloc_printf("%s/../lib/afl/", exepath); @@ -255,6 +268,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { tmp = alloc_printf("%s/%s", AFL_PATH, obj); + if (debug) DEBUGF("Trying %s\n", tmp); + if (!access(tmp, R_OK)) { obj_path = AFL_PATH; @@ -266,6 +281,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { tmp = alloc_printf("./%s", obj); + if (debug) DEBUGF("Trying %s\n", tmp); + if (!access(tmp, R_OK)) { obj_path = "."; @@ -275,6 +292,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { ck_free(tmp); + if (debug) DEBUGF("Trying ... giving up\n"); + return NULL; } -- cgit 1.4.1 From 8a1acac559edb66e8e246e73508cec541a9fc530 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 11 Dec 2020 10:28:39 +0100 Subject: schedule improvements, new default is FAST --- docs/Changelog.md | 7 ++++--- src/afl-fuzz-queue.c | 2 +- src/afl-fuzz-state.c | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 5094769d..4470388e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -16,16 +16,17 @@ sending a mail to . - all compilers combined to afl-cc which emulates the previous ones - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o - afl-fuzz - - memory limits are now disabled by default, set them with -m if required + - not specifying -M or -S will now auto-set "-S default" - deterministic fuzzing is now disabled by default and can be enabled with -D. It is still enabled by default for -M. - a new seed selection was implemented that uses weighted randoms based on a schedule performance score, which is much better that the previous walk the whole queue approach. Select the old mode with -Z (auto enabled with -M) - - rpc.statsd support, for stats and charts, by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) - - not specifying -M or -S will now auto-set "-S default" + - the default schedule is now FAST + - memory limits are now disabled by default, set them with -m if required + - rpc.statsd support, for stats and charts, by Edznux, thanks a lot! - reading testcases from -i now descends into subdirectories - allow the -x command line option up to 4 times - loaded extras now have a duplication protection diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 84092ff8..d74c07a1 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -47,7 +47,7 @@ double compute_weight(afl_state_t *afl, struct queue_entry *q, u32 hits; - if (likely(afl->schedule >= FAST && afl->schedule < RARE)) { + if (likely(afl->schedule >= FAST && afl->schedule <= RARE)) { hits = afl->n_fuzz[q->n_fuzz_entry]; if (hits == 0) { hits = 1; } diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 73b94466..36da2730 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -87,7 +87,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->w_end = 0.3; afl->g_max = 5000; afl->period_pilot_tmp = 5000.0; - afl->schedule = EXPLORE; /* Power schedule (default: EXPLORE) */ + afl->schedule = FAST ; /* Power schedule (default: FAST) */ afl->havoc_max_mult = HAVOC_MAX_MULT; afl->clear_screen = 1; /* Window resized? */ -- cgit 1.4.1 From 2bf68a0bf45fb2bb3bc0f574f20959a62c9f8239 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 11 Dec 2020 11:19:26 +0100 Subject: fix MMAP --- GNUmakefile.llvm | 4 ++-- instrumentation/afl-compiler-rt.o.c | 2 +- src/afl-cc.c | 27 +++++++++++---------------- src/afl-fuzz.c | 8 +++++++- 4 files changed, 21 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 6e80de81..414cd487 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -288,13 +288,13 @@ ifeq "$(shell echo '$(HASH)include @$(HASH)include @int ma SHMAT_OK=1 else SHMAT_OK=0 - #CFLAGS+=-DUSEMMAP=1 + CFLAGS_SAFE += -DUSEMMAP=1 LDFLAGS += -Wno-deprecated-declarations endif ifeq "$(TEST_MMAP)" "1" SHMAT_OK=0 - CFLAGS+=-DUSEMMAP=1 + CFLAGS_SAFE += -DUSEMMAP=1 LDFLAGS += -Wno-deprecated-declarations endif diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 99dcbb67..afe0839e 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1107,7 +1107,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { to avoid duplicate calls (which can happen as an artifact of the underlying implementation in LLVM). */ - *(start++) = R(MAP_SIZE - 1) + 1; + *(start++) = ++__afl_final_loc; while (start < stop) { diff --git a/src/afl-cc.c b/src/afl-cc.c index 14d8e070..37cfc7c8 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -130,21 +130,6 @@ static u8 *find_object(u8 *obj, u8 *argv0) { u8 *afl_path = getenv("AFL_PATH"); u8 *slash = NULL, *tmp; - /* - if (obj_path) { - - tmp = alloc_printf("%s/%s", obj_path, obj); - - if (debug) DEBUGF("Trying %s\n", tmp); - - if (!access(tmp, R_OK)) { return tmp; } - - ck_free(tmp); - - } - - */ - if (afl_path) { tmp = alloc_printf("%s/%s", afl_path, obj); @@ -906,6 +891,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif +#ifdef USEMMAP + cc_params[cc_par_cnt++] = "-lrt"; +#endif + } #endif @@ -1475,9 +1464,15 @@ int main(int argc, char **argv, char **envp) { if (have_llvm) SAYF("afl-cc LLVM version %d with the the binary path \"%s\".\n", LLVM_MAJOR, LLVM_BINDIR); - if (have_lto || have_llvm) SAYF("\n"); #endif +#ifdef USEMMAP + SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); +#else + SAYF("Compiled with shmat support.\n"); +#endif + SAYF("\n"); + SAYF( "Do not be overwhelmed :) afl-cc uses good defaults if no options are " "selected.\n" diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index bbe6aec6..391d4c4f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -230,6 +230,12 @@ static void usage(u8 *argv0, int more_help) { SAYF("Compiled without python module support\n"); #endif +#ifdef USEMMAP + SAYF("Compiled with shm_open support.\n"); +#else + SAYF("Compiled with shmat support.\n"); +#endif + #ifdef ASAN_BUILD SAYF("Compiled with ASAN_BUILD\n\n"); #endif @@ -254,7 +260,7 @@ static void usage(u8 *argv0, int more_help) { SAYF("Compiled with _AFL_DOCUMENT_MUTATIONS\n\n"); #endif - SAYF("For additional help please consult %s/README.md\n\n", doc_path); + SAYF("For additional help please consult %s/README.md :)\n\n", doc_path); exit(1); #undef PHYTON_SUPPORT -- cgit 1.4.1 From cc781e44f36d716f05da13bf760154a7810bfe43 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 11 Dec 2020 11:21:28 +0100 Subject: code format --- src/afl-cc.c | 8 ++++---- src/afl-fuzz-queue.c | 13 ++++++++----- src/afl-fuzz-state.c | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 37cfc7c8..c43ac2c1 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -891,9 +891,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif -#ifdef USEMMAP + #ifdef USEMMAP cc_params[cc_par_cnt++] = "-lrt"; -#endif + #endif } @@ -1467,9 +1467,9 @@ int main(int argc, char **argv, char **envp) { #endif #ifdef USEMMAP - SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); + SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); #else - SAYF("Compiled with shmat support.\n"); + SAYF("Compiled with shmat support.\n"); #endif SAYF("\n"); diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index d74c07a1..54afa17c 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -46,14 +46,17 @@ double compute_weight(afl_state_t *afl, struct queue_entry *q, double avg_exec_us, double avg_bitmap_size) { u32 hits; - + if (likely(afl->schedule >= FAST && afl->schedule <= RARE)) { - + hits = afl->n_fuzz[q->n_fuzz_entry]; if (hits == 0) { hits = 1; } - - } else { hits = 1; } - + + } else { + + hits = 1; + + } double weight = 1.0; weight *= avg_exec_us / q->exec_us; diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 36da2730..9c51a3ef 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -87,7 +87,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->w_end = 0.3; afl->g_max = 5000; afl->period_pilot_tmp = 5000.0; - afl->schedule = FAST ; /* Power schedule (default: FAST) */ + afl->schedule = FAST; /* Power schedule (default: FAST) */ afl->havoc_max_mult = HAVOC_MAX_MULT; afl->clear_screen = 1; /* Window resized? */ -- cgit 1.4.1 From 609f3d02651381215815eeadb7a10999c2041ffe Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 11 Dec 2020 13:29:45 +0100 Subject: fixed gcc analyzer warnings --- include/alloc-inl.h | 3 ++- src/afl-as.c | 5 +++++ src/afl-common.c | 24 ++++++++++++++++++++++-- src/afl-fuzz-init.c | 18 +++++++++++++----- src/afl-fuzz-run.c | 12 ++++++------ 5 files changed, 48 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 68255fb6..3044b7a0 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -94,7 +94,8 @@ static inline void *DFL_ck_alloc_nozero(u32 size) { } -/* Allocate a buffer, returning zeroed memory. */ +/* Allocate a buffer, returning zeroed memory. + Returns null for 0 size */ static inline void *DFL_ck_alloc(u32 size) { diff --git a/src/afl-as.c b/src/afl-as.c index 3d6f7d5e..2171bb58 100644 --- a/src/afl-as.c +++ b/src/afl-as.c @@ -131,6 +131,11 @@ static void edit_params(int argc, char **argv) { if (!tmp_dir) { tmp_dir = "/tmp"; } as_params = ck_alloc((argc + 32) * sizeof(u8 *)); + if (unlikely((argc + 32) < argc || !as_params)) { + + FATAL("Too many parameters passed to as"); + + } as_params[0] = afl_as ? afl_as : (u8 *)"as"; diff --git a/src/afl-common.c b/src/afl-common.c index ed0b0e53..4df22394 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -108,6 +108,7 @@ char **argv_cpy_dup(int argc, char **argv) { int i = 0; char **ret = ck_alloc((argc + 1) * sizeof(char *)); + if (unlikely(!ret)) { FATAL("Amount of arguments specified is too high"); } for (i = 0; i < argc; i++) { @@ -130,6 +131,7 @@ void argv_cpy_free(char **argv) { while (argv[i]) { ck_free(argv[i]); + argv[i] = NULL; i++; } @@ -142,8 +144,12 @@ void argv_cpy_free(char **argv) { char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { + if (!unlikely(own_loc)) { FATAL("BUG: param own_loc is NULL"); } + + u8 *tmp, *cp = NULL, *rsl, *own_copy; + char **new_argv = ck_alloc(sizeof(char *) * (argc + 4)); - u8 * tmp, *cp = NULL, *rsl, *own_copy; + if (unlikely(!new_argv)) { FATAL("Illegal amount of arguments specified"); } memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1)); new_argv[argc + 3] = NULL; @@ -224,8 +230,12 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { + if (!unlikely(own_loc)) { FATAL("BUG: param own_loc is NULL"); } + + u8 *tmp, *cp = NULL, *rsl, *own_copy; + char **new_argv = ck_alloc(sizeof(char *) * (argc + 3)); - u8 * tmp, *cp = NULL, *rsl, *own_copy; + if (unlikely(!new_argv)) { FATAL("Illegal amount of arguments specified"); } memcpy(&new_argv[2], &argv[1], (int)(sizeof(char *)) * (argc - 1)); new_argv[argc + 2] = NULL; @@ -335,6 +345,8 @@ u8 *find_binary(u8 *fname) { struct stat st; + if (unlikely(!fname)) { FATAL("No binary supplied"); } + if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { target_path = ck_strdup(fname); @@ -356,6 +368,14 @@ u8 *find_binary(u8 *fname) { if (delim) { cur_elem = ck_alloc(delim - env_path + 1); + if (unlikely(!cur_elem)) { + + FATAL( + "Unexpected overflow when processing ENV. This should never " + "happend."); + + } + memcpy(cur_elem, env_path, delim - env_path); delim++; diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 6707340b..0db3a111 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -772,10 +772,17 @@ void perform_dry_run(afl_state_t *afl) { while (q) { - u8 *use_mem; + u8 use_mem[MAX_FILE]; u8 res; s32 fd; + if (unlikely(!q->len)) { + + WARNF("Skipping 0-sized entry in queue (%s)", q->fname); + continue; + + } + u8 *fn = strrchr(q->fname, '/') + 1; ACTF("Attempting dry run with '%s'...", fn); @@ -783,9 +790,8 @@ void perform_dry_run(afl_state_t *afl) { fd = open(q->fname, O_RDONLY); if (fd < 0) { PFATAL("Unable to open '%s'", q->fname); } - use_mem = ck_alloc_nozero(q->len); - - if (read(fd, use_mem, q->len) != (ssize_t)q->len) { + u32 read_len = MIN(q->len, (u32)MAX_FILE); + if (read(fd, use_mem, read_len) != (ssize_t)read_len) { FATAL("Short read from '%s'", q->fname); @@ -794,7 +800,6 @@ void perform_dry_run(afl_state_t *afl) { close(fd); res = calibrate_case(afl, q, use_mem, 0, 1); - ck_free(use_mem); if (afl->stop_soon) { return; } @@ -2449,6 +2454,8 @@ void setup_testcase_shmem(afl_state_t *afl) { void check_binary(afl_state_t *afl, u8 *fname) { + if (unlikely(!fname)) { FATAL("BUG: Binary name is NULL"); } + u8 * env_path = 0; struct stat st; @@ -2477,6 +2484,7 @@ void check_binary(afl_state_t *afl, u8 *fname) { if (delim) { cur_elem = ck_alloc(delim - env_path + 1); + if (unlikely(!cur_elem)) { FATAL("Unexpected large PATH"); } memcpy(cur_elem, env_path, delim - env_path); ++delim; diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 5948d83a..b6603f1a 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -94,9 +94,9 @@ write_to_testcase(afl_state_t *afl, void *mem, u32 len) { if (unlikely(afl->custom_mutators_count)) { - u8 * new_buf = NULL; ssize_t new_size = len; - void * new_mem = mem; + u8 * new_mem = mem; + u8 * new_buf = NULL; LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { @@ -152,13 +152,13 @@ static void write_with_gap(afl_state_t *afl, u8 *mem, u32 len, u32 skip_at, if (unlikely(!mem_trimmed)) { PFATAL("alloc"); } ssize_t new_size = len - skip_len; - void * new_mem = mem; - u8 * new_buf = NULL; + u8 * new_mem = mem; bool post_process_skipped = true; if (unlikely(afl->custom_mutators_count)) { + u8 *new_buf = NULL; new_mem = mem_trimmed; LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { @@ -207,7 +207,7 @@ static void write_with_gap(afl_state_t *afl, u8 *mem, u32 len, u32 skip_at, // If we did post_processing, copy directly from the new_buf bufer - memcpy(afl->fsrv.shmem_fuzz, new_buf, new_size); + memcpy(afl->fsrv.shmem_fuzz, new_mem, new_size); } @@ -265,7 +265,7 @@ static void write_with_gap(afl_state_t *afl, u8 *mem, u32 len, u32 skip_at, if (!post_process_skipped) { - ck_write(fd, new_buf, new_size, afl->fsrv.out_file); + ck_write(fd, new_mem, new_size, afl->fsrv.out_file); } else { -- cgit 1.4.1 From e4a113b953e0b746cffa47e24ad52f18733217bc Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 12 Dec 2020 13:26:25 +0100 Subject: small fix in error handling --- src/afl-sharedmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-sharedmem.c b/src/afl-sharedmem.c index 3e671df5..fe641d0d 100644 --- a/src/afl-sharedmem.c +++ b/src/afl-sharedmem.c @@ -205,7 +205,7 @@ u8 *afl_shm_init(sharedmem_t *shm, size_t map_size, /* map the shared memory segment to the address space of the process */ shm->cmp_map = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm->cmplog_g_shm_fd, 0); - if (shm->map == MAP_FAILED) { + if (shm->cmp_map == MAP_FAILED) { close(shm->cmplog_g_shm_fd); shm->cmplog_g_shm_fd = -1; -- cgit 1.4.1 From fd30a4184a149cfd1f45ff6de94487dbdc6dea61 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 12 Dec 2020 16:37:23 +0100 Subject: typo --- src/afl-fuzz-run.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index b6603f1a..c8ad14c4 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -205,7 +205,7 @@ static void write_with_gap(afl_state_t *afl, u8 *mem, u32 len, u32 skip_at, if (!post_process_skipped) { - // If we did post_processing, copy directly from the new_buf bufer + // If we did post_processing, copy directly from the new_mem buffer memcpy(afl->fsrv.shmem_fuzz, new_mem, new_size); @@ -365,7 +365,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, write_to_testcase(afl, use_mem, q->len); fault = fuzz_run_target(afl, &afl->fsrv, use_tmout); - +fprintf(stderr, "from fuzz_run_target() fault=%u\n", fault); /* afl->stop_soon is set by the handler for Ctrl+C. When it's pressed, we want to bail out quickly. */ -- cgit 1.4.1 From befb1a2f39b28dbc360d5a6937705c48768ec053 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 12 Dec 2020 16:40:13 +0100 Subject: remove stray debugging fprintf --- src/afl-fuzz-run.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index c8ad14c4..a97ceb89 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -365,7 +365,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, write_to_testcase(afl, use_mem, q->len); fault = fuzz_run_target(afl, &afl->fsrv, use_tmout); -fprintf(stderr, "from fuzz_run_target() fault=%u\n", fault); + /* afl->stop_soon is set by the handler for Ctrl+C. When it's pressed, we want to bail out quickly. */ -- cgit 1.4.1 From 7382cf5f00c1ba5d926c12e3ed4ca0e14d761fcb Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 12 Dec 2020 19:30:56 +0100 Subject: afl-as.c, fix compiler warnings (overflowing is UB) --- src/afl-as.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-as.c b/src/afl-as.c index 2171bb58..7de267a3 100644 --- a/src/afl-as.c +++ b/src/afl-as.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -131,7 +132,7 @@ static void edit_params(int argc, char **argv) { if (!tmp_dir) { tmp_dir = "/tmp"; } as_params = ck_alloc((argc + 32) * sizeof(u8 *)); - if (unlikely((argc + 32) < argc || !as_params)) { + if (unlikely((INT_MAX - 32) < argc || !as_params)) { FATAL("Too many parameters passed to as"); -- cgit 1.4.1 From e0ab846f7fd3e45bbf76e4ab82eef19d9aaf5494 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 15 Dec 2020 09:37:52 +0100 Subject: v3.00c --- README.md | 2 +- include/config.h | 2 +- src/afl-cc.c | 26 ++++++++++++++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/README.md b/README.md index dc009def..68b64ce6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![Travis State](https://api.travis-ci.com/AFLplusplus/AFLplusplus.svg?branch=stable) - Release Version: [2.68c](https://github.com/AFLplusplus/AFLplusplus/releases) + Release Version: [3.00c](https://github.com/AFLplusplus/AFLplusplus/releases) Github Version: 3.00a diff --git a/include/config.h b/include/config.h index 491d8132..93249ed9 100644 --- a/include/config.h +++ b/include/config.h @@ -28,7 +28,7 @@ /* Version string: */ // c = release, d = volatile github dev, e = experimental branch -#define VERSION "++3.00a" +#define VERSION "++3.00c" /****************************************************** * * diff --git a/src/afl-cc.c b/src/afl-cc.c index c43ac2c1..2aeb2178 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -69,6 +69,7 @@ enum { INSTRUMENT_INSTRIM = 3, INSTRUMENT_CFG = 3, INSTRUMENT_LTO = 4, + INSTRUMENT_LLVMNATIVE = 5, INSTRUMENT_OPT_CTX = 8, INSTRUMENT_OPT_NGRAM = 16 @@ -76,8 +77,9 @@ enum { char instrument_mode_string[18][18] = { - "DEFAULT", "CLASSIC", "PCGUARD", "CFG", "LTO", "", "", "", "CTX", "", - "", "", "", "", "", "", "NGRAM", "" + "DEFAULT", "CLASSIC", "PCGUARD", "CFG", "LTO", "", "PCGUARD-NATIVE", + "", "CTX", "", "", "", "", "", + "", "", "NGRAM", "" }; @@ -580,6 +582,14 @@ static void edit_params(u32 argc, char **argv, char **envp) { #endif #endif + } else if (instrument_mode == INSTRUMENT_LLVMNATIVE) { + +#if LLVM_MAJOR >= 4 + cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; +#else + FATAL("pcguard instrumentation requires llvm 4.0.1+"); +#endif + } else { cc_params[cc_par_cnt++] = "-Xclang"; @@ -1162,6 +1172,18 @@ int main(int argc, char **argv, char **envp) { } + // this is a hidden option + if (strncasecmp(ptr, "llvmnative", strlen("llvmnative")) == 0 || + strncasecmp(ptr, "llvm-native", strlen("llvm-native")) == 0) { + + if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE) + instrument_mode = INSTRUMENT_LLVMNATIVE; + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + + } + if (strncasecmp(ptr, "cfg", strlen("cfg")) == 0 || strncasecmp(ptr, "instrim", strlen("instrim")) == 0) { -- cgit 1.4.1 From 14c67f15c98b16bb0c22f6a94d66d714bf61af5a Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 16 Dec 2020 14:22:09 +0100 Subject: small fixes --- src/afl-fuzz-init.c | 2 +- test/test-basic.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 0db3a111..ec937f29 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -666,7 +666,7 @@ void read_testcases(afl_state_t *afl, u8 *directory) { } - if (afl->shuffle_queue && nl_cnt > 1) { + if (unlikely(afl->old_seed_selection && afl->shuffle_queue && nl_cnt > 1)) { ACTF("Shuffling queue..."); shuffle_ptrs(afl, (void **)nl, nl_cnt); diff --git a/test/test-basic.sh b/test/test-basic.sh index 24aa30a4..79ad8743 100755 --- a/test/test-basic.sh +++ b/test/test-basic.sh @@ -220,9 +220,9 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc case "$CNT" in *2) $ECHO "$GREEN[+] afl-cmin.bash correctly minimized the number of testcases" ;; 1) { - test -s in2/* && $ECHO "$YELLOW[?] afl-cmin did minimize to one testcase. This can be a bug or due compiler optimization." + test -s in2/* && $ECHO "$YELLOW[?] afl-cmin.bash did minimize to one testcase. This can be a bug or due compiler optimization." test -s in2/* || { - $ECHO "$RED[!] afl-cmin did not correctly minimize the number of testcases ($CNT)" + $ECHO "$RED[!] afl-cmin.bash did not correctly minimize the number of testcases ($CNT)" CODE=1 } } -- cgit 1.4.1 From d07b0169cb6137e4620db63b144e46e140b7437d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 17 Dec 2020 10:55:33 +0100 Subject: skim import --- GNUmakefile | 37 ++++--- include/afl-fuzz.h | 9 +- src/afl-fuzz-bitmap.c | 284 ++++++++++++++------------------------------------ src/afl-fuzz-run.c | 6 +- 4 files changed, 105 insertions(+), 231 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index 5c82279b..af85be06 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -42,8 +42,8 @@ endif ifdef ASAN_BUILD $(info Compiling ASAN version of binaries) - override CFLAGS+=$(ASAN_CFLAGS) - LDFLAGS+=$(ASAN_LDFLAGS) + override CFLAGS += $(ASAN_CFLAGS) + LDFLAGS += $(ASAN_LDFLAGS) endif ifdef UBSAN_BUILD $(info Compiling UBSAN version of binaries) @@ -77,30 +77,34 @@ ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -fno-move-loop-invariants - SPECIAL_PERFORMANCE += -fno-move-loop-invariants -fdisable-tree-cunrolli endif +ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + ifndef SOURCE_DATE_EPOCH + HAVE_MARCHNATIVE = 1 + CFLAGS_OPT += -march=native + endif +endif + ifneq "$(shell uname)" "Darwin" - ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" - ifndef SOURCE_DATE_EPOCH - #CFLAGS_OPT += -march=native - SPECIAL_PERFORMANCE += -march=native - endif - endif + ifeq "$(HAVE_MARCHNATIVE)" "1" + SPECIAL_PERFORMANCE += -march=native + endif # OS X does not like _FORTIFY_SOURCE=2 - ifndef DEBUG - CFLAGS_OPT += -D_FORTIFY_SOURCE=2 - endif + ifndef DEBUG + CFLAGS_OPT += -D_FORTIFY_SOURCE=2 + endif endif ifeq "$(shell uname)" "SunOS" - CFLAGS_OPT += -Wno-format-truncation - LDFLAGS=-lkstat -lrt + CFLAGS_OPT += -Wno-format-truncation + LDFLAGS = -lkstat -lrt endif ifdef STATIC $(info Compiling static version of binaries, disabling python though) # Disable python for static compilation to simplify things - PYTHON_OK=0 + PYTHON_OK = 0 PYFLAGS= - PYTHON_INCLUDE=/ + PYTHON_INCLUDE = / CFLAGS_OPT += -static LDFLAGS += -lm -lpthread -lz -lutil @@ -117,6 +121,7 @@ ifdef INTROSPECTION CFLAGS_OPT += -DINTROSPECTION=1 endif + ifneq "$(shell uname -m)" "x86_64" ifneq "$(patsubst i%86,i386,$(shell uname -m))" "i386" ifneq "$(shell uname -m)" "amd64" @@ -131,7 +136,7 @@ ifdef DEBUG $(info Compiling DEBUG version of binaries) CFLAGS += -ggdb3 -O0 -Wall -Wextra -Werror else - CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT) + CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT) endif override CFLAGS += -g -Wno-pointer-sign -Wno-variadic-macros -Wall -Wextra -Wpointer-arith \ diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 2f2d31d3..6e695a97 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1014,14 +1014,9 @@ void write_bitmap(afl_state_t *); u32 count_bits(afl_state_t *, u8 *); u32 count_bytes(afl_state_t *, u8 *); u32 count_non_255_bytes(afl_state_t *, u8 *); -#ifdef WORD_SIZE_64 -void simplify_trace(afl_state_t *, u64 *); -void classify_counts(afl_forkserver_t *); -#else -void simplify_trace(afl_state_t *, u32 *); -void classify_counts(afl_forkserver_t *); -#endif +void simplify_trace(afl_state_t *, u8 *); void init_count_class16(void); +void classify_counts(afl_forkserver_t *fsrv); void minimize_bits(afl_state_t *, u8 *, u8 *); #ifndef SIMPLE_FILES u8 *describe_op(afl_state_t *, u8, size_t); diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index f1ca7400..738ba986 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -49,101 +49,6 @@ void write_bitmap(afl_state_t *afl) { } -/* Check if the current execution path brings anything new to the table. - Update virgin bits to reflect the finds. Returns 1 if the only change is - the hit-count for a particular tuple; 2 if there are new tuples seen. - Updates the map, so subsequent calls will always return 0. - - This function is called after every exec() on a fairly large buffer, so - it needs to be fast. We do this in 32-bit and 64-bit flavors. */ - -u8 __attribute__((hot)) has_new_bits(afl_state_t *afl, u8 *virgin_map) { - -#ifdef WORD_SIZE_64 - - u64 *current = (u64 *)afl->fsrv.trace_bits; - u64 *virgin = (u64 *)virgin_map; - - u32 i = (afl->fsrv.map_size >> 3); - -#else - - u32 *current = (u32 *)afl->fsrv.trace_bits; - u32 *virgin = (u32 *)virgin_map; - - u32 i = (afl->fsrv.map_size >> 2); - -#endif /* ^WORD_SIZE_64 */ - // the map size must be a minimum of 8 bytes. - // for variable/dynamic map sizes this is ensured in the forkserver - - u8 ret = 0; - - while (i--) { - - /* Optimize for (*current & *virgin) == 0 - i.e., no bits in current bitmap - that have not been already cleared from the virgin map - since this will - almost always be the case. */ - - // the (*current) is unnecessary but speeds up the overall comparison - if (unlikely(*current) && unlikely(*current & *virgin)) { - - if (likely(ret < 2)) { - - u8 *cur = (u8 *)current; - u8 *vir = (u8 *)virgin; - - /* Looks like we have not found any new bytes yet; see if any non-zero - bytes in current[] are pristine in virgin[]. */ - -#ifdef WORD_SIZE_64 - - if (*virgin == 0xffffffffffffffff || (cur[0] && vir[0] == 0xff) || - (cur[1] && vir[1] == 0xff) || (cur[2] && vir[2] == 0xff) || - (cur[3] && vir[3] == 0xff) || (cur[4] && vir[4] == 0xff) || - (cur[5] && vir[5] == 0xff) || (cur[6] && vir[6] == 0xff) || - (cur[7] && vir[7] == 0xff)) { - - ret = 2; - - } else { - - ret = 1; - - } - -#else - - if (*virgin == 0xffffffff || (cur[0] && vir[0] == 0xff) || - (cur[1] && vir[1] == 0xff) || (cur[2] && vir[2] == 0xff) || - (cur[3] && vir[3] == 0xff)) - ret = 2; - else - ret = 1; - -#endif /* ^WORD_SIZE_64 */ - - } - - *virgin &= ~*current; - - } - - ++current; - ++virgin; - - } - - if (unlikely(ret) && likely(virgin_map == afl->virgin_bits)) { - - afl->bitmap_changed = 1; - - } - - return ret; - -} - /* Count the number of bits set in the provided bitmap. Used for the status screen several times every second, does not have to be fast. */ @@ -242,77 +147,11 @@ const u8 simplify_lookup[256] = { }; -#ifdef WORD_SIZE_64 - -void simplify_trace(afl_state_t *afl, u64 *mem) { - - u32 i = (afl->fsrv.map_size >> 3); - - while (i--) { - - /* Optimize for sparse bitmaps. */ - - if (unlikely(*mem)) { - - u8 *mem8 = (u8 *)mem; - - mem8[0] = simplify_lookup[mem8[0]]; - mem8[1] = simplify_lookup[mem8[1]]; - mem8[2] = simplify_lookup[mem8[2]]; - mem8[3] = simplify_lookup[mem8[3]]; - mem8[4] = simplify_lookup[mem8[4]]; - mem8[5] = simplify_lookup[mem8[5]]; - mem8[6] = simplify_lookup[mem8[6]]; - mem8[7] = simplify_lookup[mem8[7]]; - - } else { - - *mem = 0x0101010101010101ULL; - - } - - ++mem; - - } - -} - -#else - -void simplify_trace(afl_state_t *afl, u32 *mem) { - - u32 i = (afl->fsrv.map_size >> 2); - - while (i--) { - - /* Optimize for sparse bitmaps. */ - - if (unlikely(*mem)) { - - u8 *mem8 = (u8 *)mem; - - mem8[0] = simplify_lookup[mem8[0]]; - mem8[1] = simplify_lookup[mem8[1]]; - mem8[2] = simplify_lookup[mem8[2]]; - mem8[3] = simplify_lookup[mem8[3]]; - - } else - - *mem = 0x01010101; - - ++mem; - - } - -} - -#endif /* ^WORD_SIZE_64 */ - /* Destructively classify execution counts in a trace. This is used as a preprocessing step for any newly acquired traces. Called on every exec, must be fast. */ -static const u8 count_class_lookup8[256] = { +const u8 count_class_lookup8[256] = { [0] = 0, [1] = 1, @@ -326,7 +165,7 @@ static const u8 count_class_lookup8[256] = { }; -static u16 count_class_lookup16[65536]; +u16 count_class_lookup16[65536]; void init_count_class16(void) { @@ -345,63 +184,87 @@ void init_count_class16(void) { } -#ifdef WORD_SIZE_64 +/* Import coverage processing routines. */ -void __attribute__((hot)) classify_counts(afl_forkserver_t *fsrv) { +#ifdef WORD_SIZE_64 + #include "coverage-64.h" +#else + #include "coverage-32.h" +#endif - u64 *mem = (u64 *)fsrv->trace_bits; +/* Check if the current execution path brings anything new to the table. + Update virgin bits to reflect the finds. Returns 1 if the only change is + the hit-count for a particular tuple; 2 if there are new tuples seen. + Updates the map, so subsequent calls will always return 0. - u32 i = (fsrv->map_size >> 3); + This function is called after every exec() on a fairly large buffer, so + it needs to be fast. We do this in 32-bit and 64-bit flavors. */ - while (i--) { +inline u8 has_new_bits(afl_state_t *afl, u8 *virgin_map) { - /* Optimize for sparse bitmaps. */ +#ifdef WORD_SIZE_64 - if (unlikely(*mem)) { + u64 *current = (u64 *)afl->fsrv.trace_bits; + u64 *virgin = (u64 *)virgin_map; - u16 *mem16 = (u16 *)mem; + u32 i = (afl->fsrv.map_size >> 3); - mem16[0] = count_class_lookup16[mem16[0]]; - mem16[1] = count_class_lookup16[mem16[1]]; - mem16[2] = count_class_lookup16[mem16[2]]; - mem16[3] = count_class_lookup16[mem16[3]]; +#else - } + u32 *current = (u32 *)afl->fsrv.trace_bits; + u32 *virgin = (u32 *)virgin_map; - ++mem; + u32 i = (afl->fsrv.map_size >> 2); - } +#endif /* ^WORD_SIZE_64 */ -} + u8 ret = 0; + while (i--) { -#else + if (unlikely(*current)) discover_word(&ret, current, virgin); -void __attribute__((hot)) classify_counts(afl_forkserver_t *fsrv) { + current++; + virgin++; - u32 *mem = (u32 *)fsrv->trace_bits; + } - u32 i = (fsrv->map_size >> 2); + if (unlikely(ret) && likely(virgin_map == afl->virgin_bits)) + afl->bitmap_changed = 1; - while (i--) { + return ret; - /* Optimize for sparse bitmaps. */ +} - if (unlikely(*mem)) { +/* A combination of classify_counts and has_new_bits. If 0 is returned, then the + * trace bits are kept as-is. Otherwise, the trace bits are overwritten with + * classified values. + * + * This accelerates the processing: in most cases, no interesting behavior + * happen, and the trace bits will be discarded soon. This function optimizes + * for such cases: one-pass scan on trace bits without modifying anything. Only + * on rare cases it fall backs to the slow path: classify_counts() first, then + * return has_new_bits(). */ - u16 *mem16 = (u16 *)mem; +inline u8 has_new_bits_unclassified(afl_state_t *afl, u8 *virgin_map) { - mem16[0] = count_class_lookup16[mem16[0]]; - mem16[1] = count_class_lookup16[mem16[1]]; + /* Handle the hot path first: no new coverage */ + u8 *end = afl->fsrv.trace_bits + afl->fsrv.map_size; - } +#ifdef WORD_SIZE_64 - ++mem; + if (!skim((u64 *)virgin_map, (u64 *)afl->fsrv.trace_bits, (u64 *)end)) + return 0; - } +#else -} + if (!skim((u32 *)virgin_map, (u32 *)afl->fsrv.trace_bits, (u32 *)end)) + return 0; #endif /* ^WORD_SIZE_64 */ + classify_counts(&afl->fsrv); + return has_new_bits(afl, virgin_map); + +} /* Compact trace bytes into a smaller bitmap. We effectively just drop the count information here. This is called only sporadically, for some @@ -581,7 +444,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { u8 *queue_fn = ""; u8 new_bits = '\0'; s32 fd; - u8 keeping = 0, res; + u8 keeping = 0, res, classified = 0; u64 cksum = 0; u8 fn[PATH_MAX]; @@ -605,13 +468,17 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { /* Keep only if there are new bits in the map, add to queue for future fuzzing, etc. */ - if (!(new_bits = has_new_bits(afl, afl->virgin_bits))) { + new_bits = has_new_bits_unclassified(afl, afl->virgin_bits); + + if (likely(!new_bits)) { if (unlikely(afl->crash_mode)) { ++afl->total_crashes; } return 0; } + classified = new_bits; + #ifndef SIMPLE_FILES queue_fn = alloc_printf( @@ -715,11 +582,14 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (likely(!afl->non_instrumented_mode)) { -#ifdef WORD_SIZE_64 - simplify_trace(afl, (u64 *)afl->fsrv.trace_bits); -#else - simplify_trace(afl, (u32 *)afl->fsrv.trace_bits); -#endif /* ^WORD_SIZE_64 */ + if (!classified) { + + classify_counts(&afl->fsrv); + classified = 1; + + } + + simplify_trace(afl, afl->fsrv.trace_bits); if (!has_new_bits(afl, afl->virgin_tmout)) { return keeping; } @@ -764,6 +634,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { u8 new_fault; write_to_testcase(afl, mem, len); new_fault = fuzz_run_target(afl, &afl->fsrv, afl->hang_tmout); + classify_counts(&afl->fsrv); /* A corner case that one user reported bumping into: increasing the timeout actually uncovers a crash. Make sure we don't discard it if @@ -812,11 +683,14 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (likely(!afl->non_instrumented_mode)) { -#ifdef WORD_SIZE_64 - simplify_trace(afl, (u64 *)afl->fsrv.trace_bits); -#else - simplify_trace(afl, (u32 *)afl->fsrv.trace_bits); -#endif /* ^WORD_SIZE_64 */ + if (!classified) { + + classify_counts(&afl->fsrv); + classified = 1; + + } + + simplify_trace(afl, afl->fsrv.trace_bits); if (!has_new_bits(afl, afl->virgin_crash)) { return keeping; } diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index a97ceb89..60086bd6 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -62,8 +62,6 @@ fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) { time_spent_start = (spec.tv_sec * 1000000000) + spec.tv_nsec; #endif - // TODO: Don't classify for faults? - classify_counts(fsrv); return res; } @@ -379,6 +377,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } + classify_counts(&afl->fsrv); cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); if (q->exec_cksum != cksum) { @@ -767,13 +766,14 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { write_with_gap(afl, in_buf, q->len, remove_pos, trim_avail); fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); - ++afl->trim_execs; if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; } /* Note that we don't keep track of crashes or hangs here; maybe TODO? */ + ++afl->trim_execs; + classify_counts(&afl->fsrv); cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); /* If the deletion had no impact on the trace, make it permanent. This -- cgit 1.4.1 From fd6bff727a860220bdd035952a7666c60f444b3f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 17 Dec 2020 22:57:28 +0100 Subject: fix crash for very fast targets --- docs/Changelog.md | 1 + src/afl-common.c | 5 +++++ src/afl-fuzz-stats.c | 2 ++ 3 files changed, 8 insertions(+) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index c2ed0a12..ac75c68d 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,6 +10,7 @@ sending a mail to . ### Version ++3.01a (release) + - fix crash for very, very fast targets+systems, thanks for reporting @mhlakhani - added dummy Makefile to instrumentation/ - allow instrumenting LLVMFuzzerTestOneInput diff --git a/src/afl-common.c b/src/afl-common.c index 4df22394..6dc8abe0 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "debug.h" #include "alloc-inl.h" @@ -786,6 +787,10 @@ u8 *u_stringify_float(u8 *buf, double val) { sprintf(buf, "%0.01f", val); + } else if (unlikely(isnan(val) || isfinite(val))) { + + strcpy(buf, "999.9"); + } else { return u_stringify_int(buf, (u64)val); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 321bbb35..50e2ef15 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -371,6 +371,8 @@ void show_stats(afl_state_t *afl) { if (!afl->stats_last_execs) { + if (unlikely(cur_ms == afl->start_time)) --afl->start_time; + afl->stats_avg_exec = ((double)afl->fsrv.total_execs) * 1000 / (cur_ms - afl->start_time); -- cgit 1.4.1 From 0011f2047bdd3e1adc25de4388edd609dc27bc85 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 18 Dec 2020 09:33:52 +0100 Subject: merge romu and skim --- include/afl-fuzz.h | 13 ++-- include/coverage-32.h | 109 +++++++++++++++++++++++++++++ include/coverage-64.h | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/afl-performance.c | 124 ++++++++------------------------- 4 files changed, 331 insertions(+), 101 deletions(-) create mode 100644 include/coverage-32.h create mode 100644 include/coverage-64.h (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 6e695a97..31c19287 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -134,6 +134,12 @@ // Little helper to access the ptr to afl->##name_buf - for use in afl_realloc. #define AFL_BUF_PARAM(name) ((void **)&afl->name##_buf) +#ifdef WORD_SIZE_64 + #define AFL_RAND_RETURN u64 +#else + #define AFL_RAND_RETURN u32 +#endif + extern s8 interesting_8[INTERESTING_8_LEN]; extern s16 interesting_16[INTERESTING_8_LEN + INTERESTING_16_LEN]; extern s32 @@ -580,7 +586,7 @@ typedef struct afl_state { u32 rand_cnt; /* Random number counter */ - u64 rand_seed[4]; + u64 rand_seed[3]; s64 init_seed; u64 total_cal_us, /* Total calibration time (us) */ @@ -1015,8 +1021,8 @@ u32 count_bits(afl_state_t *, u8 *); u32 count_bytes(afl_state_t *, u8 *); u32 count_non_255_bytes(afl_state_t *, u8 *); void simplify_trace(afl_state_t *, u8 *); +void classify_counts(afl_forkserver_t *); void init_count_class16(void); -void classify_counts(afl_forkserver_t *fsrv); void minimize_bits(afl_state_t *, u8 *, u8 *); #ifndef SIMPLE_FILES u8 *describe_op(afl_state_t *, u8, size_t); @@ -1106,8 +1112,7 @@ u8 common_fuzz_cmplog_stuff(afl_state_t *afl, u8 *out_buf, u32 len); u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, u64 exec_cksum); -/* xoshiro256** */ -uint64_t rand_next(afl_state_t *afl); +AFL_RAND_RETURN rand_next(afl_state_t *afl); /* probability between 0.0 and 1.0 */ double rand_next_percent(afl_state_t *afl); diff --git a/include/coverage-32.h b/include/coverage-32.h new file mode 100644 index 00000000..710ff0cf --- /dev/null +++ b/include/coverage-32.h @@ -0,0 +1,109 @@ +#include "config.h" +#include "types.h" + +inline u32 classify_word(u32 word) { + + u16 mem16[2]; + memcpy(mem16, &word, sizeof(mem16)); + + mem16[0] = count_class_lookup16[mem16[0]]; + mem16[1] = count_class_lookup16[mem16[1]]; + + memcpy(&word, mem16, sizeof(mem16)); + return word; + +} + +void simplify_trace(afl_state_t *afl, u8 *bytes) { + + u32 *mem = (u32 *)fsrv->trace_bits; + u32 i = (fsrv->map_size >> 2); + + while (i--) { + + /* Optimize for sparse bitmaps. */ + + if (unlikely(*mem)) { + + u8 *mem8 = (u8 *)mem; + + mem8[0] = simplify_lookup[mem8[0]]; + mem8[1] = simplify_lookup[mem8[1]]; + mem8[2] = simplify_lookup[mem8[2]]; + mem8[3] = simplify_lookup[mem8[3]]; + + } else + + *mem = 0x01010101; + + mem++; + + } + +} + +inline void classify_counts(u8 *bytes) { + + u64 *mem = (u64 *)bytes; + u32 i = MAP_SIZE >> 2; + + while (i--) { + + /* Optimize for sparse bitmaps. */ + + if (unlikely(*mem)) { *mem = classify_word(*mem); } + + mem++; + + } + +} + +/* Updates the virgin bits, then reflects whether a new count or a new tuple is + * seen in ret. */ +inline void discover_word(u8 *ret, u32 *current, u32 *virgin) { + + /* Optimize for (*current & *virgin) == 0 - i.e., no bits in current bitmap + that have not been already cleared from the virgin map - since this will + almost always be the case. */ + + if (*current & *virgin) { + + if (likely(*ret < 2)) { + + u8 *cur = (u8 *)current; + u8 *vir = (u8 *)virgin; + + /* Looks like we have not found any new bytes yet; see if any non-zero + bytes in current[] are pristine in virgin[]. */ + + if ((cur[0] && vir[0] == 0xff) || (cur[1] && vir[1] == 0xff) || + (cur[2] && vir[2] == 0xff) || (cur[3] && vir[3] == 0xff)) + *ret = 2; + else + *ret = 1; + + } + + *virgin &= ~*current; + + } + +} + +#define PACK_SIZE 16 +inline u32 skim(const u32 *virgin, const u32 *current, const u32 *current_end) { + + for (; current != current_end; virgin += 4, current += 4) { + + if (current[0] && classify_word(current[0]) & virgin[0]) return 1; + if (current[1] && classify_word(current[1]) & virgin[1]) return 1; + if (current[2] && classify_word(current[2]) & virgin[2]) return 1; + if (current[3] && classify_word(current[3]) & virgin[3]) return 1; + + } + + return 0; + +} + diff --git a/include/coverage-64.h b/include/coverage-64.h new file mode 100644 index 00000000..54cf0073 --- /dev/null +++ b/include/coverage-64.h @@ -0,0 +1,186 @@ +#include "config.h" +#include "types.h" + +#if (defined(__AVX512F__) && defined(__AVX512DQ__)) || defined(__AVX2__) + #include +#endif + +inline u64 classify_word(u64 word) { + + u16 mem16[4]; + memcpy(mem16, &word, sizeof(mem16)); + + mem16[0] = count_class_lookup16[mem16[0]]; + mem16[1] = count_class_lookup16[mem16[1]]; + mem16[2] = count_class_lookup16[mem16[2]]; + mem16[3] = count_class_lookup16[mem16[3]]; + + memcpy(&word, mem16, sizeof(mem16)); + return word; + +} + +void simplify_trace(afl_state_t *afl, u8 *bytes) { + + u64 *mem = (u64 *)bytes; + u32 i = (afl->fsrv.map_size >> 3); + + while (i--) { + + /* Optimize for sparse bitmaps. */ + + if (unlikely(*mem)) { + + u8 *mem8 = (u8 *)mem; + + mem8[0] = simplify_lookup[mem8[0]]; + mem8[1] = simplify_lookup[mem8[1]]; + mem8[2] = simplify_lookup[mem8[2]]; + mem8[3] = simplify_lookup[mem8[3]]; + mem8[4] = simplify_lookup[mem8[4]]; + mem8[5] = simplify_lookup[mem8[5]]; + mem8[6] = simplify_lookup[mem8[6]]; + mem8[7] = simplify_lookup[mem8[7]]; + + } else + + *mem = 0x0101010101010101ULL; + + mem++; + + } + +} + +inline void classify_counts(afl_forkserver_t *fsrv) { + + u64 *mem = (u64 *)fsrv->trace_bits; + u32 i = (fsrv->map_size >> 3); + + while (i--) { + + /* Optimize for sparse bitmaps. */ + + if (unlikely(*mem)) { *mem = classify_word(*mem); } + + mem++; + + } + +} + +/* Updates the virgin bits, then reflects whether a new count or a new tuple is + * seen in ret. */ +inline void discover_word(u8 *ret, u64 *current, u64 *virgin) { + + /* Optimize for (*current & *virgin) == 0 - i.e., no bits in current bitmap + that have not been already cleared from the virgin map - since this will + almost always be the case. */ + + if (*current & *virgin) { + + if (likely(*ret < 2)) { + + u8 *cur = (u8 *)current; + u8 *vir = (u8 *)virgin; + + /* Looks like we have not found any new bytes yet; see if any non-zero + bytes in current[] are pristine in virgin[]. */ + + if ((cur[0] && vir[0] == 0xff) || (cur[1] && vir[1] == 0xff) || + (cur[2] && vir[2] == 0xff) || (cur[3] && vir[3] == 0xff) || + (cur[4] && vir[4] == 0xff) || (cur[5] && vir[5] == 0xff) || + (cur[6] && vir[6] == 0xff) || (cur[7] && vir[7] == 0xff)) + *ret = 2; + else + *ret = 1; + + } + + *virgin &= ~*current; + + } + +} + +#if defined(__AVX512F__) && defined(__AVX512DQ__) + #define PACK_SIZE 64 +inline u32 skim(const u64 *virgin, const u64 *current, const u64 *current_end) { + + for (; current != current_end; virgin += 8, current += 8) { + + __m512i value = *(__m512i *)current; + __mmask8 mask = _mm512_testn_epi64_mask(value, value); + + /* All bytes are zero. */ + if (mask == 0xff) continue; + + /* Look for nonzero bytes and check for new bits. */ + #define UNROLL(x) \ + if (!(mask & (1 << x)) && classify_word(current[x]) & virgin[x]) return 1 + UNROLL(0); + UNROLL(1); + UNROLL(2); + UNROLL(3); + UNROLL(4); + UNROLL(5); + UNROLL(6); + UNROLL(7); + #undef UNROLL + + } + + return 0; + +} + +#endif + +#if !defined(PACK_SIZE) && defined(__AVX2__) + #define PACK_SIZE 32 +inline u32 skim(const u64 *virgin, const u64 *current, const u64 *current_end) { + + __m256i zeroes = _mm256_setzero_si256(); + + for (; current != current_end; virgin += 4, current += 4) { + + __m256i value = *(__m256i *)current; + __m256i cmp = _mm256_cmpeq_epi64(value, zeroes); + u32 mask = _mm256_movemask_epi8(cmp); + + /* All bytes are zero. */ + if (mask == (u32)-1) continue; + + /* Look for nonzero bytes and check for new bits. */ + if (!(mask & 0xff) && classify_word(current[0]) & virgin[0]) return 1; + if (!(mask & 0xff00) && classify_word(current[1]) & virgin[1]) return 1; + if (!(mask & 0xff0000) && classify_word(current[2]) & virgin[2]) return 1; + if (!(mask & 0xff000000) && classify_word(current[3]) & virgin[3]) return 1; + + } + + return 0; + +} + +#endif + +#if !defined(PACK_SIZE) + #define PACK_SIZE 32 +inline u32 skim(const u64 *virgin, const u64 *current, const u64 *current_end) { + + for (; current != current_end; virgin += 4, current += 4) { + + if (current[0] && classify_word(current[0]) & virgin[0]) return 1; + if (current[1] && classify_word(current[1]) & virgin[1]) return 1; + if (current[2] && classify_word(current[2]) & virgin[2]) return 1; + if (current[3] && classify_word(current[3]) & virgin[3]) return 1; + + } + + return 0; + +} + +#endif + diff --git a/src/afl-performance.c b/src/afl-performance.c index e070a05e..89b170eb 100644 --- a/src/afl-performance.c +++ b/src/afl-performance.c @@ -27,45 +27,49 @@ #include "xxhash.h" #undef XXH_INLINE_ALL -/* we use xoshiro256** instead of rand/random because it is 10x faster and has - better randomness properties. */ - -static inline uint64_t rotl(const uint64_t x, int k) { - - return (x << k) | (x >> (64 - k)); - -} - void rand_set_seed(afl_state_t *afl, s64 init_seed) { afl->init_seed = init_seed; afl->rand_seed[0] = hash64((u8 *)&afl->init_seed, sizeof(afl->init_seed), HASH_CONST); afl->rand_seed[1] = afl->rand_seed[0] ^ 0x1234567890abcdef; - afl->rand_seed[2] = afl->rand_seed[0] & 0x0123456789abcdef; - afl->rand_seed[3] = afl->rand_seed[0] | 0x01abcde43f567908; + afl->rand_seed[2] = (afl->rand_seed[0] & 0x1234567890abcdef) ^ + (afl->rand_seed[1] | 0xfedcba9876543210); } -inline uint64_t rand_next(afl_state_t *afl) { +#define ROTL(d, lrot) ((d << (lrot)) | (d >> (8 * sizeof(d) - (lrot)))) - const uint64_t result = - rotl(afl->rand_seed[0] + afl->rand_seed[3], 23) + afl->rand_seed[0]; +#ifdef WORD_SIZE_64 +// romuDuoJr +inline AFL_RAND_RETURN rand_next(afl_state_t *afl) { - const uint64_t t = afl->rand_seed[1] << 17; + AFL_RAND_RETURN xp = afl->rand_seed[0]; + afl->rand_seed[0] = 15241094284759029579u * afl->rand_seed[1]; + afl->rand_seed[1] = afl->rand_seed[1] - xp; + afl->rand_seed[1] = ROTL(afl->rand_seed[1], 27); + return xp; - afl->rand_seed[2] ^= afl->rand_seed[0]; - afl->rand_seed[3] ^= afl->rand_seed[1]; - afl->rand_seed[1] ^= afl->rand_seed[2]; - afl->rand_seed[0] ^= afl->rand_seed[3]; +} - afl->rand_seed[2] ^= t; +#else +// RomuTrio32 +inline AFL_RAND_RETURN rand_next(afl_state_t *afl) { + + AFL_RAND_RETURN xp = afl->rand_seed[0], yp = afl->rand_seed[1], + zp = afl->rand_seed[2]; + afl->rand_seed[0] = 3323815723u * zp; + afl->rand_seed[1] = yp - xp; + afl->rand_seed[1] = ROTL(afl->rand_seed[1], 6); + afl->rand_seed[2] = zp - yp; + afl->rand_seed[2] = ROTL(afl->rand_seed[2], 22); + return xp; - afl->rand_seed[3] = rotl(afl->rand_seed[3], 45); +} - return result; +#endif -} +#undef ROTL /* returns a double between 0.000000000 and 1.000000000 */ @@ -75,80 +79,6 @@ inline double rand_next_percent(afl_state_t *afl) { } -/* This is the jump function for the generator. It is equivalent - to 2^128 calls to rand_next(); it can be used to generate 2^128 - non-overlapping subsequences for parallel computations. */ - -void jump(afl_state_t *afl) { - - static const uint64_t JUMP[] = {0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, - 0xa9582618e03fc9aa, 0x39abdc4529b1661c}; - size_t i, b; - uint64_t s0 = 0; - uint64_t s1 = 0; - uint64_t s2 = 0; - uint64_t s3 = 0; - for (i = 0; i < (sizeof(JUMP) / sizeof(*JUMP)); i++) - for (b = 0; b < 64; b++) { - - if (JUMP[i] & UINT64_C(1) << b) { - - s0 ^= afl->rand_seed[0]; - s1 ^= afl->rand_seed[1]; - s2 ^= afl->rand_seed[2]; - s3 ^= afl->rand_seed[3]; - - } - - rand_next(afl); - - } - - afl->rand_seed[0] = s0; - afl->rand_seed[1] = s1; - afl->rand_seed[2] = s2; - afl->rand_seed[3] = s3; - -} - -/* This is the long-jump function for the generator. It is equivalent to - 2^192 calls to rand_next(); it can be used to generate 2^64 starting points, - from each of which jump() will generate 2^64 non-overlapping - subsequences for parallel distributed computations. */ - -void long_jump(afl_state_t *afl) { - - static const uint64_t LONG_JUMP[] = {0x76e15d3efefdcbbf, 0xc5004e441c522fb3, - 0x77710069854ee241, 0x39109bb02acbe635}; - - size_t i, b; - uint64_t s0 = 0; - uint64_t s1 = 0; - uint64_t s2 = 0; - uint64_t s3 = 0; - for (i = 0; i < (sizeof(LONG_JUMP) / sizeof(*LONG_JUMP)); i++) - for (b = 0; b < 64; b++) { - - if (LONG_JUMP[i] & UINT64_C(1) << b) { - - s0 ^= afl->rand_seed[0]; - s1 ^= afl->rand_seed[1]; - s2 ^= afl->rand_seed[2]; - s3 ^= afl->rand_seed[3]; - - } - - rand_next(afl); - - } - - afl->rand_seed[0] = s0; - afl->rand_seed[1] = s1; - afl->rand_seed[2] = s2; - afl->rand_seed[3] = s3; - -} - /* we switch from afl's murmur implementation to xxh3 as it is 30% faster - and get 64 bit hashes instead of just 32 bit. Less collisions! :-) */ -- cgit 1.4.1 From ea9db86bb86341e4e2b53c46204e3f86496e6ab9 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 18 Dec 2020 10:20:43 +0100 Subject: mem error fix --- include/afl-fuzz.h | 3 ++- instrumentation/afl-compiler-rt.o.c | 4 ++-- src/afl-forkserver.c | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 3acb6b93..99647c5b 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1022,7 +1022,7 @@ u32 count_bytes(afl_state_t *, u8 *); u32 count_non_255_bytes(afl_state_t *, u8 *); void simplify_trace(afl_state_t *, u8 *); void classify_counts(afl_forkserver_t *); -u8 has_new_bits_unclassified(afl_state_t *, u8 *); +void discover_word(u8 *ret, u64 *current, u64 *virgin); void init_count_class16(void); void minimize_bits(afl_state_t *, u8 *, u8 *); #ifndef SIMPLE_FILES @@ -1030,6 +1030,7 @@ u8 *describe_op(afl_state_t *, u8, size_t); #endif u8 save_if_interesting(afl_state_t *, void *, u32, u8); u8 has_new_bits(afl_state_t *, u8 *); +u8 has_new_bits_unclassified(afl_state_t *, u8 *); /* Extras */ diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index b1df26db..cddde87c 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -236,8 +236,8 @@ static void __afl_map_shm(void) { if (__afl_final_loc) { - if (__afl_final_loc % 8) - __afl_final_loc = (((__afl_final_loc + 7) >> 3) << 3); + if (__afl_final_loc % 32) + __afl_final_loc = (((__afl_final_loc + 31) >> 5) << 5); __afl_map_size = __afl_final_loc; if (__afl_final_loc > MAP_SIZE) { diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 3afb94be..90fa55e9 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -641,11 +641,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if (!fsrv->map_size) { fsrv->map_size = MAP_SIZE; } - if (unlikely(tmp_map_size % 8)) { + if (unlikely(tmp_map_size % 32)) { // should not happen WARNF("Target reported non-aligned map size of %u", tmp_map_size); - tmp_map_size = (((tmp_map_size + 8) >> 3) << 3); + tmp_map_size = (((tmp_map_size + 31) >> 5) << 5); } -- cgit 1.4.1 From 12ebb351dc2b5655b4174539e2b9ee59e4acf893 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Fri, 18 Dec 2020 21:10:39 +0100 Subject: apply nocolor changes --- docs/Changelog.md | 2 ++ docs/env_variables.md | 3 +++ include/config.h | 16 ++++++++++++ include/debug.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++----- include/envs.h | 4 +++ src/afl-fuzz-state.c | 16 ++++++++++++ src/afl-fuzz.c | 15 +++++++++++ 7 files changed, 122 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index ac75c68d..ebc514f3 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -52,6 +52,8 @@ sending a mail to . - somewhere we broke -n dumb fuzzing, fixed - added afl_custom_describe to the custom mutator API to allow for easy mutation reproduction on crashing inputs + - new env. var. AFL_NO_COLOR (or AFL_NO_COLOUR) to suppress colored + console output (when configured with USE_COLOR and not ALWAYS_COLORED) - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! diff --git a/docs/env_variables.md b/docs/env_variables.md index e203055f..74863d8d 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -381,6 +381,9 @@ checks or alter some of the more exotic semantics of the tool: some basic stats. This behavior is also automatically triggered when the output from afl-fuzz is redirected to a file or to a pipe. + - Setting `AFL_NO_COLOR` or `AFL_NO_COLOUR` will omit control sequences for + coloring console output when configured with USE_COLOR and not ALWAYS_COLORED. + - Setting `AFL_FORCE_UI` will force painting the UI on the screen even if no valid terminal was detected (for virtual consoles) diff --git a/include/config.h b/include/config.h index 1eb6bc5e..d57ef223 100644 --- a/include/config.h +++ b/include/config.h @@ -36,11 +36,27 @@ * * ******************************************************/ +/* console output colors: There are three ways to configure its behavior + * 1. default: colored outputs fixed on: defined USE_COLOR && defined ALWAYS_COLORED + * The env var. AFL_NO_COLOR will have no effect + * 2. defined USE_COLOR && !defined ALWAYS_COLORED + * -> depending on env var AFL_NO_COLOR=1 colors can be switched off + * at run-time. Default is to use colors. + * 3. colored outputs fixed off: !defined USE_COLOR + * The env var. AFL_NO_COLOR will have no effect +*/ + /* Comment out to disable terminal colors (note that this makes afl-analyze a lot less nice): */ #define USE_COLOR +#ifdef USE_COLOR +/* Comment in to always enable terminal colors */ +/* Comment out to enable runtime controlled terminal colors via AFL_NO_COLOR */ +#define ALWAYS_COLORED 1 +#endif + /* StatsD config Config can be adjusted via AFL_STATSD_HOST and AFL_STATSD_PORT environment variable. diff --git a/include/debug.h b/include/debug.h index 5512023c..7a1725b5 100644 --- a/include/debug.h +++ b/include/debug.h @@ -168,12 +168,72 @@ * Debug & error macros * ************************/ -/* Just print stuff to the appropriate stream. */ +#if defined USE_COLOR && !defined ALWAYS_COLORED +#include +#pragma GCC diagnostic ignored "-Wformat-security" +static inline const char * colorfilter(const char * x) { + static int once = 1; + static int disabled = 0; + + if (once) { + /* when there is no tty -> we always want filtering + * when AFL_NO_UI is set filtering depends on AFL_NO_COLOR + * otherwise we want always colors + */ + disabled = isatty(2) && (!getenv("AFL_NO_UI") || (!getenv("AFL_NO_COLOR") && !getenv("AFL_NO_COLOUR"))); + once = 0; + } + if (likely(disabled)) return x; + + static char monochromestring[4096]; + char *d = monochromestring; + int in_seq = 0; + + while(*x) { + if (in_seq && *x == 'm') { + in_seq = 0; + } else { + if (!in_seq && *x == '\x1b') { in_seq = 1; } + if (!in_seq) { + *d++ = *x; + } + } + ++x; + } + + *d = '\0'; + return monochromestring; +} +#else +#define colorfilter(x) x /* no filtering necessary */ +#endif +/* macro magic to transform the first parameter to SAYF + * through colorfilter which strips coloring */ +#define GET_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,\ +_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,\ +_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,\ +_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,\ +NAME,...) NAME + +#define SAYF(...) GET_MACRO(__VA_ARGS__, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) + +#define SAYF_1(x) MY_SAYF(colorfilter(x)) +#define SAYF_N(x,...) MY_SAYF(colorfilter(x), __VA_ARGS__) + +/* Just print stuff to the appropriate stream. */ #ifdef MESSAGES_TO_STDOUT - #define SAYF(x...) printf(x) + #define MY_SAYF(x...) printf(x) #else - #define SAYF(x...) fprintf(stderr, x) + #define MY_SAYF(x...) fprintf(stderr, x) #endif /* ^MESSAGES_TO_STDOUT */ /* Show a prefixed warning. */ @@ -222,7 +282,7 @@ do { \ \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __func__, \ __FILE__, __LINE__); \ exit(1); \ @@ -235,7 +295,7 @@ do { \ \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __func__, \ __FILE__, __LINE__); \ abort(); \ @@ -249,7 +309,7 @@ \ fflush(stdout); \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] SYSTEM ERROR : " cRST x); \ + "\n[-] SYSTEM ERROR : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __func__, \ __FILE__, __LINE__); \ SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ diff --git a/include/envs.h b/include/envs.h index c0f41ca5..f16e61f0 100644 --- a/include/envs.h +++ b/include/envs.h @@ -103,6 +103,10 @@ static char *afl_environment_variables[] = { "AFL_NO_ARITH", "AFL_NO_AUTODICT", "AFL_NO_BUILTIN", +#if defined USE_COLOR && ! defined ALWAYS_COLORED + "AFL_NO_COLOR", + "AFL_NO_COLOUR", +#endif "AFL_NO_CPU_RED", "AFL_NO_FORKSRV", "AFL_NO_UI", diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 9c51a3ef..e863c4c7 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -401,6 +401,22 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_crash_exitcode = (u8 *)get_afl_env(afl_environment_variables[i]); +#if defined USE_COLOR && ! defined ALWAYS_COLORED + } else if (!strncmp(env, "AFL_NO_COLOR", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd_tags_flavor = + (u8 *)get_afl_env(afl_environment_variables[i]); + + } else if (!strncmp(env, "AFL_NO_COLOUR", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd_tags_flavor = + (u8 *)get_afl_env(afl_environment_variables[i]); +#endif + } } else { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 391d4c4f..e0d46f7e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -156,6 +156,12 @@ static void usage(u8 *argv0, int more_help) { if (more_help > 1) { +#if defined USE_COLOR && !defined ALWAYS_COLORED + #define DYN_COLOR "AFL_NO_COLOR or AFL_NO_COLOUR: switch colored console output off\n" +#else + #define DYN_COLOR +#endif + SAYF( "Environment variables used:\n" "LD_BIND_LAZY: do not set LD_BIND_NOW env var for target\n" @@ -194,6 +200,9 @@ static void usage(u8 *argv0, int more_help) { "AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n" "AFL_NO_SNAPSHOT: do not use the snapshot feature (if the snapshot lkm is loaded)\n" "AFL_NO_UI: switch status screen off\n" + + DYN_COLOR + "AFL_PATH: path to AFL support binaries\n" "AFL_PYTHON_MODULE: mutate and trim inputs with the specified Python module\n" "AFL_QUIET: suppress forkserver status messages\n" @@ -298,6 +307,12 @@ int main(int argc, char **argv_orig, char **envp) { struct timeval tv; struct timezone tz; +#if defined USE_COLOR && defined ALWAYS_COLORED + if (getenv("AFL_NO_COLOR") || getenv("AFL_NO_COLOUR")) { + WARNF("Setting AFL_NO_COLOR has no effect (colors are configured on at compile time)"); + } +#endif + char **argv = argv_cpy_dup(argc, argv_orig); afl_state_t *afl = calloc(1, sizeof(afl_state_t)); -- cgit 1.4.1 From 98ee17bc47b1f1d4664be9955a72727fff8e51fa Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 20 Dec 2020 14:30:06 +0100 Subject: fix endless loop in afl-cc allow/blocklists starting a line with a comment --- docs/Changelog.md | 8 +++- include/config.h | 13 ++++--- include/debug.h | 76 ++++++++++++++++++++++---------------- include/envs.h | 4 +- instrumentation/afl-gcc-pass.so.cc | 6 ++- instrumentation/afl-llvm-common.cc | 6 ++- src/afl-cc.c | 2 +- src/afl-fuzz-state.c | 3 +- src/afl-fuzz.c | 14 +++++-- 9 files changed, 80 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index ebc514f3..28b7e723 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,9 +10,13 @@ sending a mail to . ### Version ++3.01a (release) - - fix crash for very, very fast targets+systems, thanks for reporting @mhlakhani + - fix crash for very, very fast targets+systems (thanks to mhlakhani + for reporting) - added dummy Makefile to instrumentation/ - - allow instrumenting LLVMFuzzerTestOneInput + - afl-cc + - allow instrumenting LLVMFuzzerTestOneInput + - fixed endless loop for allow/blocklist lines starting with a + comment (thanks to Zherya for reporting) ### Version ++3.00c (release) diff --git a/include/config.h b/include/config.h index d57ef223..e8a49270 100644 --- a/include/config.h +++ b/include/config.h @@ -37,14 +37,14 @@ ******************************************************/ /* console output colors: There are three ways to configure its behavior - * 1. default: colored outputs fixed on: defined USE_COLOR && defined ALWAYS_COLORED - * The env var. AFL_NO_COLOR will have no effect + * 1. default: colored outputs fixed on: defined USE_COLOR && defined + * ALWAYS_COLORED The env var. AFL_NO_COLOR will have no effect * 2. defined USE_COLOR && !defined ALWAYS_COLORED * -> depending on env var AFL_NO_COLOR=1 colors can be switched off * at run-time. Default is to use colors. * 3. colored outputs fixed off: !defined USE_COLOR * The env var. AFL_NO_COLOR will have no effect -*/ + */ /* Comment out to disable terminal colors (note that this makes afl-analyze a lot less nice): */ @@ -52,9 +52,10 @@ #define USE_COLOR #ifdef USE_COLOR -/* Comment in to always enable terminal colors */ -/* Comment out to enable runtime controlled terminal colors via AFL_NO_COLOR */ -#define ALWAYS_COLORED 1 + /* Comment in to always enable terminal colors */ + /* Comment out to enable runtime controlled terminal colors via AFL_NO_COLOR + */ + #define ALWAYS_COLORED 1 #endif /* StatsD config diff --git a/include/debug.h b/include/debug.h index 7a1725b5..7f4a6be1 100644 --- a/include/debug.h +++ b/include/debug.h @@ -169,65 +169,77 @@ ************************/ #if defined USE_COLOR && !defined ALWAYS_COLORED -#include -#pragma GCC diagnostic ignored "-Wformat-security" -static inline const char * colorfilter(const char * x) { + #include + #pragma GCC diagnostic ignored "-Wformat-security" +static inline const char *colorfilter(const char *x) { + static int once = 1; static int disabled = 0; if (once) { + /* when there is no tty -> we always want filtering * when AFL_NO_UI is set filtering depends on AFL_NO_COLOR * otherwise we want always colors */ - disabled = isatty(2) && (!getenv("AFL_NO_UI") || (!getenv("AFL_NO_COLOR") && !getenv("AFL_NO_COLOUR"))); + disabled = + isatty(2) && (!getenv("AFL_NO_UI") || + (!getenv("AFL_NO_COLOR") && !getenv("AFL_NO_COLOUR"))); once = 0; + } + if (likely(disabled)) return x; static char monochromestring[4096]; - char *d = monochromestring; - int in_seq = 0; + char * d = monochromestring; + int in_seq = 0; + + while (*x) { - while(*x) { if (in_seq && *x == 'm') { + in_seq = 0; + } else { + if (!in_seq && *x == '\x1b') { in_seq = 1; } - if (!in_seq) { - *d++ = *x; - } + if (!in_seq) { *d++ = *x; } + } + ++x; + } *d = '\0'; return monochromestring; + } + #else -#define colorfilter(x) x /* no filtering necessary */ + #define colorfilter(x) x /* no filtering necessary */ #endif /* macro magic to transform the first parameter to SAYF * through colorfilter which strips coloring */ -#define GET_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,\ -_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,\ -_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,\ -_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,\ -NAME,...) NAME - -#define SAYF(...) GET_MACRO(__VA_ARGS__, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) - -#define SAYF_1(x) MY_SAYF(colorfilter(x)) -#define SAYF_N(x,...) MY_SAYF(colorfilter(x), __VA_ARGS__) +#define GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, \ + _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, \ + _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, \ + _39, _40, NAME, ...) \ + NAME + +#define SAYF(...) \ + GET_MACRO(__VA_ARGS__, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_1) \ + (__VA_ARGS__) + +#define SAYF_1(x) MY_SAYF(colorfilter(x)) +#define SAYF_N(x, ...) MY_SAYF(colorfilter(x), __VA_ARGS__) /* Just print stuff to the appropriate stream. */ #ifdef MESSAGES_TO_STDOUT @@ -282,7 +294,7 @@ SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) do { \ \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __func__, \ __FILE__, __LINE__); \ exit(1); \ @@ -295,7 +307,7 @@ SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) do { \ \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __func__, \ __FILE__, __LINE__); \ abort(); \ @@ -309,7 +321,7 @@ SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) \ fflush(stdout); \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] SYSTEM ERROR : " cRST x); \ + "\n[-] SYSTEM ERROR : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __func__, \ __FILE__, __LINE__); \ SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ diff --git a/include/envs.h b/include/envs.h index f16e61f0..e4e49c4d 100644 --- a/include/envs.h +++ b/include/envs.h @@ -78,8 +78,8 @@ static char *afl_environment_variables[] = { "AFL_LLVM_CTX", "AFL_LLVM_DICT2FILE", "AFL_LLVM_DOCUMENT_IDS", - "AFL_LLVM_INSTRUMENT", "AFL_LLVM_INSTRIM_LOOPHEAD", + "AFL_LLVM_INSTRUMENT", "AFL_LLVM_LTO_AUTODICTIONARY", "AFL_LLVM_AUTODICTIONARY", "AFL_LLVM_SKIPSINGLEBLOCK", @@ -103,7 +103,7 @@ static char *afl_environment_variables[] = { "AFL_NO_ARITH", "AFL_NO_AUTODICT", "AFL_NO_BUILTIN", -#if defined USE_COLOR && ! defined ALWAYS_COLORED +#if defined USE_COLOR && !defined ALWAYS_COLORED "AFL_NO_COLOR", "AFL_NO_COLOUR", #endif diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index c95ead8f..25437609 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -622,10 +622,11 @@ struct afl_pass : gimple_opt_pass { allowListFiles.push_back(line); else allowListFunctions.push_back(line); - getline(fileStream, line); } + getline(fileStream, line); + } if (debug) @@ -696,10 +697,11 @@ struct afl_pass : gimple_opt_pass { denyListFiles.push_back(line); else denyListFunctions.push_back(line); - getline(fileStream, line); } + getline(fileStream, line); + } if (debug) diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc index 557939fd..a27c4069 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -168,10 +168,11 @@ void initInstrumentList() { allowListFiles.push_back(line); else allowListFunctions.push_back(line); - getline(fileStream, line); } + getline(fileStream, line); + } if (debug) @@ -242,10 +243,11 @@ void initInstrumentList() { denyListFiles.push_back(line); else denyListFunctions.push_back(line); - getline(fileStream, line); } + getline(fileStream, line); + } if (debug) diff --git a/src/afl-cc.c b/src/afl-cc.c index 2aeb2178..8593f9b8 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1432,7 +1432,7 @@ int main(int argc, char **argv, char **envp) { " AFL_LLVM_LAF_SPLIT_FLOATS: cascaded comparisons on floats\n" " AFL_LLVM_LAF_TRANSFORM_COMPARES: cascade comparisons for string " "functions\n" - " AFL_LLVM_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: enable " + " AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST: enable " "instrument allow/\n" " deny listing (selective instrumentation)\n"); diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index e863c4c7..7053572b 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -401,7 +401,8 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_crash_exitcode = (u8 *)get_afl_env(afl_environment_variables[i]); -#if defined USE_COLOR && ! defined ALWAYS_COLORED +#if defined USE_COLOR && !defined ALWAYS_COLORED + } else if (!strncmp(env, "AFL_NO_COLOR", afl_environment_variable_len)) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e0d46f7e..2af374f2 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -157,7 +157,8 @@ static void usage(u8 *argv0, int more_help) { if (more_help > 1) { #if defined USE_COLOR && !defined ALWAYS_COLORED - #define DYN_COLOR "AFL_NO_COLOR or AFL_NO_COLOUR: switch colored console output off\n" + #define DYN_COLOR \ + "AFL_NO_COLOR or AFL_NO_COLOUR: switch colored console output off\n" #else #define DYN_COLOR #endif @@ -307,11 +308,16 @@ int main(int argc, char **argv_orig, char **envp) { struct timeval tv; struct timezone tz; -#if defined USE_COLOR && defined ALWAYS_COLORED + #if defined USE_COLOR && defined ALWAYS_COLORED if (getenv("AFL_NO_COLOR") || getenv("AFL_NO_COLOUR")) { - WARNF("Setting AFL_NO_COLOR has no effect (colors are configured on at compile time)"); + + WARNF( + "Setting AFL_NO_COLOR has no effect (colors are configured on at " + "compile time)"); + } -#endif + + #endif char **argv = argv_cpy_dup(argc, argv_orig); -- cgit 1.4.1 From 2e3cf10070681375a6c0e63ad39e7ce04ff22684 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 20 Dec 2020 22:53:41 +0100 Subject: document AFL_LLVM_INSTRUMENT option NATIVE --- docs/Changelog.md | 2 ++ docs/env_variables.md | 2 ++ instrumentation/README.llvm.md | 21 +-------------------- src/afl-cc.c | 3 +++ 4 files changed, 8 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 28b7e723..a26a4e0e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -17,6 +17,8 @@ sending a mail to . - allow instrumenting LLVMFuzzerTestOneInput - fixed endless loop for allow/blocklist lines starting with a comment (thanks to Zherya for reporting) + - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support + (less performant than our own) ### Version ++3.00c (release) diff --git a/docs/env_variables.md b/docs/env_variables.md index 74863d8d..c1693748 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -113,6 +113,8 @@ Then there are a few specific features that are only available in instrumentatio - `AFL_LLVM_INSTRUMENT` - this configures the instrumentation mode. Available options: + PCGUARD - our own pcgard based instrumentation (default) + NATIVE - clang's original pcguard based instrumentation CLASSIC - classic AFL (map[cur_loc ^ prev_loc >> 1]++) (default) CFG - InsTrim instrumentation (see below) LTO - LTO instrumentation (see below) diff --git a/instrumentation/README.llvm.md b/instrumentation/README.llvm.md index 07636970..2705ce0d 100644 --- a/instrumentation/README.llvm.md +++ b/instrumentation/README.llvm.md @@ -168,26 +168,7 @@ This is the most powerful and effective fuzzing you can do. Please see [README.persistent_mode.md](README.persistent_mode.md) for a full explanation. -## 7) Bonus feature: 'trace-pc-guard' mode - -LLVM is shipping with a built-in execution tracing feature -that provides AFL with the necessary tracing data without the need to -post-process the assembly or install any compiler plugins. See: - - http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards - -If you have not an outdated compiler and want to give it a try, build -targets this way: - -``` -AFL_LLVM_INSTRUMENT=PCGUARD make -``` - -Note that this is currently the default if you use LLVM >= 7, as it is the best -mode. Recommended is LLVM >= 9. -If you have llvm 11+ and compiled afl-clang-lto - this is the only better mode. - -## 8) Bonus feature: 'dict2file' pass +## 7) Bonus feature: 'dict2file' pass Just specify `AFL_LLVM_DICT2FILE=/absolute/path/file.txt` and during compilation all constant string compare parameters will be written to this file to be diff --git a/src/afl-cc.c b/src/afl-cc.c index 8593f9b8..6f4801de 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1346,6 +1346,9 @@ int main(int argc, char **argv, char **envp) { "Sub-Modes: (set via env AFL_LLVM_INSTRUMENT, afl-cc selects the best " "available)\n" " PCGUARD: Dominator tree instrumentation (best!) (README.llvm.md)\n" +#if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) + " NATIVE: use llvm's native PCGUARD instrumentation (less performant)\n" +#endif " CLASSIC: decision target instrumentation (README.llvm.md)\n" " CTX: CLASSIC + callee context (instrumentation/README.ctx.md)\n" " NGRAM-x: CLASSIC + previous path " -- cgit 1.4.1 From 7e27448dac2191060320831904f32fe9d572bc3d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 21 Dec 2020 12:19:22 +0100 Subject: another 32 bit fix --- include/afl-fuzz.h | 4 ++++ include/coverage-32.h | 2 +- src/afl-cc.c | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 99647c5b..e2fb0344 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1022,7 +1022,11 @@ u32 count_bytes(afl_state_t *, u8 *); u32 count_non_255_bytes(afl_state_t *, u8 *); void simplify_trace(afl_state_t *, u8 *); void classify_counts(afl_forkserver_t *); +#ifdef WORD_SIZE_64 void discover_word(u8 *ret, u64 *current, u64 *virgin); +#else +void discover_word(u8 *ret, u32 *current, u32 *virgin); +#endif void init_count_class16(void); void minimize_bits(afl_state_t *, u8 *, u8 *); #ifndef SIMPLE_FILES diff --git a/include/coverage-32.h b/include/coverage-32.h index 124d6ee5..a5cc498c 100644 --- a/include/coverage-32.h +++ b/include/coverage-32.h @@ -47,7 +47,7 @@ void simplify_trace(afl_state_t *afl, u8 *bytes) { inline void classify_counts(afl_forkserver_t *fsrv) { - u64 *mem = (u32 *)fsrv->trace_bits; + u32 *mem = (u32 *)fsrv->trace_bits; u32 i = (fsrv->map_size >> 2); while (i--) { diff --git a/src/afl-cc.c b/src/afl-cc.c index 6f4801de..3b8092a9 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1347,7 +1347,8 @@ int main(int argc, char **argv, char **envp) { "available)\n" " PCGUARD: Dominator tree instrumentation (best!) (README.llvm.md)\n" #if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) - " NATIVE: use llvm's native PCGUARD instrumentation (less performant)\n" + " NATIVE: use llvm's native PCGUARD instrumentation (less " + "performant)\n" #endif " CLASSIC: decision target instrumentation (README.llvm.md)\n" " CTX: CLASSIC + callee context (instrumentation/README.ctx.md)\n" -- cgit 1.4.1 From 27b9ba45026397ee0605dd88aab359c4c1dea4cc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 22 Dec 2020 10:51:40 +0100 Subject: better gcc and clang support for afl-cc --- docs/Changelog.md | 3 +- docs/env_variables.md | 2 + src/afl-cc.c | 134 ++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 123 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index e36e4e9f..cf9bfbe1 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -20,7 +20,8 @@ sending a mail to . - fixed endless loop for allow/blocklist lines starting with a comment (thanks to Zherya for reporting) - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard - support (less performant than our own) + support (less performant than our own), GCC for old afl-gcc and + CLANG for old afl-clang - added dummy Makefile to instrumentation/ diff --git a/docs/env_variables.md b/docs/env_variables.md index c1693748..e6b9381b 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -120,6 +120,8 @@ Then there are a few specific features that are only available in instrumentatio LTO - LTO instrumentation (see below) CTX - context sensitive instrumentation (see below) NGRAM-x - deeper previous location coverage (from NGRAM-2 up to NGRAM-16) + GCC - outdated gcc instrumentation + CLANG - outdated clang instrumentation In CLASSIC (default) and CFG/INSTRIM you can also specify CTX and/or NGRAM, seperate the options with a comma "," then, e.g.: `AFL_LLVM_INSTRUMENT=CFG,CTX,NGRAM-4` diff --git a/src/afl-cc.c b/src/afl-cc.c index 3b8092a9..a18f87db 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -62,7 +62,7 @@ u8 use_stdin; /* dummy */ enum { - INSTURMENT_DEFAULT = 0, + INSTRUMENT_DEFAULT = 0, INSTRUMENT_CLASSIC = 1, INSTRUMENT_AFL = 1, INSTRUMENT_PCGUARD = 2, @@ -70,6 +70,8 @@ enum { INSTRUMENT_CFG = 3, INSTRUMENT_LTO = 4, INSTRUMENT_LLVMNATIVE = 5, + INSTRUMENT_GCC = 6, + INSTRUMENT_CLANG = 7, INSTRUMENT_OPT_CTX = 8, INSTRUMENT_OPT_NGRAM = 16 @@ -77,9 +79,24 @@ enum { char instrument_mode_string[18][18] = { - "DEFAULT", "CLASSIC", "PCGUARD", "CFG", "LTO", "", "PCGUARD-NATIVE", - "", "CTX", "", "", "", "", "", - "", "", "NGRAM", "" + "DEFAULT", + "CLASSIC", + "PCGUARD", + "CFG", + "LTO", + "PCGUARD-NATIVE", + "GCC", + "CLANG", + "CTX", + "", + "", + "", + "", + "", + "", + "", + "NGRAM", + "" }; @@ -89,14 +106,15 @@ enum { LTO = 1, LLVM = 2, GCC_PLUGIN = 3, - GCC = 4 + GCC = 4, + CLANG = 5 }; -char compiler_mode_string[6][12] = { +char compiler_mode_string[7][12] = { "AUTOSELECT", "LLVM-LTO", "LLVM", "GCC_PLUGIN", - "GCC", "" + "GCC", "CLANG", "" }; @@ -324,6 +342,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { alt_cxx = clang_mode ? "clang++" : "g++"; + } else if (compiler_mode == CLANG) { + + alt_cxx = "clang++"; + } else { alt_cxx = "g++"; @@ -357,6 +379,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { alt_cc = clang_mode ? "clang" : "gcc"; + } else if (compiler_mode == CLANG) { + + alt_cc = "clang"; + } else { alt_cc = "gcc"; @@ -380,12 +406,16 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (compiler_mode == GCC) { + if (compiler_mode == GCC || compiler_mode == CLANG) { cc_params[cc_par_cnt++] = "-B"; cc_params[cc_par_cnt++] = obj_path; - if (clang_mode) { cc_params[cc_par_cnt++] = "-no-integrated-as"; } + if (clang_mode || compiler_mode == CLANG) { + + cc_params[cc_par_cnt++] = "-no-integrated-as"; + + } } @@ -996,12 +1026,14 @@ int main(int argc, char **argv, char **envp) { } else if (strncmp(callname, "afl-gcc", 7) == 0 || - strncmp(callname, "afl-g++", 7) == 0 || - - strncmp(callname, "afl-clang", 9) == 0) { + strncmp(callname, "afl-g++", 7) == 0) { compiler_mode = GCC; + } else if (strncmp(callname, "afl-clang", 9) == 0) { + + compiler_mode = CLANG; + } if ((ptr = getenv("AFL_CC_COMPILER"))) { @@ -1045,6 +1077,7 @@ int main(int argc, char **argv, char **envp) { if (strncmp(callname, "afl-clang", 9) == 0) { clang_mode = 1; + compiler_mode = CLANG; if (strncmp(callname, "afl-clang++", 11) == 0) { plusplus_mode = 1; } @@ -1072,6 +1105,34 @@ int main(int argc, char **argv, char **envp) { compiler_mode = LLVM; + } else if (strncasecmp(ptr, "PCGUARD", 7) == 0 || + + strncasecmp(ptr, "PC-GUARD", 8) == 0) { + + compiler_mode = LLVM; + instrument_mode = INSTRUMENT_PCGUARD; + + } else if (strcasecmp(ptr, "INSTRIM") == 0 || + + strcasecmp(ptr, "CFG") == 0) { + + compiler_mode = LLVM; + instrument_mode = INSTRUMENT_CFG; + + } else if (strcasecmp(ptr, "AFL") == 0 || + + strcasecmp(ptr, "CLASSIC") == 0) { + + compiler_mode = LLVM; + instrument_mode = INSTRUMENT_CLASSIC; + + } else if (strcasecmp(ptr, "LLVMNATIVE") == 0 || + + strcasecmp(ptr, "LLVM-NATIVE") == 0) { + + compiler_mode = LLVM; + instrument_mode = INSTRUMENT_LLVMNATIVE; + } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || strncasecmp(ptr, "GCC-P", 5) == 0 || @@ -1083,6 +1144,10 @@ int main(int argc, char **argv, char **envp) { compiler_mode = GCC; + } else if (strcasecmp(ptr, "CLANG") == 0) { + + compiler_mode = CLANG; + } else FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]); @@ -1212,6 +1277,28 @@ int main(int argc, char **argv, char **envp) { } + if (strcasecmp(ptr, "gcc") == 0) { + + if (!instrument_mode || instrument_mode == INSTRUMENT_GCC) + instrument_mode = INSTRUMENT_GCC; + else if (instrument_mode != INSTRUMENT_GCC) + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + compiler_mode = GCC; + + } + + if (strcasecmp(ptr, "clang") == 0) { + + if (!instrument_mode || instrument_mode == INSTRUMENT_CLANG) + instrument_mode = INSTRUMENT_CLANG; + else if (instrument_mode != INSTRUMENT_CLANG) + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + compiler_mode = CLANG; + + } + if (strncasecmp(ptr, "ctx", strlen("ctx")) == 0) { instrument_opt_mode |= INSTRUMENT_OPT_CTX; @@ -1270,6 +1357,22 @@ int main(int argc, char **argv, char **envp) { } + if (compiler_mode == GCC) { + + if (clang_mode) { + + instrument_mode = CLANG; + + } else { + + instrument_mode = GCC; + + } + + } + + if (compiler_mode == CLANG) { instrument_mode = CLANG; } + if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) { printf("afl-cc" VERSION @@ -1316,7 +1419,7 @@ int main(int argc, char **argv, char **envp) { " [GCC_PLUGIN] gcc plugin: %s%s\n" " CLASSIC DEFAULT no yes yes no no no " " yes\n" - " [GCC] simple gcc: %s%s\n" + " [GCC/CLANG] simple gcc/clang: %s%s\n" " CLASSIC DEFAULT no no no no no no " " no\n\n", have_lto ? "AVAILABLE" : "unavailable!", @@ -1328,7 +1431,7 @@ int main(int argc, char **argv, char **envp) { have_gcc_plugin ? "AVAILABLE" : "unavailable!", compiler_mode == GCC_PLUGIN ? " [SELECTED]" : "", have_gcc ? "AVAILABLE" : "unavailable!", - compiler_mode == GCC ? " [SELECTED]" : ""); + (compiler_mode == GCC || compiler_mode == CLANG) ? " [SELECTED]" : ""); SAYF( "Modes:\n" @@ -1445,7 +1548,8 @@ int main(int argc, char **argv, char **envp) { " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen " "mutator)\n" " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" - " CLASSIC, INSTRIM, PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-16\n" + " CLASSIC, INSTRIM, PCGUARD, LTO, GCC, CLANG, CTX, NGRAM-2 ... " + "NGRAM-16\n" " You can also use the old environment variables instead:\n" " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n" " AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n" -- cgit 1.4.1 From 9759320266d3f334f71d06eed5267d78de1837d8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 22 Dec 2020 12:33:58 +0100 Subject: afl-clang-fast fix --- src/afl-cc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index a18f87db..66f4860f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1030,7 +1030,9 @@ int main(int argc, char **argv, char **envp) { compiler_mode = GCC; - } else if (strncmp(callname, "afl-clang", 9) == 0) { + } else if (strncmp(callname, "afl-clang", 9) == 0 && + + strstr(callname, "fast") == NULL) { compiler_mode = CLANG; @@ -1074,7 +1076,8 @@ int main(int argc, char **argv, char **envp) { } - if (strncmp(callname, "afl-clang", 9) == 0) { + if (strncmp(callname, "afl-clang", 9) == 0 && + strstr(callname, "fast") == NULL) { clang_mode = 1; compiler_mode = CLANG; @@ -1144,7 +1147,7 @@ int main(int argc, char **argv, char **envp) { compiler_mode = GCC; - } else if (strcasecmp(ptr, "CLANG") == 0) { + } else if (strncasecmp(ptr, "CLANG", 5) == 0) { compiler_mode = CLANG; -- cgit 1.4.1 From 2a994e457a75c28272373ba24cd4158239c007fd Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 22 Dec 2020 19:39:49 +0100 Subject: portability: avoid void * arithmetic (UB), avoid GNU extension for array initializations --- include/alloc-inl.h | 18 +++++++++--------- src/afl-fuzz-bitmap.c | 43 +++++++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 3044b7a0..8a91d196 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -271,7 +271,7 @@ static inline void *DFL_ck_alloc_nozero(u32 size) { ret = malloc(size + ALLOC_OFF_TOTAL); ALLOC_CHECK_RESULT(ret, size); - ret += ALLOC_OFF_HEAD; + ret = (char *)ret + ALLOC_OFF_HEAD; ALLOC_C1(ret) = ALLOC_MAGIC_C1; ALLOC_S(ret) = size; @@ -311,7 +311,7 @@ static inline void DFL_ck_free(void *mem) { ALLOC_C1(mem) = ALLOC_MAGIC_F; - free(mem - ALLOC_OFF_HEAD); + free((char *)mem - ALLOC_OFF_HEAD); } @@ -340,7 +340,7 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) { #endif /* !DEBUG_BUILD */ old_size = ALLOC_S(orig); - orig -= ALLOC_OFF_HEAD; + orig = (char *)orig - ALLOC_OFF_HEAD; ALLOC_CHECK_SIZE(old_size); @@ -363,10 +363,10 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) { if (orig) { - memcpy(ret + ALLOC_OFF_HEAD, orig + ALLOC_OFF_HEAD, MIN(size, old_size)); - memset(orig + ALLOC_OFF_HEAD, 0xFF, old_size); + memcpy((char *)ret + ALLOC_OFF_HEAD, (char *)orig + ALLOC_OFF_HEAD, MIN(size, old_size)); + memset((char *)orig + ALLOC_OFF_HEAD, 0xFF, old_size); - ALLOC_C1(orig + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F; + ALLOC_C1((char *)orig + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F; free(orig); @@ -374,13 +374,13 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) { #endif /* ^!DEBUG_BUILD */ - ret += ALLOC_OFF_HEAD; + ret = (char *)ret + ALLOC_OFF_HEAD; ALLOC_C1(ret) = ALLOC_MAGIC_C1; ALLOC_S(ret) = size; ALLOC_C2(ret) = ALLOC_MAGIC_C2; - if (size > old_size) memset(ret + old_size, 0, size - old_size); + if (size > old_size) memset((char *)ret + old_size, 0, size - old_size); return ret; @@ -401,7 +401,7 @@ static inline u8 *DFL_ck_strdup(u8 *str) { ret = malloc(size + ALLOC_OFF_TOTAL); ALLOC_CHECK_RESULT(ret, size); - ret += ALLOC_OFF_HEAD; + ret = (char *)ret + ALLOC_OFF_HEAD; ALLOC_C1(ret) = ALLOC_MAGIC_C1; ALLOC_S(ret) = size; diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 738ba986..a17478f0 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -97,10 +97,10 @@ u32 count_bytes(afl_state_t *afl, u8 *mem) { u32 v = *(ptr++); if (!v) { continue; } - if (v & 0x000000ff) { ++ret; } - if (v & 0x0000ff00) { ++ret; } - if (v & 0x00ff0000) { ++ret; } - if (v & 0xff000000) { ++ret; } + if (v & 0x000000ffU) { ++ret; } + if (v & 0x0000ff00U) { ++ret; } + if (v & 0x00ff0000U) { ++ret; } + if (v & 0xff000000U) { ++ret; } } @@ -124,11 +124,11 @@ u32 count_non_255_bytes(afl_state_t *afl, u8 *mem) { /* This is called on the virgin bitmap, so optimize for the most likely case. */ - if (v == 0xffffffff) { continue; } - if ((v & 0x000000ff) != 0x000000ff) { ++ret; } - if ((v & 0x0000ff00) != 0x0000ff00) { ++ret; } - if ((v & 0x00ff0000) != 0x00ff0000) { ++ret; } - if ((v & 0xff000000) != 0xff000000) { ++ret; } + if (v == 0xffffffffU) { continue; } + if ((v & 0x000000ffU) != 0x000000ffU) { ++ret; } + if ((v & 0x0000ff00U) != 0x0000ff00U) { ++ret; } + if ((v & 0x00ff0000U) != 0x00ff0000U) { ++ret; } + if ((v & 0xff000000U) != 0xff000000U) { ++ret; } } @@ -140,10 +140,15 @@ u32 count_non_255_bytes(afl_state_t *afl, u8 *mem) { and replacing it with 0x80 or 0x01 depending on whether the tuple is hit or not. Called on every new crash or timeout, should be reasonably fast. */ - +#define TIMES4(x) x,x,x,x +#define TIMES8(x) TIMES4(x),TIMES4(x) +#define TIMES16(x) TIMES8(x),TIMES8(x) +#define TIMES32(x) TIMES16(x),TIMES16(x) +#define TIMES64(x) TIMES32(x),TIMES32(x) +#define TIMES255(x) TIMES64(x),TIMES64(x),TIMES64(x),TIMES32(x),TIMES16(x),TIMES8(x),TIMES4(x),x,x,x const u8 simplify_lookup[256] = { - [0] = 1, [1 ... 255] = 128 + [0] = 1, [1] = TIMES255(128) }; @@ -157,13 +162,19 @@ const u8 count_class_lookup8[256] = { [1] = 1, [2] = 2, [3] = 4, - [4 ... 7] = 8, - [8 ... 15] = 16, - [16 ... 31] = 32, - [32 ... 127] = 64, - [128 ... 255] = 128 + [4] = TIMES4(8), + [8] = TIMES8(16), + [16] = TIMES16(32), + [32] = TIMES32(64), + [128] = TIMES64(128) }; +#undef TIMES255 +#undef TIMES64 +#undef TIMES32 +#undef TIMES16 +#undef TIMES8 +#undef TIMES4 u16 count_class_lookup16[65536]; -- cgit 1.4.1 From 8241ded12ecdc4a28d3a99c37ac8cb420f724a86 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 22 Dec 2020 20:19:43 +0100 Subject: more portability non std array initializers --- src/afl-showmap.c | 28 +++++++++++++++++++++------- src/afl-tmin.c | 20 +++++++++++++++----- 2 files changed, 36 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 34a4f30d..b891632a 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -98,11 +98,18 @@ static sharedmem_t * shm_fuzz; /* Classify tuple counts. Instead of mapping to individual bits, as in afl-fuzz.c, we map to more user-friendly numbers between 1 and 8. */ +#define TIMES4(x) x,x,x,x +#define TIMES8(x) TIMES4(x),TIMES4(x) +#define TIMES16(x) TIMES8(x),TIMES8(x) +#define TIMES32(x) TIMES16(x),TIMES16(x) +#define TIMES64(x) TIMES32(x),TIMES32(x) +#define TIMES96(x) TIMES64(x),TIMES32(x) +#define TIMES128(x) TIMES64(x),TIMES64(x) static const u8 count_class_human[256] = { [0] = 0, [1] = 1, [2] = 2, [3] = 3, - [4 ... 7] = 4, [8 ... 15] = 5, [16 ... 31] = 6, [32 ... 127] = 7, - [128 ... 255] = 8 + [4] = TIMES4(4), [8] = TIMES8(5),[16] = TIMES16(6),[32] = TIMES96(7), + [128] = TIMES128(8) }; @@ -112,13 +119,20 @@ static const u8 count_class_binary[256] = { [1] = 1, [2] = 2, [3] = 4, - [4 ... 7] = 8, - [8 ... 15] = 16, - [16 ... 31] = 32, - [32 ... 127] = 64, - [128 ... 255] = 128 + [4] = TIMES4(8), + [8] = TIMES8(16), + [16] = TIMES16(32), + [32] = TIMES32(64), + [128] = TIMES64(128) }; +#undef TIMES128 +#undef TIMES96 +#undef TIMES64 +#undef TIMES32 +#undef TIMES16 +#undef TIMES8 +#undef TIMES4 static void classify_counts(afl_forkserver_t *fsrv) { diff --git a/src/afl-tmin.c b/src/afl-tmin.c index b9045551..6cb0d458 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -98,19 +98,29 @@ static sharedmem_t * shm_fuzz; /* Classify tuple counts. This is a slow & naive version, but good enough here. */ +#define TIMES4(x) x,x,x,x +#define TIMES8(x) TIMES4(x),TIMES4(x) +#define TIMES16(x) TIMES8(x),TIMES8(x) +#define TIMES32(x) TIMES16(x),TIMES16(x) +#define TIMES64(x) TIMES32(x),TIMES32(x) static const u8 count_class_lookup[256] = { [0] = 0, [1] = 1, [2] = 2, [3] = 4, - [4 ... 7] = 8, - [8 ... 15] = 16, - [16 ... 31] = 32, - [32 ... 127] = 64, - [128 ... 255] = 128 + [4] = TIMES4(8), + [8] = TIMES8(16), + [16] = TIMES16(32), + [32] = TIMES32(64), + [128] = TIMES64(128) }; +#undef TIMES64 +#undef TIMES32 +#undef TIMES16 +#undef TIMES8 +#undef TIMES4 static sharedmem_t *deinit_shmem(afl_forkserver_t *fsrv, sharedmem_t * shm_fuzz) { -- cgit 1.4.1 From e790667fd2992ab867382a0946ea7337e01329fa Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 22 Dec 2020 20:29:22 +0100 Subject: one more with nonstd array initializers --- src/afl-analyze.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/afl-analyze.c b/src/afl-analyze.c index 2780deff..a6825ef6 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -103,19 +103,29 @@ static u32 map_size = MAP_SIZE; /* Classify tuple counts. This is a slow & naive version, but good enough here. */ +#define TIMES4(x) x,x,x,x +#define TIMES8(x) TIMES4(x),TIMES4(x) +#define TIMES16(x) TIMES8(x),TIMES8(x) +#define TIMES32(x) TIMES16(x),TIMES16(x) +#define TIMES64(x) TIMES32(x),TIMES32(x) static u8 count_class_lookup[256] = { [0] = 0, [1] = 1, [2] = 2, [3] = 4, - [4 ... 7] = 8, - [8 ... 15] = 16, - [16 ... 31] = 32, - [32 ... 127] = 64, - [128 ... 255] = 128 + [4] = TIMES4(8), + [8] = TIMES8(16), + [16] = TIMES16(32), + [32] = TIMES32(64), + [128] = TIMES64(128) }; +#undef TIMES64 +#undef TIMES32 +#undef TIMES16 +#undef TIMES8 +#undef TIMES4 static void classify_counts(u8 *mem) { -- cgit 1.4.1 From 9a5ea5b5c235b236a9c026230133fc2ad014f89d Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 22 Dec 2020 20:33:06 +0100 Subject: workaround for Solaris: NAME_MAX is not defined --- src/afl-fuzz-bitmap.c | 3 +++ src/afl-fuzz-run.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'src') diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index a17478f0..1cb9b15f 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -25,6 +25,9 @@ #include "afl-fuzz.h" #include +#if !defined NAME_MAX +#define NAME_MAX _XOPEN_NAME_MAX +#endif /* Write bitmap to file. The bitmap is useful mostly for the secret -B option, to focus a separate fuzzing session on a particular diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 60086bd6..32cca579 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -28,6 +28,9 @@ #include #include #include +#if !defined NAME_MAX +#define NAME_MAX _XOPEN_NAME_MAX +#endif #include "cmplog.h" -- cgit 1.4.1 From 03849d147a69cf627746a8ad5f1b653367a56ff5 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 23 Dec 2020 17:56:39 +0100 Subject: warn on _AFL and __AFL env vars --- include/alloc-inl.h | 3 ++- src/afl-analyze.c | 11 ++++++----- src/afl-common.c | 4 +++- src/afl-fuzz-bitmap.c | 17 ++++++++++------- src/afl-fuzz-run.c | 2 +- src/afl-showmap.c | 25 ++++++++++++++++--------- src/afl-tmin.c | 11 ++++++----- 7 files changed, 44 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 8a91d196..c914da5f 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -363,7 +363,8 @@ static inline void *DFL_ck_realloc(void *orig, u32 size) { if (orig) { - memcpy((char *)ret + ALLOC_OFF_HEAD, (char *)orig + ALLOC_OFF_HEAD, MIN(size, old_size)); + memcpy((char *)ret + ALLOC_OFF_HEAD, (char *)orig + ALLOC_OFF_HEAD, + MIN(size, old_size)); memset((char *)orig + ALLOC_OFF_HEAD, 0xFF, old_size); ALLOC_C1((char *)orig + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F; diff --git a/src/afl-analyze.c b/src/afl-analyze.c index a6825ef6..6dac415b 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -103,11 +103,11 @@ static u32 map_size = MAP_SIZE; /* Classify tuple counts. This is a slow & naive version, but good enough here. */ -#define TIMES4(x) x,x,x,x -#define TIMES8(x) TIMES4(x),TIMES4(x) -#define TIMES16(x) TIMES8(x),TIMES8(x) -#define TIMES32(x) TIMES16(x),TIMES16(x) -#define TIMES64(x) TIMES32(x),TIMES32(x) +#define TIMES4(x) x, x, x, x +#define TIMES8(x) TIMES4(x), TIMES4(x) +#define TIMES16(x) TIMES8(x), TIMES8(x) +#define TIMES32(x) TIMES16(x), TIMES16(x) +#define TIMES64(x) TIMES32(x), TIMES32(x) static u8 count_class_lookup[256] = { [0] = 0, @@ -121,6 +121,7 @@ static u8 count_class_lookup[256] = { [128] = TIMES64(128) }; + #undef TIMES64 #undef TIMES32 #undef TIMES16 diff --git a/src/afl-common.c b/src/afl-common.c index 6dc8abe0..7914f83a 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -432,7 +432,9 @@ void check_environment_vars(char **envp) { char *env, *val; while ((env = envp[index++]) != NULL) { - if (strncmp(env, "ALF_", 4) == 0) { + if (strncmp(env, "ALF_", 4) == 0 || strncmp(env, "_ALF", 4) == 0 || + strncmp(env, "__ALF", 5) == 0 || strncmp(env, "_AFL", 4) == 0 || + strncmp(env, "__AFL", 5) == 0) { WARNF("Potentially mistyped AFL environment variable: %s", env); issue_detected = 1; diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 1cb9b15f..62a8211c 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -26,7 +26,7 @@ #include "afl-fuzz.h" #include #if !defined NAME_MAX -#define NAME_MAX _XOPEN_NAME_MAX + #define NAME_MAX _XOPEN_NAME_MAX #endif /* Write bitmap to file. The bitmap is useful mostly for the secret @@ -143,12 +143,14 @@ u32 count_non_255_bytes(afl_state_t *afl, u8 *mem) { and replacing it with 0x80 or 0x01 depending on whether the tuple is hit or not. Called on every new crash or timeout, should be reasonably fast. */ -#define TIMES4(x) x,x,x,x -#define TIMES8(x) TIMES4(x),TIMES4(x) -#define TIMES16(x) TIMES8(x),TIMES8(x) -#define TIMES32(x) TIMES16(x),TIMES16(x) -#define TIMES64(x) TIMES32(x),TIMES32(x) -#define TIMES255(x) TIMES64(x),TIMES64(x),TIMES64(x),TIMES32(x),TIMES16(x),TIMES8(x),TIMES4(x),x,x,x +#define TIMES4(x) x, x, x, x +#define TIMES8(x) TIMES4(x), TIMES4(x) +#define TIMES16(x) TIMES8(x), TIMES8(x) +#define TIMES32(x) TIMES16(x), TIMES16(x) +#define TIMES64(x) TIMES32(x), TIMES32(x) +#define TIMES255(x) \ + TIMES64(x), TIMES64(x), TIMES64(x), TIMES32(x), TIMES16(x), TIMES8(x), \ + TIMES4(x), x, x, x const u8 simplify_lookup[256] = { [0] = 1, [1] = TIMES255(128) @@ -172,6 +174,7 @@ const u8 count_class_lookup8[256] = { [128] = TIMES64(128) }; + #undef TIMES255 #undef TIMES64 #undef TIMES32 diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 32cca579..d53ba546 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -29,7 +29,7 @@ #include #include #if !defined NAME_MAX -#define NAME_MAX _XOPEN_NAME_MAX + #define NAME_MAX _XOPEN_NAME_MAX #endif #include "cmplog.h" diff --git a/src/afl-showmap.c b/src/afl-showmap.c index b891632a..355b2dc3 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -98,17 +98,23 @@ static sharedmem_t * shm_fuzz; /* Classify tuple counts. Instead of mapping to individual bits, as in afl-fuzz.c, we map to more user-friendly numbers between 1 and 8. */ -#define TIMES4(x) x,x,x,x -#define TIMES8(x) TIMES4(x),TIMES4(x) -#define TIMES16(x) TIMES8(x),TIMES8(x) -#define TIMES32(x) TIMES16(x),TIMES16(x) -#define TIMES64(x) TIMES32(x),TIMES32(x) -#define TIMES96(x) TIMES64(x),TIMES32(x) -#define TIMES128(x) TIMES64(x),TIMES64(x) +#define TIMES4(x) x, x, x, x +#define TIMES8(x) TIMES4(x), TIMES4(x) +#define TIMES16(x) TIMES8(x), TIMES8(x) +#define TIMES32(x) TIMES16(x), TIMES16(x) +#define TIMES64(x) TIMES32(x), TIMES32(x) +#define TIMES96(x) TIMES64(x), TIMES32(x) +#define TIMES128(x) TIMES64(x), TIMES64(x) static const u8 count_class_human[256] = { - [0] = 0, [1] = 1, [2] = 2, [3] = 3, - [4] = TIMES4(4), [8] = TIMES8(5),[16] = TIMES16(6),[32] = TIMES96(7), + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 3, + [4] = TIMES4(4), + [8] = TIMES8(5), + [16] = TIMES16(6), + [32] = TIMES96(7), [128] = TIMES128(8) }; @@ -126,6 +132,7 @@ static const u8 count_class_binary[256] = { [128] = TIMES64(128) }; + #undef TIMES128 #undef TIMES96 #undef TIMES64 diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 6cb0d458..ed928c7c 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -98,11 +98,11 @@ static sharedmem_t * shm_fuzz; /* Classify tuple counts. This is a slow & naive version, but good enough here. */ -#define TIMES4(x) x,x,x,x -#define TIMES8(x) TIMES4(x),TIMES4(x) -#define TIMES16(x) TIMES8(x),TIMES8(x) -#define TIMES32(x) TIMES16(x),TIMES16(x) -#define TIMES64(x) TIMES32(x),TIMES32(x) +#define TIMES4(x) x, x, x, x +#define TIMES8(x) TIMES4(x), TIMES4(x) +#define TIMES16(x) TIMES8(x), TIMES8(x) +#define TIMES32(x) TIMES16(x), TIMES16(x) +#define TIMES64(x) TIMES32(x), TIMES32(x) static const u8 count_class_lookup[256] = { [0] = 0, @@ -116,6 +116,7 @@ static const u8 count_class_lookup[256] = { [128] = TIMES64(128) }; + #undef TIMES64 #undef TIMES32 #undef TIMES16 -- cgit 1.4.1 From a4fd4ea0f46529feb09577a13cc7c053fb22146f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 25 Dec 2020 12:13:45 +0100 Subject: fix LTO --- GNUmakefile | 6 +++--- src/afl-cc.c | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index b9c806c3..db2ad572 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -307,7 +307,7 @@ all: test_x86 test_shm test_python ready $(PROGS) afl-as llvm gcc_plugin test_bu .PHONY: llvm llvm: - -$(MAKE) -j4 -f GNUmakefile.llvm + -$(MAKE) -j -f GNUmakefile.llvm @test -e afl-cc || { echo "[-] Compiling afl-cc failed. You seem not to have a working compiler." ; exit 1; } .PHONY: gcc_plugin @@ -579,7 +579,7 @@ deepclean: clean .PHONY: distrib distrib: all - -$(MAKE) -j4 -f GNUmakefile.llvm + -$(MAKE) -j -f GNUmakefile.llvm -$(MAKE) -f GNUmakefile.gcc_plugin $(MAKE) -C utils/libdislocator $(MAKE) -C utils/libtokencap @@ -602,7 +602,7 @@ binary-only: test_shm test_python ready $(PROGS) .PHONY: source-only source-only: all - -$(MAKE) -j4 -f GNUmakefile.llvm + -$(MAKE) -j -f GNUmakefile.llvm -$(MAKE) -f GNUmakefile.gcc_plugin $(MAKE) -C utils/libdislocator $(MAKE) -C utils/libtokencap diff --git a/src/afl-cc.c b/src/afl-cc.c index 66f4860f..00e9cfce 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1030,9 +1030,9 @@ int main(int argc, char **argv, char **envp) { compiler_mode = GCC; - } else if (strncmp(callname, "afl-clang", 9) == 0 && + } else if (strcmp(callname, "afl-clang") == 0 || - strstr(callname, "fast") == NULL) { + strcmp(callname, "afl-clang++") == 0) { compiler_mode = CLANG; @@ -1076,13 +1076,13 @@ int main(int argc, char **argv, char **envp) { } - if (strncmp(callname, "afl-clang", 9) == 0 && - strstr(callname, "fast") == NULL) { + if (strcmp(callname, "afl-clang") == 0 || + strcmp(callname, "afl-clang++") == 0) { clang_mode = 1; compiler_mode = CLANG; - if (strncmp(callname, "afl-clang++", 11) == 0) { plusplus_mode = 1; } + if (strcmp(callname, "afl-clang++") == 0) { plusplus_mode = 1; } } -- cgit 1.4.1 From 0b9ca807f2a5e1ef6f43a8cb95356ed21ba3a3e5 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 26 Dec 2020 13:15:05 +0100 Subject: fix exec/s display --- src/afl-common.c | 8 ++++++-- src/afl-fuzz-state.c | 2 +- src/afl-fuzz-stats.c | 35 +++++++++++++++++++++-------------- 3 files changed, 28 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/afl-common.c b/src/afl-common.c index 7914f83a..1928663d 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -630,6 +630,10 @@ u8 *stringify_float(u8 *buf, size_t len, double val) { snprintf(buf, len, "%0.01f", val); + } else if (unlikely(isnan(val) || isinf(val))) { + + strcpy(buf, "inf"); + } else { stringify_int(buf, len, (u64)val); @@ -789,9 +793,9 @@ u8 *u_stringify_float(u8 *buf, double val) { sprintf(buf, "%0.01f", val); - } else if (unlikely(isnan(val) || isfinite(val))) { + } else if (unlikely(isnan(val) || isinf(val))) { - strcpy(buf, "999.9"); + strcpy(buf, "infinite"); } else { diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 7053572b..34456c0d 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -100,7 +100,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->cal_cycles_long = CAL_CYCLES_LONG; afl->hang_tmout = EXEC_TIMEOUT; afl->stats_update_freq = 1; - afl->stats_avg_exec = -1; + afl->stats_avg_exec = 0; afl->skip_deterministic = 1; #ifndef NO_SPLICING afl->use_splicing = 1; diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 50e2ef15..cb0d3dcd 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -369,30 +369,37 @@ void show_stats(afl_state_t *afl) { /* Calculate smoothed exec speed stats. */ - if (!afl->stats_last_execs) { + if (unlikely(!afl->stats_last_execs)) { - if (unlikely(cur_ms == afl->start_time)) --afl->start_time; + if (likely(cur_ms != afl->start_time)) { - afl->stats_avg_exec = - ((double)afl->fsrv.total_execs) * 1000 / (cur_ms - afl->start_time); + afl->stats_avg_exec = + ((double)afl->fsrv.total_execs) * 1000 / (cur_ms - afl->start_time); + + } } else { - double cur_avg = ((double)(afl->fsrv.total_execs - afl->stats_last_execs)) * - 1000 / (cur_ms - afl->stats_last_ms); + if (likely(cur_ms != afl->stats_last_ms)) { - /* If there is a dramatic (5x+) jump in speed, reset the indicator - more quickly. */ + double cur_avg = + ((double)(afl->fsrv.total_execs - afl->stats_last_execs)) * 1000 / + (cur_ms - afl->stats_last_ms); - if (cur_avg * 5 < afl->stats_avg_exec || - cur_avg / 5 > afl->stats_avg_exec) { + /* If there is a dramatic (5x+) jump in speed, reset the indicator + more quickly. */ - afl->stats_avg_exec = cur_avg; + if (cur_avg * 5 < afl->stats_avg_exec || + cur_avg / 5 > afl->stats_avg_exec) { - } + afl->stats_avg_exec = cur_avg; - afl->stats_avg_exec = afl->stats_avg_exec * (1.0 - 1.0 / AVG_SMOOTHING) + - cur_avg * (1.0 / AVG_SMOOTHING); + } + + afl->stats_avg_exec = afl->stats_avg_exec * (1.0 - 1.0 / AVG_SMOOTHING) + + cur_avg * (1.0 / AVG_SMOOTHING); + + } } -- cgit 1.4.1 From 4af0065f4a7518f7df8f8b49c40127e2e674a760 Mon Sep 17 00:00:00 2001 From: David Manouchehri Date: Sat, 26 Dec 2020 14:23:59 -0500 Subject: Fix missing include for macOS. (#660) --- src/afl-ld-lto.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index 16feaa80..19491036 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -45,6 +45,10 @@ #include +#ifdef __APPLE__ +#include +#endif + #define MAX_PARAM_COUNT 4096 static u8 **ld_params; /* Parameters passed to the real 'ld' */ -- cgit 1.4.1 From 8e2b59ffcab0102e3bebe0c4ed64cb9d36de5559 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 26 Dec 2020 23:04:21 +0100 Subject: more flexible system support for afl-frida --- src/afl-ld-lto.c | 2 +- utils/afl_frida/afl-frida.c | 227 +++----------------------------------------- 2 files changed, 16 insertions(+), 213 deletions(-) (limited to 'src') diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index 19491036..fccdb1a5 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -46,7 +46,7 @@ #include #ifdef __APPLE__ -#include + #include #endif #define MAX_PARAM_COUNT 4096 diff --git a/utils/afl_frida/afl-frida.c b/utils/afl_frida/afl-frida.c index c8ea656b..b5b8196d 100644 --- a/utils/afl_frida/afl-frida.c +++ b/utils/afl_frida/afl-frida.c @@ -143,197 +143,6 @@ void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, } -typedef struct library_list { - - uint8_t *name; - uint64_t addr_start, addr_end; - -} library_list_t; - -#define MAX_LIB_COUNT 256 -static library_list_t liblist[MAX_LIB_COUNT]; -static u32 liblist_cnt; - -void read_library_information() { - -#if defined(__linux__) - FILE *f; - u8 buf[1024], *b, *m, *e, *n; - - if ((f = fopen("/proc/self/maps", "r")) == NULL) { - - fprintf(stderr, "Error: cannot open /proc/self/maps\n"); - exit(-1); - - } - - if (debug) fprintf(stderr, "Library list:\n"); - while (fgets(buf, sizeof(buf), f)) { - - if (strstr(buf, " r-x")) { - - if (liblist_cnt >= MAX_LIB_COUNT) { - - fprintf( - stderr, - "Warning: too many libraries to old, maximum count of %d reached\n", - liblist_cnt); - return; - - } - - b = buf; - m = index(buf, '-'); - e = index(buf, ' '); - if ((n = rindex(buf, '/')) == NULL) n = rindex(buf, ' '); - if (n && - ((*n >= '0' && *n <= '9') || *n == '[' || *n == '{' || *n == '(')) - n = NULL; - else - n++; - if (b && m && e && n && *n) { - - *m++ = 0; - *e = 0; - if (n[strlen(n) - 1] == '\n') n[strlen(n) - 1] = 0; - - if (rindex(n, '/') != NULL) { - - n = rindex(n, '/'); - n++; - - } - - liblist[liblist_cnt].name = strdup(n); - liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16); - liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16); - if (debug) - fprintf( - stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name, - liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_end - 1); - liblist_cnt++; - - } - - } - - } - - if (debug) fprintf(stderr, "\n"); - -#elif defined(__FreeBSD__) - int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; - char * buf, *start, *end; - size_t miblen = sizeof(mib) / sizeof(mib[0]); - size_t len; - - if (debug) fprintf(stderr, "Library list:\n"); - if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) { return; } - - len = len * 4 / 3; - - buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); - if (buf == MAP_FAILED) { return; } - if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) { - - munmap(buf, len); - return; - - } - - start = buf; - end = buf + len; - - while (start < end) { - - struct kinfo_vmentry *region = (struct kinfo_vmentry *)start; - size_t size = region->kve_structsize; - - if (size == 0) { break; } - - if ((region->kve_protection & KVME_PROT_READ) && - !(region->kve_protection & KVME_PROT_EXEC)) { - - liblist[liblist_cnt].name = - region->kve_path[0] != '\0' ? strdup(region->kve_path) : 0; - liblist[liblist_cnt].addr_start = region->kve_start; - liblist[liblist_cnt].addr_end = region->kve_end; - - if (debug) { - - fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name, - liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_end - 1); - - } - - liblist_cnt++; - - } - - start += size; - - } - -#endif - -} - -library_list_t *find_library(char *name) { - - char *filename = rindex(name, '/'); - - if (filename) - filename++; - else - filename = name; - -#if defined(__linux__) - u32 i; - for (i = 0; i < liblist_cnt; i++) - if (strcmp(liblist[i].name, filename) == 0) return &liblist[i]; -#elif defined(__APPLE__) && defined(__LP64__) - kern_return_t err; - static library_list_t lib; - - // get the list of all loaded modules from dyld - // the task_info mach API will get the address of the dyld all_image_info - // struct for the given task from which we can get the names and load - // addresses of all modules - task_dyld_info_data_t task_dyld_info; - mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; - err = task_info(mach_task_self(), TASK_DYLD_INFO, - (task_info_t)&task_dyld_info, &count); - - const struct dyld_all_image_infos *all_image_infos = - (const struct dyld_all_image_infos *)task_dyld_info.all_image_info_addr; - const struct dyld_image_info *image_infos = all_image_infos->infoArray; - - for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) { - - const char * image_name = image_infos[i].imageFilePath; - mach_vm_address_t image_load_address = - (mach_vm_address_t)image_infos[i].imageLoadAddress; - if (strstr(image_name, name)) { - - lib.name = name; - lib.addr_start = (u64)image_load_address; - lib.addr_end = 0; - return &lib; - - } - - } - -#endif - - return NULL; - -} - /* Because this CAN be called more than once, it will return the LAST range */ static int enumerate_ranges(const GumRangeDetails *details, gpointer user_data) { @@ -372,16 +181,6 @@ int main() { // END STEP 2 - read_library_information(); - library_list_t *lib = find_library(TARGET_LIBRARY); - - if (lib == NULL) { - - fprintf(stderr, "Could not find target library\n"); - exit(-1); - - } - gum_init_embedded(); if (!gum_stalker_is_supported()) { @@ -392,20 +191,24 @@ int main() { GumStalker *stalker = gum_stalker_new(); - /* - This does not work here as we load a shared library. pretty sure this - would also be easily solvable with frida gum, but I already have all the - code I need from afl-untracer - - GumAddress base_address = gum_module_find_base_address(TARGET_LIBRARY); + GumAddress base_address = gum_module_find_base_address(TARGET_LIBRARY); GumMemoryRange code_range; gum_module_enumerate_ranges(TARGET_LIBRARY, GUM_PAGE_RX, enumerate_ranges, &code_range); - guint64 code_start = code_range.base_address - base_address; - guint64 code_end = (code_range.base_address + code_range.size) - base_address; - range_t instr_range = {base_address, code_start, code_end}; - */ - range_t instr_range = {0, lib->addr_start, lib->addr_end}; + + guint64 code_start = code_range.base_address; + guint64 code_end = code_range.base_address + code_range.size; + range_t instr_range = {0, code_start, code_end}; + + printf("Frida instrumentation: base=0x%lx instrumenting=0x%lx-%lx\n", + base_address, code_start, code_end); + if (!code_start || !code_end) { + + fprintf(stderr, "Error: no valid memory address found for %s\n", + TARGET_LIBRARY); + exit(-1); + + } GumStalkerTransformer *transformer = gum_stalker_transformer_make_from_callback(instr_basic_block, -- cgit 1.4.1 From 688f4ffb89ebf41a497070e8fcf2927510b66874 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 28 Dec 2020 14:01:48 +0100 Subject: added corpus introspection --- docs/Changelog.md | 1 + include/afl-fuzz.h | 5 +++++ src/afl-fuzz-queue.c | 30 ++++++++++++++++++++++++++++++ src/afl-fuzz-run.c | 4 ++++ 4 files changed, 40 insertions(+) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index a8b8f0af..71846535 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,6 +10,7 @@ sending a mail to . ### Version ++3.01a (release) + - Mac OS ARM64 support - afl-fuzz - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index e2fb0344..57b0e6cc 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -174,6 +174,10 @@ struct queue_entry { u8 *trace_mini; /* Trace bytes, if kept */ u32 tc_ref; /* Trace bytes ref count */ +#ifdef INTROSPECTION + u32 bitsmap_size; +#endif + double perf_score, /* performance score */ weight; @@ -734,6 +738,7 @@ typedef struct afl_state { char mutation[8072]; char m_tmp[4096]; FILE *introspection_file; + u32 bitsmap_size; #endif } afl_state_t; diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 54afa17c..5dc2d70b 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -190,6 +190,32 @@ void create_alias_table(afl_state_t *afl) { while (nS) afl->alias_probability[S[--nS]] = 1; +#ifdef INTROSPECTION + u8 fn[PATH_MAX]; + snprintf(fn, PATH_MAX, "%s/introspection_corpus.txt", afl->out_dir); + FILE *f = fopen(fn, "a"); + if (f) { + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + fprintf( + f, + "entry=%u name=%s variable=%s disabled=%s len=%u exec_us=%u " + "bitmap_size=%u bitsmap_size=%u tops=%u weight=%f perf_score=%f\n", + i, q->fname, q->var_behavior ? "true" : "false", + q->disabled ? "true" : "false", q->len, (u32)q->exec_us, + q->bitmap_size, q->bitsmap_size, q->tc_ref, q->weight, q->perf_score); + + } + + fprintf(f, "\n"); + fclose(f); + + } + +#endif + /* fprintf(stderr, " entry alias probability perf_score filename\n"); for (u32 i = 0; i < n; ++i) @@ -398,6 +424,10 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { q->trace_mini = NULL; q->testcase_buf = NULL; +#ifdef INTROSPECTION + q->bitsmap_size = afl->bitsmap_size; +#endif + if (q->depth > afl->max_depth) { afl->max_depth = q->depth; } if (afl->queue_top) { diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index d53ba546..339fb9c3 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -380,6 +380,10 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } +#ifdef INTROSPECTION + if (unlikely(!q->bitsmap_size)) q->bitsmap_size = afl->bitsmap_size; +#endif + classify_counts(&afl->fsrv); cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); if (q->exec_cksum != cksum) { -- cgit 1.4.1 From d103e39f5866fa29a13e7e8d4204b1e962100369 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 28 Dec 2020 17:06:55 +0100 Subject: fix instrumentation type display --- src/afl-cc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 00e9cfce..758b958b 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1364,17 +1364,17 @@ int main(int argc, char **argv, char **envp) { if (clang_mode) { - instrument_mode = CLANG; + instrument_mode = INSTRUMENT_CLANG; } else { - instrument_mode = GCC; + instrument_mode = INSTRUMENT_GCC; } } - if (compiler_mode == CLANG) { instrument_mode = CLANG; } + if (compiler_mode == CLANG) { instrument_mode = INSTRUMENT_CLANG; } if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) { -- cgit 1.4.1 From 0922763db1df494c1547db296934a1d66286319f Mon Sep 17 00:00:00 2001 From: Marcel Böhme Date: Tue, 29 Dec 2020 14:57:05 +1100 Subject: Update afl-fuzz-queue.c --- src/afl-fuzz-queue.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 5dc2d70b..d4b35ad2 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -112,12 +112,8 @@ void create_alias_table(afl_state_t *afl) { struct queue_entry *q = afl->queue_buf[i]; - if (!q->disabled) { - - q->weight = compute_weight(afl, q, avg_exec_us, avg_bitmap_size); - q->perf_score = calculate_score(afl, q); - - } + q->weight = q->disabled ? 0 : compute_weight(afl, q, avg_exec_us, avg_bitmap_size); + q->perf_score = q->disabled ? 0 : calculate_score(afl, q); sum += q->weight; -- cgit 1.4.1 From 83c1378fc1810d177d9cdfa2c00f62bbadd98f00 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 29 Dec 2020 14:19:35 +0100 Subject: fix afl-clang on 32-Bit systems --- qemu_mode/qemuafl | 1 - src/afl-cc.c | 2 +- unicorn_mode/unicornafl | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) delete mode 160000 qemu_mode/qemuafl delete mode 160000 unicorn_mode/unicornafl (limited to 'src') diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl deleted file mode 160000 index 21ff3438..00000000 --- a/qemu_mode/qemuafl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 21ff34383764a8c6f66509b3b8d5282468c721e1 diff --git a/src/afl-cc.c b/src/afl-cc.c index 758b958b..e6a6718e 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -879,7 +879,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { #ifndef __ANDROID__ - if (compiler_mode != GCC) { + if (compiler_mode != GCC && compiler_mode != CLANG) { switch (bit_mode) { diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl deleted file mode 160000 index 8cca4801..00000000 --- a/unicorn_mode/unicornafl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8cca4801adb767dce7cf72202d7d25bdb420cf7d -- cgit 1.4.1 From 80767480f0f40d380c83069b5f82b808c76493cc Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 29 Dec 2020 18:13:36 +0100 Subject: Revert "fix afl-clang on 32-Bit systems" This reverts commit 83c1378fc1810d177d9cdfa2c00f62bbadd98f00. --- qemu_mode/qemuafl | 1 + src/afl-cc.c | 2 +- unicorn_mode/unicornafl | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) create mode 160000 qemu_mode/qemuafl create mode 160000 unicorn_mode/unicornafl (limited to 'src') diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl new file mode 160000 index 00000000..21ff3438 --- /dev/null +++ b/qemu_mode/qemuafl @@ -0,0 +1 @@ +Subproject commit 21ff34383764a8c6f66509b3b8d5282468c721e1 diff --git a/src/afl-cc.c b/src/afl-cc.c index e6a6718e..758b958b 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -879,7 +879,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { #ifndef __ANDROID__ - if (compiler_mode != GCC && compiler_mode != CLANG) { + if (compiler_mode != GCC) { switch (bit_mode) { diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl new file mode 160000 index 00000000..8cca4801 --- /dev/null +++ b/unicorn_mode/unicornafl @@ -0,0 +1 @@ +Subproject commit 8cca4801adb767dce7cf72202d7d25bdb420cf7d -- cgit 1.4.1 From 107c79b84b7cd7d90e8f9e10b3a46fbafcae39eb Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 29 Dec 2020 18:17:50 +0100 Subject: now fix afl-clang compiles on 32-Bit properly --- src/afl-cc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 758b958b..e6a6718e 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -879,7 +879,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { #ifndef __ANDROID__ - if (compiler_mode != GCC) { + if (compiler_mode != GCC && compiler_mode != CLANG) { switch (bit_mode) { -- cgit 1.4.1 From e9a306a50eee1444ae40b961fbc8d942541348ca Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 29 Dec 2020 10:25:48 +0100 Subject: fix localtime warnings from CodeQL --- src/afl-fuzz-init.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index ec937f29..75c0384f 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -1668,19 +1668,20 @@ static void handle_existing_out_dir(afl_state_t *afl) { if (afl->in_place_resume && rmdir(fn)) { time_t cur_t = time(0); - struct tm *t = localtime(&cur_t); + struct tm t; + localtime_r(&cur_t, &t); #ifndef SIMPLE_FILES u8 *nfn = alloc_printf("%s.%04d-%02d-%02d-%02d:%02d:%02d", fn, - t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, - t->tm_hour, t->tm_min, t->tm_sec); + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec); #else - u8 *nfn = alloc_printf("%s_%04d%02d%02d%02d%02d%02d", fn, t->tm_year + 1900, - t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, - t->tm_sec); + u8 *nfn = alloc_printf("%s_%04d%02d%02d%02d%02d%02d", fn, t.tm_year + 1900, + t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, + t.tm_sec); #endif /* ^!SIMPLE_FILES */ @@ -1699,19 +1700,20 @@ static void handle_existing_out_dir(afl_state_t *afl) { if (afl->in_place_resume && rmdir(fn)) { time_t cur_t = time(0); - struct tm *t = localtime(&cur_t); + struct tm t; + localtime_r(&cur_t, &t); #ifndef SIMPLE_FILES u8 *nfn = alloc_printf("%s.%04d-%02d-%02d-%02d:%02d:%02d", fn, - t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, - t->tm_hour, t->tm_min, t->tm_sec); + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec); #else - u8 *nfn = alloc_printf("%s_%04d%02d%02d%02d%02d%02d", fn, t->tm_year + 1900, - t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, - t->tm_sec); + u8 *nfn = alloc_printf("%s_%04d%02d%02d%02d%02d%02d", fn, t.tm_year + 1900, + t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, + t.tm_sec); #endif /* ^!SIMPLE_FILES */ -- cgit 1.4.1 From d687fbdfb4d3fc1485c9ab9d46da2546ae243f4a Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 29 Dec 2020 19:53:56 +0100 Subject: fix CodeQL warning on theoretically possible unsigned overflow --- src/afl-fuzz-redqueen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 9a9ac33f..5b3ade1d 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -456,7 +456,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - if (SHAPE_BYTES(h->shape) >= 1 && *status != 1) { + if (/* SHAPE_BYTES(h->shape) >= 1 && */ *status != 1) { /* avoid CodeQL warning on unsigned overflow */ if (its_len >= 1 && *buf_8 == (u8)pattern && *o_buf_8 == (u8)o_pattern) { -- cgit 1.4.1 From 0246fe9200ec29afd56a545c41b9888be84eafbf Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 30 Dec 2020 10:34:22 +0100 Subject: fix 32-bit mode, fix weighting --- GNUmakefile | 2 +- src/afl-fuzz-init.c | 32 ++++++++++++++++---------------- src/afl-fuzz-queue.c | 31 ++++++++++++++++++++++--------- src/afl-fuzz-redqueen.c | 3 ++- src/afl-performance.c | 8 ++++---- 5 files changed, 45 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index 58a49571..7b05a1d5 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -414,7 +414,7 @@ afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86 @ln -sf afl-as as src/afl-performance.o : $(COMM_HDR) src/afl-performance.c include/hash.h - $(CC) -Iinclude $(SPECIAL_PERFORMANCE) -O3 -fno-unroll-loops -c src/afl-performance.c -o src/afl-performance.o + $(CC) $(CFLAGS) -Iinclude $(SPECIAL_PERFORMANCE) -O3 -fno-unroll-loops -c src/afl-performance.c -o src/afl-performance.o src/afl-common.o : $(COMM_HDR) src/afl-common.c include/common.h $(CC) $(CFLAGS) $(CFLAGS_FLTO) -c src/afl-common.c -o src/afl-common.o diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 75c0384f..dbffa4f9 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -1667,21 +1667,21 @@ static void handle_existing_out_dir(afl_state_t *afl) { if (afl->in_place_resume && rmdir(fn)) { - time_t cur_t = time(0); - struct tm t; + time_t cur_t = time(0); + struct tm t; localtime_r(&cur_t, &t); #ifndef SIMPLE_FILES - u8 *nfn = alloc_printf("%s.%04d-%02d-%02d-%02d:%02d:%02d", fn, - t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, - t.tm_hour, t.tm_min, t.tm_sec); + u8 *nfn = + alloc_printf("%s.%04d-%02d-%02d-%02d:%02d:%02d", fn, t.tm_year + 1900, + t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); #else - u8 *nfn = alloc_printf("%s_%04d%02d%02d%02d%02d%02d", fn, t.tm_year + 1900, - t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, - t.tm_sec); + u8 *nfn = + alloc_printf("%s_%04d%02d%02d%02d%02d%02d", fn, t.tm_year + 1900, + t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); #endif /* ^!SIMPLE_FILES */ @@ -1699,21 +1699,21 @@ static void handle_existing_out_dir(afl_state_t *afl) { if (afl->in_place_resume && rmdir(fn)) { - time_t cur_t = time(0); - struct tm t; + time_t cur_t = time(0); + struct tm t; localtime_r(&cur_t, &t); #ifndef SIMPLE_FILES - u8 *nfn = alloc_printf("%s.%04d-%02d-%02d-%02d:%02d:%02d", fn, - t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, - t.tm_hour, t.tm_min, t.tm_sec); + u8 *nfn = + alloc_printf("%s.%04d-%02d-%02d-%02d:%02d:%02d", fn, t.tm_year + 1900, + t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); #else - u8 *nfn = alloc_printf("%s_%04d%02d%02d%02d%02d%02d", fn, t.tm_year + 1900, - t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, - t.tm_sec); + u8 *nfn = + alloc_printf("%s_%04d%02d%02d%02d%02d%02d", fn, t.tm_year + 1900, + t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); #endif /* ^!SIMPLE_FILES */ diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index d4b35ad2..928cdb62 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -97,30 +97,43 @@ void create_alias_table(afl_state_t *afl) { double avg_exec_us = 0.0; double avg_bitmap_size = 0.0; + u32 active = 0; + for (i = 0; i < n; i++) { struct queue_entry *q = afl->queue_buf[i]; - avg_exec_us += q->exec_us; - avg_bitmap_size += log(q->bitmap_size); + + // disabled entries might have timings and bitmap values + if (likely(!q->disabled)) { + + avg_exec_us += q->exec_us; + avg_bitmap_size += log(q->bitmap_size); + ++active; + + } } - avg_exec_us /= afl->queued_paths; - avg_bitmap_size /= afl->queued_paths; + avg_exec_us /= active; + avg_bitmap_size /= active; for (i = 0; i < n; i++) { struct queue_entry *q = afl->queue_buf[i]; - q->weight = q->disabled ? 0 : compute_weight(afl, q, avg_exec_us, avg_bitmap_size); - q->perf_score = q->disabled ? 0 : calculate_score(afl, q); + if (likely(!q->disabled)) { + + q->weight = compute_weight(afl, q, avg_exec_us, avg_bitmap_size); + q->perf_score = calculate_score(afl, q); + sum += q->weight; - sum += q->weight; + } } for (i = 0; i < n; i++) { + // weight is always 0 for disabled entries P[i] = (afl->queue_buf[i]->weight * n) / sum; } @@ -139,8 +152,8 @@ void create_alias_table(afl_state_t *afl) { for (i = 0; i < n; i++) { - struct queue_entry *q = afl->queue_buf[i]; - P[i] = (q->perf_score * n) / sum; + // perf_score is always 0 for disabled entries + P[i] = (afl->queue_buf[i]->perf_score * n) / sum; } diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 5b3ade1d..37d66aef 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -456,7 +456,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - if (/* SHAPE_BYTES(h->shape) >= 1 && */ *status != 1) { /* avoid CodeQL warning on unsigned overflow */ + /* avoid CodeQL warning on unsigned overflow */ + if (/* SHAPE_BYTES(h->shape) >= 1 && */ *status != 1) { if (its_len >= 1 && *buf_8 == (u8)pattern && *o_buf_8 == (u8)o_pattern) { diff --git a/src/afl-performance.c b/src/afl-performance.c index 89b170eb..4bca95d6 100644 --- a/src/afl-performance.c +++ b/src/afl-performance.c @@ -56,13 +56,13 @@ inline AFL_RAND_RETURN rand_next(afl_state_t *afl) { // RomuTrio32 inline AFL_RAND_RETURN rand_next(afl_state_t *afl) { - AFL_RAND_RETURN xp = afl->rand_seed[0], yp = afl->rand_seed[1], - zp = afl->rand_seed[2]; + AFL_RAND_RETURN xp = (u32)afl->rand_seed[0], yp = (u32)afl->rand_seed[1], + zp = (u32)afl->rand_seed[2]; afl->rand_seed[0] = 3323815723u * zp; afl->rand_seed[1] = yp - xp; - afl->rand_seed[1] = ROTL(afl->rand_seed[1], 6); + afl->rand_seed[1] = ROTL((u32)afl->rand_seed[1], 6); afl->rand_seed[2] = zp - yp; - afl->rand_seed[2] = ROTL(afl->rand_seed[2], 22); + afl->rand_seed[2] = ROTL((u32)afl->rand_seed[2], 22); return xp; } -- cgit 1.4.1 From f38595f6b47c538ba4e8a177e81c596040ee99c9 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 30 Dec 2020 12:32:55 +0100 Subject: better weighting --- src/afl-fuzz-queue.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 928cdb62..9a0d199e 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -45,25 +45,19 @@ inline u32 select_next_queue_entry(afl_state_t *afl) { double compute_weight(afl_state_t *afl, struct queue_entry *q, double avg_exec_us, double avg_bitmap_size) { - u32 hits; + double weight = 1.0; if (likely(afl->schedule >= FAST && afl->schedule <= RARE)) { - hits = afl->n_fuzz[q->n_fuzz_entry]; - if (hits == 0) { hits = 1; } - - } else { - - hits = 1; + u32 hits = afl->n_fuzz[q->n_fuzz_entry]; + if (likely(hits)) { weight *= log10(hits) + 1; } } - double weight = 1.0; weight *= avg_exec_us / q->exec_us; - weight *= log(q->bitmap_size) / avg_bitmap_size; - weight /= log10(hits) + 1; + weight *= (log(q->bitmap_size) / avg_bitmap_size); - if (q->favored) weight *= 5; + if (unlikely(q->favored)) weight *= 5; return weight; @@ -210,11 +204,13 @@ void create_alias_table(afl_state_t *afl) { struct queue_entry *q = afl->queue_buf[i]; fprintf( f, - "entry=%u name=%s variable=%s disabled=%s len=%u exec_us=%u " + "entry=%u name=%s favored=%s variable=%s disabled=%s len=%u " + "exec_us=%u " "bitmap_size=%u bitsmap_size=%u tops=%u weight=%f perf_score=%f\n", - i, q->fname, q->var_behavior ? "true" : "false", - q->disabled ? "true" : "false", q->len, (u32)q->exec_us, - q->bitmap_size, q->bitsmap_size, q->tc_ref, q->weight, q->perf_score); + i, q->fname, q->favored ? "true" : "false", + q->var_behavior ? "true" : "false", q->disabled ? "true" : "false", + q->len, (u32)q->exec_us, q->bitmap_size, q->bitsmap_size, q->tc_ref, + q->weight, q->perf_score); } @@ -226,10 +222,10 @@ void create_alias_table(afl_state_t *afl) { #endif /* - fprintf(stderr, " entry alias probability perf_score filename\n"); - for (u32 i = 0; i < n; ++i) - fprintf(stderr, " %5u %5u %11u %0.9f %s\n", i, afl->alias_table[i], - afl->alias_probability[i], afl->queue_buf[i]->perf_score, + fprintf(stderr, " entry alias probability perf_score weight + filename\n"); for (u32 i = 0; i < n; ++i) fprintf(stderr, " %5u %5u %11u + %0.9f %0.9f %s\n", i, afl->alias_table[i], afl->alias_probability[i], + afl->queue_buf[i]->perf_score, afl->queue_buf[i]->weight, afl->queue_buf[i]->fname); */ -- cgit 1.4.1 From b0d69f1b6995a9ddc0aebb0c289da79ebd3fc5e9 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Wed, 30 Dec 2020 13:02:11 +0100 Subject: cleanup of hot fix for -s on 32-bit --- include/afl-fuzz.h | 3 ++- src/afl-performance.c | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 57b0e6cc..0bf0aebe 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -590,7 +590,8 @@ typedef struct afl_state { u32 rand_cnt; /* Random number counter */ - u64 rand_seed[3]; +/* unsigned long rand_seed[3]; would also work */ + AFL_RAND_RETURN rand_seed[3]; s64 init_seed; u64 total_cal_us, /* Total calibration time (us) */ diff --git a/src/afl-performance.c b/src/afl-performance.c index 4bca95d6..89b170eb 100644 --- a/src/afl-performance.c +++ b/src/afl-performance.c @@ -56,13 +56,13 @@ inline AFL_RAND_RETURN rand_next(afl_state_t *afl) { // RomuTrio32 inline AFL_RAND_RETURN rand_next(afl_state_t *afl) { - AFL_RAND_RETURN xp = (u32)afl->rand_seed[0], yp = (u32)afl->rand_seed[1], - zp = (u32)afl->rand_seed[2]; + AFL_RAND_RETURN xp = afl->rand_seed[0], yp = afl->rand_seed[1], + zp = afl->rand_seed[2]; afl->rand_seed[0] = 3323815723u * zp; afl->rand_seed[1] = yp - xp; - afl->rand_seed[1] = ROTL((u32)afl->rand_seed[1], 6); + afl->rand_seed[1] = ROTL(afl->rand_seed[1], 6); afl->rand_seed[2] = zp - yp; - afl->rand_seed[2] = ROTL((u32)afl->rand_seed[2], 22); + afl->rand_seed[2] = ROTL(afl->rand_seed[2], 22); return xp; } -- cgit 1.4.1 From 7e6645d5a25b874b978430eeb7d1ddce1f681d98 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 31 Dec 2020 11:51:10 +0100 Subject: float2double --- include/afl-fuzz.h | 2 +- src/afl-fuzz-stats.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 0bf0aebe..a99e4991 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -645,7 +645,7 @@ typedef struct afl_state { unsigned long long int last_avg_exec_update; u32 last_avg_execs; - float last_avg_execs_saved; + double last_avg_execs_saved; /* foreign sync */ #define FOREIGN_SYNCS_MAX 32 diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index cb0d3dcd..1c211da6 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -120,8 +120,8 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, cur_time - afl->last_avg_exec_update >= 60000))) { afl->last_avg_execs_saved = - (float)(1000 * (afl->fsrv.total_execs - afl->last_avg_execs)) / - (float)(cur_time - afl->last_avg_exec_update); + (double)(1000 * (afl->fsrv.total_execs - afl->last_avg_execs)) / + (double)(cur_time - afl->last_avg_exec_update); afl->last_avg_execs = afl->fsrv.total_execs; afl->last_avg_exec_update = cur_time; -- cgit 1.4.1 From fac373ec9e4a0a35a5f0491a34e790137ca17dee Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 1 Jan 2021 13:42:58 +0100 Subject: try new weighting --- include/afl-fuzz.h | 4 ++-- src/afl-fuzz-queue.c | 17 +++++++++++------ utils/libdislocator/libdislocator.so.c | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index a99e4991..d6a322cc 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -590,9 +590,9 @@ typedef struct afl_state { u32 rand_cnt; /* Random number counter */ -/* unsigned long rand_seed[3]; would also work */ + /* unsigned long rand_seed[3]; would also work */ AFL_RAND_RETURN rand_seed[3]; - s64 init_seed; + s64 init_seed; u64 total_cal_us, /* Total calibration time (us) */ total_cal_cycles; /* Total calibration cycles */ diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 9a0d199e..de750f36 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -43,7 +43,8 @@ inline u32 select_next_queue_entry(afl_state_t *afl) { } double compute_weight(afl_state_t *afl, struct queue_entry *q, - double avg_exec_us, double avg_bitmap_size) { + double avg_exec_us, double avg_bitmap_size, + double avg_top_size) { double weight = 1.0; @@ -54,9 +55,9 @@ double compute_weight(afl_state_t *afl, struct queue_entry *q, } - weight *= avg_exec_us / q->exec_us; - weight *= (log(q->bitmap_size) / avg_bitmap_size); - + if (likely(afl->schedule < RARE)) { weight *= (avg_exec_us / q->exec_us); } + weight *= (q->bitmap_size / avg_bitmap_size); + weight *= (log(q->tc_ref) / avg_top_size); if (unlikely(q->favored)) weight *= 5; return weight; @@ -91,6 +92,7 @@ void create_alias_table(afl_state_t *afl) { double avg_exec_us = 0.0; double avg_bitmap_size = 0.0; + double avg_top_size = 0.0; u32 active = 0; for (i = 0; i < n; i++) { @@ -101,7 +103,8 @@ void create_alias_table(afl_state_t *afl) { if (likely(!q->disabled)) { avg_exec_us += q->exec_us; - avg_bitmap_size += log(q->bitmap_size); + avg_bitmap_size += q->bitmap_size; + avg_top_size += log(q->tc_ref); ++active; } @@ -110,6 +113,7 @@ void create_alias_table(afl_state_t *afl) { avg_exec_us /= active; avg_bitmap_size /= active; + avg_top_size /= active; for (i = 0; i < n; i++) { @@ -117,7 +121,8 @@ void create_alias_table(afl_state_t *afl) { if (likely(!q->disabled)) { - q->weight = compute_weight(afl, q, avg_exec_us, avg_bitmap_size); + q->weight = + compute_weight(afl, q, avg_exec_us, avg_bitmap_size, avg_top_size); q->perf_score = calculate_score(afl, q); sum += q->weight; diff --git a/utils/libdislocator/libdislocator.so.c b/utils/libdislocator/libdislocator.so.c index c2b200cb..c041fec6 100644 --- a/utils/libdislocator/libdislocator.so.c +++ b/utils/libdislocator/libdislocator.so.c @@ -345,7 +345,7 @@ void free(void *ptr) { len = PTR_L(ptr); total_mem -= len; - u8 * ptr_ = ptr; + u8 *ptr_ = ptr; if (align_allocations && (len & (ALLOC_ALIGN_SIZE - 1))) { -- cgit 1.4.1 From 33abd70647789fc0e3960e76351bec5793944918 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 2 Jan 2021 20:06:02 +0100 Subject: typo --- src/afl-fuzz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 2af374f2..897c2f1e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1534,7 +1534,7 @@ int main(int argc, char **argv_orig, char **envp) { if (!afl->pending_not_fuzzed) { - FATAL("We need at least on valid input seed that does not crash!"); + FATAL("We need at least one valid input seed that does not crash!"); } -- cgit 1.4.1 From 1857df8d06c48b7d0bf99eee5dea2de595cdf4c5 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 3 Jan 2021 00:37:56 +0100 Subject: cleanup --- src/afl-fuzz-redqueen.c | 4 +-- utils/aflpp_driver/aflpp_driver.c | 65 ++++----------------------------------- 2 files changed, 8 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 37d66aef..df215e9c 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -802,13 +802,13 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, u64 exec_cksum) { u8 r = 1; - if (afl->orig_cmp_map == NULL) { + if (unlikely(!afl->orig_cmp_map)) { afl->orig_cmp_map = ck_alloc_nozero(sizeof(struct cmp_map)); } - if (afl->pass_stats == NULL) { + if (unlikely(!afl->pass_stats)) { afl->pass_stats = ck_alloc(sizeof(struct afl_pass_stat) * CMP_MAP_W); diff --git a/utils/aflpp_driver/aflpp_driver.c b/utils/aflpp_driver/aflpp_driver.c index c6f5a76c..30e6ebb9 100644 --- a/utils/aflpp_driver/aflpp_driver.c +++ b/utils/aflpp_driver/aflpp_driver.c @@ -1,12 +1,8 @@ -//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +//===- afl_driver.cpp - a glue between AFL++ and libFuzzer ------*- C++ -* ===// //===----------------------------------------------------------------------===// /* This file allows to fuzz libFuzzer-style target functions - (LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode. + (LLVMFuzzerTestOneInput) with AFL++ using persistent in-memory fuzzing. Usage: ################################################################################ @@ -25,25 +21,17 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { EOF # Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang. -clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c +clang -c aflpp_driver.c # Build afl-compiler-rt.o.c from the AFL distribution. -clang -c -w $AFL_HOME/instrumentation/afl-compiler-rt.o.c +clang -c $AFL_HOME/instrumentation/afl-compiler-rt.o.c # Build this file, link it with afl-compiler-rt.o.o and the target code. -clang++ afl_driver.cpp test_fuzzer.o afl-compiler-rt.o.o +afl-clang-fast -o test_fuzzer test_fuzzer.cc afl-compiler-rt.o aflpp_driver.o # Run AFL: rm -rf IN OUT; mkdir IN OUT; echo z > IN/z; $AFL_HOME/afl-fuzz -i IN -o OUT ./a.out ################################################################################ -AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file -specified. If the file does not exist, it is created. This is useful for getting -stack traces (when using ASAN for example) or original error messages on hard -to reproduce bugs. Note that any content written to stderr will be written to -this file instead of stderr's usual location. - -AFL_DRIVER_CLOSE_FD_MASK: Similar to libFuzzer's -close_fd_mask behavior option. -If 1, close stdout at startup. If 2 close stderr; if 3 close both. - */ + #include #include #include @@ -65,47 +53,6 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. #include "hash.h" #endif -#ifndef MAP_FIXED_NOREPLACE - #define MAP_FIXED_NOREPLACE 0x100000 -#endif - -#define MAX_DUMMY_SIZE 256000 - -// Platform detection. Copied from FuzzerInternal.h -#ifdef __linux__ - #define LIBFUZZER_LINUX 1 - #define LIBFUZZER_APPLE 0 - #define LIBFUZZER_NETBSD 0 - #define LIBFUZZER_FREEBSD 0 - #define LIBFUZZER_OPENBSD 0 -#elif __APPLE__ - #define LIBFUZZER_LINUX 0 - #define LIBFUZZER_APPLE 1 - #define LIBFUZZER_NETBSD 0 - #define LIBFUZZER_FREEBSD 0 - #define LIBFUZZER_OPENBSD 0 -#elif __NetBSD__ - #define LIBFUZZER_LINUX 0 - #define LIBFUZZER_APPLE 0 - #define LIBFUZZER_NETBSD 1 - #define LIBFUZZER_FREEBSD 0 - #define LIBFUZZER_OPENBSD 0 -#elif __FreeBSD__ - #define LIBFUZZER_LINUX 0 - #define LIBFUZZER_APPLE 0 - #define LIBFUZZER_NETBSD 0 - #define LIBFUZZER_FREEBSD 1 - #define LIBFUZZER_OPENBSD 0 -#elif __OpenBSD__ - #define LIBFUZZER_LINUX 0 - #define LIBFUZZER_APPLE 0 - #define LIBFUZZER_NETBSD 0 - #define LIBFUZZER_FREEBSD 0 - #define LIBFUZZER_OPENBSD 1 -#else - #error "Support for your platform has not been implemented" -#endif - int __afl_sharedmem_fuzzing = 1; extern unsigned int * __afl_fuzz_len; extern unsigned char *__afl_fuzz_ptr; -- cgit 1.4.1 From 880513651d271db538eeb3f04d046cf9ce6021f8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 3 Jan 2021 10:13:55 +0100 Subject: cmplog cleanup --- include/afl-fuzz.h | 4 ++-- src/afl-fuzz-redqueen.c | 40 +++------------------------------- utils/libdislocator/libdislocator.so.c | 2 +- 3 files changed, 6 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index a99e4991..d6a322cc 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -590,9 +590,9 @@ typedef struct afl_state { u32 rand_cnt; /* Random number counter */ -/* unsigned long rand_seed[3]; would also work */ + /* unsigned long rand_seed[3]; would also work */ AFL_RAND_RETURN rand_seed[3]; - s64 init_seed; + s64 init_seed; u64 total_cal_us, /* Total calibration time (us) */ total_cal_cycles; /* Total calibration cycles */ diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index df215e9c..bbd47ab2 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -99,12 +99,12 @@ static u8 get_exec_checksum(afl_state_t *afl, u8 *buf, u32 len, u64 *cksum) { } -static void rand_replace(afl_state_t *afl, u8 *buf, u32 len) { +static void xor_replace(u8 *buf, u32 len) { u32 i; for (i = 0; i < len; ++i) { - buf[i] = rand_below(afl, 256); + buf[i] ^= 0xff; } @@ -115,8 +115,6 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum) { struct range *ranges = add_range(NULL, 0, len); u8 * backup = ck_alloc_nozero(len); - u8 needs_write = 0; - u64 orig_hit_cnt, new_hit_cnt; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; @@ -136,7 +134,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum) { /* Range not empty */ memcpy(backup, buf + rng->start, s); - rand_replace(afl, buf + rng->start, s); + xor_replace(buf + rng->start, s); u64 cksum; u64 start_us = get_cur_time_us(); @@ -158,10 +156,6 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum) { ranges = add_range(ranges, rng->start + s / 2 + 1, rng->end); memcpy(buf + rng->start, backup, s); - } else { - - needs_write = 1; - } } @@ -191,32 +185,6 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum) { } - // save the input with the high entropy - - if (needs_write) { - - s32 fd; - - if (afl->no_unlink) { - - fd = open(afl->queue_cur->fname, O_WRONLY | O_CREAT | O_TRUNC, 0600); - - } else { - - unlink(afl->queue_cur->fname); /* ignore errors */ - fd = open(afl->queue_cur->fname, O_WRONLY | O_CREAT | O_EXCL, 0600); - - } - - if (fd < 0) { PFATAL("Unable to create '%s'", afl->queue_cur->fname); } - - ck_write(fd, buf, len, afl->queue_cur->fname); - afl->queue_cur->len = len; // no-op, just to be 100% safe - - close(fd); - - } - return 0; checksum_fail: @@ -232,8 +200,6 @@ checksum_fail: } - // TODO: clang notices a _potential_ leak of mem pointed to by rng - return 1; } diff --git a/utils/libdislocator/libdislocator.so.c b/utils/libdislocator/libdislocator.so.c index c2b200cb..c041fec6 100644 --- a/utils/libdislocator/libdislocator.so.c +++ b/utils/libdislocator/libdislocator.so.c @@ -345,7 +345,7 @@ void free(void *ptr) { len = PTR_L(ptr); total_mem -= len; - u8 * ptr_ = ptr; + u8 *ptr_ = ptr; if (align_allocations && (len & (ALLOC_ALIGN_SIZE - 1))) { -- cgit 1.4.1 From 58039d181afcc22b54f42abeabe67ae9bb43e251 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 3 Jan 2021 12:34:23 +0100 Subject: stability fix --- src/afl-fuzz-redqueen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index bbd47ab2..c5db8fa1 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -854,7 +854,7 @@ exit_its: afl->stage_finds[STAGE_ITS] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_ITS] += afl->fsrv.total_execs - orig_execs; - memcpy(orig_buf, buf, len); + memcpy(buf, orig_buf, len); return r; -- cgit 1.4.1 From c6e038fe25789caa8da777f53154de1bd7b4e178 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Mon, 4 Jan 2021 20:40:53 +0100 Subject: code cleanups (shadowed vars, (un)signed type mismatches, format types, etc.) --- include/afl-fuzz.h | 4 +-- include/config.h | 46 +++++++++++++++--------------- src/afl-analyze.c | 4 +-- src/afl-cc.c | 74 ++++++++++++++++++++++++++++--------------------- src/afl-common.c | 20 ++++++------- src/afl-forkserver.c | 4 +-- src/afl-fuzz-bitmap.c | 2 +- src/afl-fuzz-extras.c | 6 ++-- src/afl-fuzz-mutators.c | 28 ++++++++++++------- src/afl-fuzz-one.c | 24 ++++++++-------- src/afl-fuzz-queue.c | 5 ++-- src/afl-fuzz-redqueen.c | 9 ++++-- src/afl-fuzz-run.c | 2 +- src/afl-fuzz-stats.c | 8 +++--- src/afl-fuzz.c | 8 +++--- src/afl-ld-lto.c | 6 ++-- src/afl-showmap.c | 12 ++++---- src/afl-tmin.c | 4 +-- 18 files changed, 144 insertions(+), 122 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index d6a322cc..ede54f0e 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -573,7 +573,7 @@ typedef struct afl_state { u8 stage_name_buf[STAGE_BUF_SIZE]; /* reused stagename buf with len 64 */ - s32 stage_cur, stage_max; /* Stage progression */ + u32 stage_cur, stage_max; /* Stage progression */ s32 splicing_with; /* Splicing with which test case? */ u32 main_node_id, main_node_max; /* Main instance job splitting */ @@ -648,7 +648,7 @@ typedef struct afl_state { double last_avg_execs_saved; /* foreign sync */ -#define FOREIGN_SYNCS_MAX 32 +#define FOREIGN_SYNCS_MAX 32U u8 foreign_sync_cnt; struct foreign_sync foreign_syncs[FOREIGN_SYNCS_MAX]; diff --git a/include/config.h b/include/config.h index e8a49270..7c75e9c9 100644 --- a/include/config.h +++ b/include/config.h @@ -80,11 +80,11 @@ /* Default timeout for fuzzed code (milliseconds). This is the upper bound, also used for detecting hangs; the actual value is auto-scaled: */ -#define EXEC_TIMEOUT 1000 +#define EXEC_TIMEOUT 1000U /* Timeout rounding factor when auto-scaling (milliseconds): */ -#define EXEC_TM_ROUND 20 +#define EXEC_TM_ROUND 20U /* 64bit arch MACRO */ #if (defined(__x86_64__) || defined(__arm64__) || defined(__aarch64__)) @@ -93,48 +93,48 @@ /* Default memory limit for child process (MB) 0 = disabled : */ -#define MEM_LIMIT 0 +#define MEM_LIMIT 0U /* Default memory limit when running in QEMU mode (MB) 0 = disabled : */ -#define MEM_LIMIT_QEMU 0 +#define MEM_LIMIT_QEMU 0U /* Default memory limit when running in Unicorn mode (MB) 0 = disabled : */ -#define MEM_LIMIT_UNICORN 0 +#define MEM_LIMIT_UNICORN 0U /* Number of calibration cycles per every new test case (and for test cases that show variable behavior): */ -#define CAL_CYCLES 8 -#define CAL_CYCLES_LONG 40 +#define CAL_CYCLES 8U +#define CAL_CYCLES_LONG 40U /* Number of subsequent timeouts before abandoning an input file: */ -#define TMOUT_LIMIT 250 +#define TMOUT_LIMIT 250U /* Maximum number of unique hangs or crashes to record: */ -#define KEEP_UNIQUE_HANG 500 -#define KEEP_UNIQUE_CRASH 5000 +#define KEEP_UNIQUE_HANG 500U +#define KEEP_UNIQUE_CRASH 5000U /* Baseline number of random tweaks during a single 'havoc' stage: */ -#define HAVOC_CYCLES 256 -#define HAVOC_CYCLES_INIT 1024 +#define HAVOC_CYCLES 256U +#define HAVOC_CYCLES_INIT 1024U /* Maximum multiplier for the above (should be a power of two, beware of 32-bit int overflows): */ -#define HAVOC_MAX_MULT 64 -#define HAVOC_MAX_MULT_MOPT 64 +#define HAVOC_MAX_MULT 64U +#define HAVOC_MAX_MULT_MOPT 64U /* Absolute minimum number of havoc cycles (after all adjustments): */ -#define HAVOC_MIN 12 +#define HAVOC_MIN 12U /* Power Schedule Divisor */ -#define POWER_BETA 1 +#define POWER_BETA 1U #define MAX_FACTOR (POWER_BETA * 32) /* Maximum stacking for havoc-stage tweaks. The actual value is calculated @@ -146,19 +146,19 @@ In other words, the default (n = 4) produces 2, 4, 8, 16 stacked tweaks: */ -#define HAVOC_STACK_POW2 4 +#define HAVOC_STACK_POW2 4U /* Caps on block sizes for cloning and deletion operations. Each of these ranges has a 33% probability of getting picked, except for the first two cycles where smaller blocks are favored: */ -#define HAVOC_BLK_SMALL 32 -#define HAVOC_BLK_MEDIUM 128 -#define HAVOC_BLK_LARGE 1500 +#define HAVOC_BLK_SMALL 32U +#define HAVOC_BLK_MEDIUM 128U +#define HAVOC_BLK_LARGE 1500U /* Extra-large blocks, selected very rarely (<5% of the time): */ -#define HAVOC_BLK_XL 32768 +#define HAVOC_BLK_XL 32768U /* Probabilities of skipping non-favored entries in the queue, expressed as percentages: */ @@ -188,11 +188,11 @@ /* Maximum size of input file, in bytes (keep under 100MB): */ -#define MAX_FILE (1 * 1024 * 1024) +#define MAX_FILE (1 * 1024 * 1024U) /* The same, for the test case minimizer: */ -#define TMIN_MAX_FILE (10 * 1024 * 1024) +#define TMIN_MAX_FILE (10 * 1024 * 1024U) /* Block normalization steps for afl-tmin: */ diff --git a/src/afl-analyze.c b/src/afl-analyze.c index 6dac415b..8fc4434a 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -903,8 +903,8 @@ static void usage(u8 *argv0) { "Execution control settings:\n" " -f file - input file read by the tested program (stdin)\n" - " -t msec - timeout for each run (%d ms)\n" - " -m megs - memory limit for child process (%d MB)\n" + " -t msec - timeout for each run (%u ms)\n" + " -m megs - memory limit for child process (%u MB)\n" " -Q - use binary-only instrumentation (QEMU mode)\n" " -U - use unicorn-based instrumentation (Unicorn mode)\n" " -W - use qemu-based instrumentation with Wine (Wine " diff --git a/src/afl-cc.c b/src/afl-cc.c index e6a6718e..180ab3c4 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -120,8 +120,10 @@ char compiler_mode_string[7][12] = { u8 *getthecwd() { - static u8 fail[] = ""; - if (getcwd(cwd, sizeof(cwd)) == NULL) return fail; + if (getcwd(cwd, sizeof(cwd)) == NULL) { + static u8 fail[] = ""; + return fail; + } return cwd; } @@ -654,9 +656,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - u32 idx; if (lto_mode && argc > 1) { + u32 idx; for (idx = 1; idx < argc; idx++) { if (!strncasecmp(argv[idx], "-fpic", 5)) have_pic = 1; @@ -1208,12 +1210,12 @@ int main(int argc, char **argv, char **envp) { if (getenv("AFL_LLVM_INSTRUMENT")) { - u8 *ptr = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); + u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); - while (ptr) { + while (ptr2) { - if (strncasecmp(ptr, "afl", strlen("afl")) == 0 || - strncasecmp(ptr, "classic", strlen("classic")) == 0) { + if (strncasecmp(ptr2, "afl", strlen("afl")) == 0 || + strncasecmp(ptr2, "classic", strlen("classic")) == 0) { if (instrument_mode == INSTRUMENT_LTO) { @@ -1229,8 +1231,8 @@ int main(int argc, char **argv, char **envp) { } - if (strncasecmp(ptr, "pc-guard", strlen("pc-guard")) == 0 || - strncasecmp(ptr, "pcguard", strlen("pcguard")) == 0) { + if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 || + strncasecmp(ptr2, "pcguard", strlen("pcguard")) == 0) { if (!instrument_mode || instrument_mode == INSTRUMENT_PCGUARD) instrument_mode = INSTRUMENT_PCGUARD; @@ -1241,8 +1243,8 @@ int main(int argc, char **argv, char **envp) { } // this is a hidden option - if (strncasecmp(ptr, "llvmnative", strlen("llvmnative")) == 0 || - strncasecmp(ptr, "llvm-native", strlen("llvm-native")) == 0) { + if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 || + strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0) { if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE) instrument_mode = INSTRUMENT_LLVMNATIVE; @@ -1252,8 +1254,8 @@ int main(int argc, char **argv, char **envp) { } - if (strncasecmp(ptr, "cfg", strlen("cfg")) == 0 || - strncasecmp(ptr, "instrim", strlen("instrim")) == 0) { + if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 || + strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) { if (instrument_mode == INSTRUMENT_LTO) { @@ -1269,7 +1271,7 @@ int main(int argc, char **argv, char **envp) { } - if (strncasecmp(ptr, "lto", strlen("lto")) == 0) { + if (strncasecmp(ptr2, "lto", strlen("lto")) == 0) { lto_mode = 1; if (!instrument_mode || instrument_mode == INSTRUMENT_LTO) @@ -1280,7 +1282,7 @@ int main(int argc, char **argv, char **envp) { } - if (strcasecmp(ptr, "gcc") == 0) { + if (strcasecmp(ptr2, "gcc") == 0) { if (!instrument_mode || instrument_mode == INSTRUMENT_GCC) instrument_mode = INSTRUMENT_GCC; @@ -1291,7 +1293,7 @@ int main(int argc, char **argv, char **envp) { } - if (strcasecmp(ptr, "clang") == 0) { + if (strcasecmp(ptr2, "clang") == 0) { if (!instrument_mode || instrument_mode == INSTRUMENT_CLANG) instrument_mode = INSTRUMENT_CLANG; @@ -1302,29 +1304,29 @@ int main(int argc, char **argv, char **envp) { } - if (strncasecmp(ptr, "ctx", strlen("ctx")) == 0) { + if (strncasecmp(ptr2, "ctx", strlen("ctx")) == 0) { instrument_opt_mode |= INSTRUMENT_OPT_CTX; setenv("AFL_LLVM_CTX", "1", 1); } - if (strncasecmp(ptr, "ngram", strlen("ngram")) == 0) { + if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) { - ptr += strlen("ngram"); - while (*ptr && (*ptr < '0' || *ptr > '9')) - ptr++; + ptr2 += strlen("ngram"); + while (*ptr2 && (*ptr2 < '0' || *ptr2 > '9')) + ptr2++; - if (!*ptr) { + if (!*ptr2) { - if ((ptr = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL) + if ((ptr2 = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL) FATAL( "you must set the NGRAM size with (e.g. for value 2) " "AFL_LLVM_INSTRUMENT=ngram-2"); } - ngram_size = atoi(ptr); + ngram_size = atoi(ptr2); if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) FATAL( "NGRAM instrumentation option must be between 2 and " @@ -1332,12 +1334,12 @@ int main(int argc, char **argv, char **envp) { "(%u)", NGRAM_SIZE_MAX); instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM); - ptr = alloc_printf("%u", ngram_size); - setenv("AFL_LLVM_NGRAM_SIZE", ptr, 1); + ptr2 = alloc_printf("%u", ngram_size); + setenv("AFL_LLVM_NGRAM_SIZE", ptr2, 1); } - ptr = strtok(NULL, ":,;"); + ptr2 = strtok(NULL, ":,;"); } @@ -1448,20 +1450,28 @@ int main(int argc, char **argv, char **envp) { " The best is LTO but it often needs RANLIB and AR settings outside " "of afl-cc.\n\n"); +#if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) +#define NATIVE_MSG \ + " NATIVE: use llvm's native PCGUARD instrumentation (less " \ + "performant)\n" +#else +#define NATIVE_MSG "" +#endif + SAYF( "Sub-Modes: (set via env AFL_LLVM_INSTRUMENT, afl-cc selects the best " "available)\n" " PCGUARD: Dominator tree instrumentation (best!) (README.llvm.md)\n" -#if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) - " NATIVE: use llvm's native PCGUARD instrumentation (less " - "performant)\n" -#endif + + NATIVE_MSG + " CLASSIC: decision target instrumentation (README.llvm.md)\n" " CTX: CLASSIC + callee context (instrumentation/README.ctx.md)\n" " NGRAM-x: CLASSIC + previous path " "((instrumentation/README.ngram.md)\n" " INSTRIM: Dominator tree (for LLVM <= 6.0) " "(instrumentation/README.instrim.md)\n\n"); +#undef NATIVE_MSG SAYF( "Features: (see documentation links)\n" @@ -1625,7 +1635,7 @@ int main(int argc, char **argv, char **envp) { if (!instrument_mode) { instrument_mode = INSTRUMENT_CFG; - ptr = instrument_mode_string[instrument_mode]; + //ptr = instrument_mode_string[instrument_mode]; } diff --git a/src/afl-common.c b/src/afl-common.c index 1928663d..21cb6ab4 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -696,16 +696,16 @@ u8 *stringify_mem_size(u8 *buf, size_t len, u64 val) { u8 *stringify_time_diff(u8 *buf, size_t len, u64 cur_ms, u64 event_ms) { - u64 delta; - s32 t_d, t_h, t_m, t_s; - u8 val_buf[STRINGIFY_VAL_SIZE_MAX]; - if (!event_ms) { snprintf(buf, len, "none seen yet"); } else { + u64 delta; + s32 t_d, t_h, t_m, t_s; + u8 val_buf[STRINGIFY_VAL_SIZE_MAX]; + delta = cur_ms - event_ms; t_d = delta / 1000 / 60 / 60 / 24; @@ -858,16 +858,16 @@ u8 *u_stringify_mem_size(u8 *buf, u64 val) { u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms) { - u64 delta; - s32 t_d, t_h, t_m, t_s; - u8 val_buf[STRINGIFY_VAL_SIZE_MAX]; - if (!event_ms) { sprintf(buf, "none seen yet"); } else { + u64 delta; + s32 t_d, t_h, t_m, t_s; + u8 val_buf[STRINGIFY_VAL_SIZE_MAX]; + delta = cur_ms - event_ms; t_d = delta / 1000 / 60 / 60 / 24; @@ -895,8 +895,8 @@ u32 get_map_size(void) { map_size = atoi(ptr); if (map_size < 8 || map_size > (1 << 29)) { - FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 8, - 1 << 29); + FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 8U, + 1U << 29); } diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 90fa55e9..d6195cb5 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -213,7 +213,7 @@ restart_select: static void afl_fauxsrv_execv(afl_forkserver_t *fsrv, char **argv) { unsigned char tmp[4] = {0, 0, 0, 0}; - pid_t child_pid = -1; + pid_t child_pid; if (!be_quiet) { ACTF("Using Fauxserver:"); } @@ -1104,7 +1104,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, "Unable to communicate with fork server. Some possible reasons:\n\n" " - You've run out of memory. Use -m to increase the the memory " "limit\n" - " to something higher than %lld.\n" + " to something higher than %llu.\n" " - The binary or one of the libraries it uses manages to " "create\n" " threads before the forkserver initializes.\n" diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 62a8211c..85a01f98 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -703,7 +703,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (!classified) { classify_counts(&afl->fsrv); - classified = 1; +// classified = 1; } diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 171cce96..04f0878c 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -266,7 +266,7 @@ static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, if (afl->extras_cnt > afl->max_det_extras) { - WARNF("More than %d tokens - will use them probabilistically.", + WARNF("More than %u tokens - will use them probabilistically.", afl->max_det_extras); } @@ -431,7 +431,6 @@ void dedup_extras(afl_state_t *afl) { /* Adds a new extra / dict entry. */ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { - u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; u32 i, found = 0; for (i = 0; i < afl->extras_cnt; i++) { @@ -451,6 +450,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { if (len > MAX_DICT_FILE) { + u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; WARNF("Extra '%.*s' is too big (%s, limit is %s), skipping file!", (int)len, mem, stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), len), stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); @@ -481,7 +481,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { if (afl->extras_cnt == afl->max_det_extras + 1) { - WARNF("More than %d tokens - will use them probabilistically.", + WARNF("More than %u tokens - will use them probabilistically.", afl->max_det_extras); } diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index 0c85458e..5da692d3 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -316,16 +316,20 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, /* Initialize trimming in the custom mutator */ afl->stage_cur = 0; - afl->stage_max = mutator->afl_custom_init_trim(mutator->data, in_buf, q->len); - if (unlikely(afl->stage_max) < 0) { + s32 retval = mutator->afl_custom_init_trim(mutator->data, in_buf, q->len); + if (unlikely(retval) < 0) { - FATAL("custom_init_trim error ret: %d", afl->stage_max); + FATAL("custom_init_trim error ret: %d", retval); + } else { + + afl->stage_max = retval; + } if (afl->not_on_tty && afl->debug) { - SAYF("[Custom Trimming] START: Max %d iterations, %u bytes", afl->stage_max, + SAYF("[Custom Trimming] START: Max %u iterations, %u bytes", afl->stage_max, q->len); } @@ -343,7 +347,7 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, if (unlikely(!retbuf)) { - FATAL("custom_trim failed (ret %zd)", retlen); + FATAL("custom_trim failed (ret %zu)", retlen); } else if (unlikely(retlen > orig_len)) { @@ -409,7 +413,7 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, if (afl->not_on_tty && afl->debug) { - SAYF("[Custom Trimming] SUCCESS: %d/%d iterations (now at %u bytes)", + SAYF("[Custom Trimming] SUCCESS: %u/%u iterations (now at %u bytes)", afl->stage_cur, afl->stage_max, q->len); } @@ -417,16 +421,20 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, } else { /* Tell the custom mutator that the trimming was unsuccessful */ - afl->stage_cur = mutator->afl_custom_post_trim(mutator->data, 0); - if (unlikely(afl->stage_cur < 0)) { + s32 retval2 = mutator->afl_custom_post_trim(mutator->data, 0); + if (unlikely(retval2 < 0)) { + + FATAL("Error ret in custom_post_trim: %d", retval2); + + } else { - FATAL("Error ret in custom_post_trim: %d", afl->stage_cur); + afl->stage_cur = retval2; } if (afl->not_on_tty && afl->debug) { - SAYF("[Custom Trimming] FAILURE: %d/%d iterations", afl->stage_cur, + SAYF("[Custom Trimming] FAILURE: %u/%u iterations", afl->stage_cur, afl->stage_max); } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index e6fa6064..f9509e86 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -368,7 +368,7 @@ static void locate_diffs(u8 *ptr1, u8 *ptr2, u32 len, s32 *first, s32 *last) { u8 fuzz_one_original(afl_state_t *afl) { - s32 len, temp_len; + u32 len, temp_len; u32 j; u32 i; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; @@ -545,7 +545,7 @@ u8 fuzz_one_original(afl_state_t *afl) { else orig_perf = perf_score = calculate_score(afl, afl->queue_cur); - if (unlikely(perf_score <= 0)) { goto abandon_entry; } + if (unlikely(perf_score == 0)) { goto abandon_entry; } if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { @@ -902,7 +902,7 @@ u8 fuzz_one_original(afl_state_t *afl) { orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 1; ++i) { + for (i = 0; i < len - 1; ++i) { /* Let's consult the effector map... */ @@ -945,7 +945,7 @@ u8 fuzz_one_original(afl_state_t *afl) { orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 3; ++i) { + for (i = 0; i < len - 3; ++i) { /* Let's consult the effector map... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] && @@ -1405,7 +1405,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 1; ++i) { + for (i = 0; i < len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -1493,7 +1493,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 3; i++) { + for (i = 0; i < len - 3; i++) { u32 orig = *(u32 *)(out_buf + i); @@ -1850,7 +1850,7 @@ custom_mutator_stage: if (unlikely(!mutated_buf)) { - FATAL("Error in custom_fuzz. Size returned: %zd", mutated_size); + FATAL("Error in custom_fuzz. Size returned: %zu", mutated_size); } @@ -2026,7 +2026,7 @@ havoc_stage: el->data, out_buf, temp_len, &custom_havoc_buf, MAX_FILE); if (unlikely(!custom_havoc_buf)) { - FATAL("Error in custom_havoc (return %zd)", new_len); + FATAL("Error in custom_havoc (return %zu)", new_len); } @@ -2458,7 +2458,7 @@ havoc_stage: u32 use_extra = rand_below(afl, afl->a_extras_cnt); u32 extra_len = afl->a_extras[use_extra].len; - if ((s32)extra_len > temp_len) { break; } + if (extra_len > temp_len) { break; } u32 insert_at = rand_below(afl, temp_len - extra_len + 1); #ifdef INTROSPECTION @@ -2476,7 +2476,7 @@ havoc_stage: u32 use_extra = rand_below(afl, afl->extras_cnt); u32 extra_len = afl->extras[use_extra].len; - if ((s32)extra_len > temp_len) { break; } + if (extra_len > temp_len) { break; } u32 insert_at = rand_below(afl, temp_len - extra_len + 1); #ifdef INTROSPECTION @@ -2577,7 +2577,7 @@ havoc_stage: u32 copy_from, copy_to, copy_len; copy_len = choose_block_len(afl, new_len - 1); - if ((s32)copy_len > temp_len) copy_len = temp_len; + if (copy_len > temp_len) copy_len = temp_len; copy_from = rand_below(afl, new_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); @@ -2952,7 +2952,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { else orig_perf = perf_score = calculate_score(afl, afl->queue_cur); - if (unlikely(perf_score <= 0)) { goto abandon_entry; } + if (unlikely(perf_score == 0)) { goto abandon_entry; } if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 9a0d199e..cd41bafc 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -489,11 +489,12 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { void destroy_queue(afl_state_t *afl) { - struct queue_entry *q; u32 i; for (i = 0; i < afl->queued_paths; i++) { + struct queue_entry *q; + q = afl->queue_buf[i]; ck_free(q->fname); ck_free(q->trace_mini); @@ -996,7 +997,7 @@ inline void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, if (unlikely(!q->testcase_buf)) { - PFATAL("Unable to malloc '%s' with len %d", q->fname, len); + PFATAL("Unable to malloc '%s' with len %u", q->fname, len); } diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index c5db8fa1..7dba1caa 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -445,6 +445,9 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { u32 k; u8 cons_ff = 0, cons_0 = 0; + + if (shape > sizeof(v)) FATAL("shape is greater than %zu, please report!", sizeof(v)); + for (k = 0; k < shape; ++k) { if (b[k] == 0) { @@ -453,7 +456,7 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { } else if (b[k] == 0xff) { - ++cons_0; + ++cons_ff; } else { @@ -667,12 +670,12 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { u8 status = 0; // opt not in the paper - u32 fails = 0; +// u32 fails = 0; u8 found_one = 0; for (i = 0; i < loggeds; ++i) { - fails = 0; + u32 fails = 0; struct cmpfn_operands *o = &((struct cmpfn_operands *)afl->shm.cmp_map->log[key])[i]; diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 339fb9c3..11d8204b 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -682,7 +682,7 @@ void sync_fuzzers(afl_state_t *afl) { // same time. If so, the first temporary main node running again will demote // themselves so this is not an issue - u8 path[PATH_MAX]; +// u8 path2[PATH_MAX]; afl->is_main_node = 1; sprintf(path, "%s/is_main_node", afl->out_dir); int fd = open(path, O_CREAT | O_RDWR, 0644); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 1c211da6..c8366174 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -31,7 +31,6 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { - char *val; u8 fn[PATH_MAX]; snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); FILE *f = create_ffile(fn); @@ -44,6 +43,7 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { for (i = 0; i < s_afl_env; ++i) { + char *val; if ((val = getenv(afl_environment_variables[i])) != NULL) { fprintf(f, "%s=%s\n", afl_environment_variables[i], val); @@ -228,7 +228,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, if (afl->virgin_bits[i] != 0xff) { - fprintf(f, " %d[%02x]", i, afl->virgin_bits[i]); + fprintf(f, " %u[%02x]", i, afl->virgin_bits[i]); } @@ -238,7 +238,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, fprintf(f, "var_bytes :"); for (i = 0; i < afl->fsrv.map_size; i++) { - if (afl->var_bytes[i]) { fprintf(f, " %d", i); } + if (afl->var_bytes[i]) { fprintf(f, " %u", i); } } @@ -1163,7 +1163,7 @@ void show_init_stats(afl_state_t *afl) { } else { - ACTF("-t option specified. We'll use an exec timeout of %d ms.", + ACTF("-t option specified. We'll use an exec timeout of %u ms.", afl->fsrv.exec_tmout); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 897c2f1e..e239b47f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -99,8 +99,8 @@ static void usage(u8 *argv0, int more_help) { " lin, quad> -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " "or @@)\n" - " -t msec - timeout for each run (auto-scaled, 50-%d ms)\n" - " -m megs - memory limit for child process (%d MB, 0 = no limit)\n" + " -t msec - timeout for each run (auto-scaled, 50-%u ms)\n" + " -m megs - memory limit for child process (%u MB, 0 = no limit)\n" " -Q - use binary-only instrumentation (QEMU mode)\n" " -U - use unicorn-based instrumentation (Unicorn mode)\n" " -W - use qemu-based instrumentation with Wine (Wine " @@ -299,7 +299,7 @@ int main(int argc, char **argv_orig, char **envp) { s32 opt, i, auto_sync = 0 /*, user_set_cache = 0*/; u64 prev_queued = 0; - u32 sync_interval_cnt = 0, seek_to = 0, show_help = 0, map_size = MAP_SIZE; + u32 sync_interval_cnt = 0, seek_to = 0, show_help = 0, map_size = get_map_size(); u8 *extras_dir[4]; u8 mem_limit_given = 0, exit_1 = 0, debug = 0, extras_dir_cnt = 0 /*, have_p = 0*/; @@ -326,7 +326,7 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_DEBUG")) { debug = afl->debug = 1; } - map_size = get_map_size(); +// map_size = get_map_size(); afl_state_init(afl, map_size); afl->debug = debug; afl_fsrv_init(&afl->fsrv); diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index fccdb1a5..1d54fda0 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -187,7 +187,7 @@ static void edit_params(int argc, char **argv) { if (debug) DEBUGF( - "passthrough=%s instrim=%d, gold_pos=%d, gold_present=%s " + "passthrough=%s instrim=%u, gold_pos=%u, gold_present=%s " "inst_present=%s rt_present=%s rt_lto_present=%s\n", passthrough ? "true" : "false", instrim, gold_pos, gold_present ? "true" : "false", inst_present ? "true" : "false", @@ -253,10 +253,10 @@ static void edit_params(int argc, char **argv) { int main(int argc, char **argv) { s32 pid, i, status; - u8 * ptr; +// u8 * ptr; char thecwd[PATH_MAX]; - if ((ptr = getenv("AFL_LD_CALLER")) != NULL) { + if (getenv("AFL_LD_CALLER") != NULL) { FATAL("ld loop detected! Set AFL_REAL_LD!\n"); diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 355b2dc3..c0223a07 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -662,7 +662,7 @@ static void usage(u8 *argv0) { "Execution control settings:\n" " -t msec - timeout for each run (none)\n" - " -m megs - memory limit for child process (%d MB)\n" + " -m megs - memory limit for child process (%u MB)\n" " -Q - use binary-only instrumentation (QEMU mode)\n" " -U - use Unicorn-based instrumentation (Unicorn mode)\n" " -W - use qemu-based instrumentation with Wine (Wine mode)\n" @@ -1014,7 +1014,7 @@ int main(int argc, char **argv_orig, char **envp) { DIR * dir_in, *dir_out = NULL; struct dirent *dir_ent; - int done = 0; +// int done = 0; u8 infile[PATH_MAX], outfile[PATH_MAX]; u8 wait_for_gdb = 0; #if !defined(DT_REG) @@ -1090,11 +1090,11 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_DEBUG")) { - int i = optind; + int j = optind; DEBUGF("%s:", fsrv->target_path); - while (argv[i] != NULL) { + while (argv[j] != NULL) { - SAYF(" \"%s\"", argv[i++]); + SAYF(" \"%s\"", argv[j++]); } @@ -1143,7 +1143,7 @@ int main(int argc, char **argv_orig, char **envp) { if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); - while (done == 0 && (dir_ent = readdir(dir_in))) { + while ((dir_ent = readdir(dir_in))) { if (dir_ent->d_name[0] == '.') { diff --git a/src/afl-tmin.c b/src/afl-tmin.c index ed928c7c..09d97f58 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -835,8 +835,8 @@ static void usage(u8 *argv0) { "Execution control settings:\n" " -f file - input file read by the tested program (stdin)\n" - " -t msec - timeout for each run (%d ms)\n" - " -m megs - memory limit for child process (%d MB)\n" + " -t msec - timeout for each run (%u ms)\n" + " -m megs - memory limit for child process (%u MB)\n" " -Q - use binary-only instrumentation (QEMU mode)\n" " -U - use unicorn-based instrumentation (Unicorn mode)\n" " -W - use qemu-based instrumentation with Wine (Wine " -- cgit 1.4.1 From 6c095b3937565e0e3c645cab706269e8c764701e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 4 Jan 2021 23:13:56 +0100 Subject: code format --- instrumentation/afl-compiler-rt.o.c | 19 ++++++++++++++----- instrumentation/afl-llvm-lto-instrumentation.so.cc | 6 +++--- instrumentation/cmplog-instructions-pass.cc | 14 +++++++------- instrumentation/compare-transform-pass.so.cc | 21 ++++++++++++--------- src/afl-cc.c | 15 +++++++++------ src/afl-fuzz-bitmap.c | 2 +- src/afl-fuzz-extras.c | 2 +- src/afl-fuzz-mutators.c | 2 +- src/afl-fuzz-queue.c | 2 +- src/afl-fuzz-redqueen.c | 7 ++++--- src/afl-fuzz-run.c | 2 +- src/afl-fuzz-stats.c | 2 +- src/afl-fuzz.c | 5 +++-- src/afl-ld-lto.c | 4 ++-- src/afl-showmap.c | 6 +++--- 15 files changed, 63 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 0b6c6e47..fdfc8d58 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -691,17 +691,26 @@ static void __afl_start_forkserver(void) { void (*old_sigchld_handler)(int) = 0; // = signal(SIGCHLD, SIG_DFL); - if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) + if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) { + status_for_fsrv |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); - if (__afl_dictionary_len && __afl_dictionary) status_for_fsrv |= FS_OPT_AUTODICT; - if (__afl_sharedmem_fuzzing != 0) status_for_fsrv |= FS_OPT_SHDMEM_FUZZ; - if (status_for_fsrv) status_for_fsrv |= (FS_OPT_ENABLED); + + } + + if (__afl_dictionary_len && __afl_dictionary) { + + status_for_fsrv |= FS_OPT_AUTODICT; + + } + + if (__afl_sharedmem_fuzzing != 0) { status_for_fsrv |= FS_OPT_SHDMEM_FUZZ; } + if (status_for_fsrv) { status_for_fsrv |= (FS_OPT_ENABLED); } memcpy(tmp, &status_for_fsrv, 4); /* Phone home and tell the parent that we're OK. If parent isn't there, assume we're not running in forkserver mode and just execute program. */ - if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; + if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; } if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) { diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc index 89d49936..9cacacf9 100644 --- a/instrumentation/afl-llvm-lto-instrumentation.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc @@ -100,9 +100,9 @@ class AFLLTOPass : public ModulePass { bool AFLLTOPass::runOnModule(Module &M) { - LLVMContext & C = M.getContext(); - std::vector dictionary; -// std::vector calls; + LLVMContext & C = M.getContext(); + std::vector dictionary; + // std::vector calls; DenseMap valueMap; std::vector BlockList; char * ptr; diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index 154bec2b..3499ccf0 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -234,9 +234,9 @@ bool CmpLogInstructions::hookInstrs(Module &M) { auto ty0 = op0->getType(); if (ty0->isHalfTy() #if LLVM_VERSION_MAJOR >= 11 - || ty0->isBFloatTy() + || ty0->isBFloatTy() #endif - ) + ) max_size = 16; else if (ty0->isFloatTy()) max_size = 32; @@ -253,15 +253,15 @@ bool CmpLogInstructions::hookInstrs(Module &M) { if (intTyOp0 && intTyOp1) { max_size = intTyOp0->getBitWidth() > intTyOp1->getBitWidth() - ? intTyOp0->getBitWidth() - : intTyOp1->getBitWidth(); + ? intTyOp0->getBitWidth() + : intTyOp1->getBitWidth(); args.push_back(V0); args.push_back(V1); - + } else { - + max_size = 0; - + } } diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index 887970a0..da5cf7e9 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -68,7 +68,7 @@ class CompareTransform : public ModulePass { const char *getPassName() const override { #else - StringRef getPassName() const override { + StringRef getPassName() const override { #endif return "transforms compare functions"; @@ -106,23 +106,26 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, FunctionCallee tolowerFn; #endif { + #if LLVM_VERSION_MAJOR < 9 - Constant * + Constant * #else - FunctionCallee + FunctionCallee #endif - c = M.getOrInsertFunction("tolower", Int32Ty, Int32Ty + c = M.getOrInsertFunction("tolower", Int32Ty, Int32Ty #if LLVM_VERSION_MAJOR < 5 - , - NULL + , + NULL #endif - ); + ); #if LLVM_VERSION_MAJOR < 9 - tolowerFn = cast(c); + tolowerFn = cast(c); #else - tolowerFn = c; + tolowerFn = c; #endif + } + /* iterate over all functions, bbs and instruction and add suitable calls to * strcmp/memcmp/strncmp/strcasecmp/strncasecmp */ for (auto &F : M) { diff --git a/src/afl-cc.c b/src/afl-cc.c index 180ab3c4..db2dcd14 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -121,9 +121,12 @@ char compiler_mode_string[7][12] = { u8 *getthecwd() { if (getcwd(cwd, sizeof(cwd)) == NULL) { + static u8 fail[] = ""; return fail; + } + return cwd; } @@ -1451,11 +1454,11 @@ int main(int argc, char **argv, char **envp) { "of afl-cc.\n\n"); #if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) -#define NATIVE_MSG \ - " NATIVE: use llvm's native PCGUARD instrumentation (less " \ - "performant)\n" + #define NATIVE_MSG \ + " NATIVE: use llvm's native PCGUARD instrumentation (less " \ + "performant)\n" #else -#define NATIVE_MSG "" + #define NATIVE_MSG "" #endif SAYF( @@ -1463,7 +1466,7 @@ int main(int argc, char **argv, char **envp) { "available)\n" " PCGUARD: Dominator tree instrumentation (best!) (README.llvm.md)\n" - NATIVE_MSG + NATIVE_MSG " CLASSIC: decision target instrumentation (README.llvm.md)\n" " CTX: CLASSIC + callee context (instrumentation/README.ctx.md)\n" @@ -1635,7 +1638,7 @@ int main(int argc, char **argv, char **envp) { if (!instrument_mode) { instrument_mode = INSTRUMENT_CFG; - //ptr = instrument_mode_string[instrument_mode]; + // ptr = instrument_mode_string[instrument_mode]; } diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 85a01f98..ed8c2510 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -703,7 +703,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (!classified) { classify_counts(&afl->fsrv); -// classified = 1; + // classified = 1; } diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 04f0878c..a3583651 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -450,7 +450,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { if (len > MAX_DICT_FILE) { - u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; + u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; WARNF("Extra '%.*s' is too big (%s, limit is %s), skipping file!", (int)len, mem, stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), len), stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index 5da692d3..089707b9 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -324,7 +324,7 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, } else { afl->stage_max = retval; - + } if (afl->not_on_tty && afl->debug) { diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index cd41bafc..7b8c039b 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -489,7 +489,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { void destroy_queue(afl_state_t *afl) { - u32 i; + u32 i; for (i = 0; i < afl->queued_paths; i++) { diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 7dba1caa..28585afe 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -446,7 +446,8 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { u32 k; u8 cons_ff = 0, cons_0 = 0; - if (shape > sizeof(v)) FATAL("shape is greater than %zu, please report!", sizeof(v)); + if (shape > sizeof(v)) + FATAL("shape is greater than %zu, please report!", sizeof(v)); for (k = 0; k < shape; ++k) { @@ -670,8 +671,8 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { u8 status = 0; // opt not in the paper -// u32 fails = 0; - u8 found_one = 0; + // u32 fails = 0; + u8 found_one = 0; for (i = 0; i < loggeds; ++i) { diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 11d8204b..41557707 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -682,7 +682,7 @@ void sync_fuzzers(afl_state_t *afl) { // same time. If so, the first temporary main node running again will demote // themselves so this is not an issue -// u8 path2[PATH_MAX]; + // u8 path2[PATH_MAX]; afl->is_main_node = 1; sprintf(path, "%s/is_main_node", afl->out_dir); int fd = open(path, O_CREAT | O_RDWR, 0644); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index c8366174..e86f2aeb 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -31,7 +31,7 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { - u8 fn[PATH_MAX]; + u8 fn[PATH_MAX]; snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); FILE *f = create_ffile(fn); u32 i; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e239b47f..063134fb 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -299,7 +299,8 @@ int main(int argc, char **argv_orig, char **envp) { s32 opt, i, auto_sync = 0 /*, user_set_cache = 0*/; u64 prev_queued = 0; - u32 sync_interval_cnt = 0, seek_to = 0, show_help = 0, map_size = get_map_size(); + u32 sync_interval_cnt = 0, seek_to = 0, show_help = 0, + map_size = get_map_size(); u8 *extras_dir[4]; u8 mem_limit_given = 0, exit_1 = 0, debug = 0, extras_dir_cnt = 0 /*, have_p = 0*/; @@ -326,7 +327,7 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_DEBUG")) { debug = afl->debug = 1; } -// map_size = get_map_size(); + // map_size = get_map_size(); afl_state_init(afl, map_size); afl->debug = debug; afl_fsrv_init(&afl->fsrv); diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index 1d54fda0..7a4d9132 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -252,8 +252,8 @@ static void edit_params(int argc, char **argv) { int main(int argc, char **argv) { - s32 pid, i, status; -// u8 * ptr; + s32 pid, i, status; + // u8 * ptr; char thecwd[PATH_MAX]; if (getenv("AFL_LD_CALLER") != NULL) { diff --git a/src/afl-showmap.c b/src/afl-showmap.c index c0223a07..d50601fc 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1014,9 +1014,9 @@ int main(int argc, char **argv_orig, char **envp) { DIR * dir_in, *dir_out = NULL; struct dirent *dir_ent; -// int done = 0; - u8 infile[PATH_MAX], outfile[PATH_MAX]; - u8 wait_for_gdb = 0; + // int done = 0; + u8 infile[PATH_MAX], outfile[PATH_MAX]; + u8 wait_for_gdb = 0; #if !defined(DT_REG) struct stat statbuf; #endif -- cgit 1.4.1 From 6b54310452a1b743a90ad45fcc511f59dd7821ec Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 5 Jan 2021 12:30:26 +0100 Subject: selective instrumentation documented --- docs/Changelog.md | 5 ++- include/config.h | 4 ++- instrumentation/README.instrument_list.md | 53 ++++++++++++++++++++----------- src/afl-cc.c | 14 ++++++++ 4 files changed, 55 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 88dc74d2..03b8e036 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,8 +9,11 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . -### Version ++3.01a (release) +### Version ++3.01a (dev) - Mac OS ARM64 support + - New selective instrumentation option with __AFL_COVERAGE_... commands + to be placed in the source code. + Check out instrumentation/README.instrument_list.md - afl-fuzz - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) diff --git a/include/config.h b/include/config.h index ba62d4c6..c0cd0ef1 100644 --- a/include/config.h +++ b/include/config.h @@ -186,7 +186,9 @@ #define TRIM_START_STEPS 16 #define TRIM_END_STEPS 1024 -/* Maximum size of input file, in bytes (keep under 100MB): */ +/* Maximum size of input file, in bytes (keep under 100MB, default 1MB): + (note that if this value is changed, several areas in afl-cc.c, afl-fuzz.c + and afl-fuzz-state.c have to be changed as well! */ #define MAX_FILE (1 * 1024 * 1024U) diff --git a/instrumentation/README.instrument_list.md b/instrumentation/README.instrument_list.md index 122be2b6..83197954 100644 --- a/instrumentation/README.instrument_list.md +++ b/instrumentation/README.instrument_list.md @@ -1,8 +1,9 @@ # Using afl++ with partial instrumentation - This file describes how to selectively instrument only source files - or functions that are of interest to you using the LLVM and GCC_PLUGIN - instrumentation provided by afl++. + This file describes two different mechanisms to selectively instrument + only specific parts in the target. + + Both mechanisms work for LLVM and GCC_PLUGIN, but not for afl-clang/afl-gcc. ## 1) Description and purpose @@ -12,28 +13,42 @@ the program, leaving the rest uninstrumented. This helps to focus the fuzzer on the important parts of the program, avoiding undesired noise and disturbance by uninteresting code being exercised. -For this purpose, a "partial instrumentation" support en par with llvm sancov -is provided by afl++ that allows to specify on a source file and function -level which function should be compiled with or without instrumentation. +For this purpose, "partial instrumentation" support is provided by afl++ that +allows to specify what should be instrumented and what not. + +Both mechanisms can be used together. + +## 2) Selective instrumentation with __AFL_COVERAGE_... directives + +In this mechanism the selective instrumentation is done in the source code. -Note: When using PCGUARD mode - and llvm 12+ - you can use this instead: -https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation +After the includes a special define has to be made, eg.: + +``` +#include +#include +// ... + +__AFL_COVERAGE(); // <- required for this feature to work +``` -The llvm sancov list format is fully supported by afl++, however afl++ has -more flexibility. +If you want to disable the coverage at startup until you specify coverage +should be started, then add `__AFL_COVERAGE_START_OFF();` at that position. -## 2a) Building the LLVM module +From here on out you have the following macros available that you can use +in any function where you want: -The new code is part of the existing afl++ LLVM module in the instrumentation/ -subdirectory. There is nothing specifically to do for the build :) + * `__AFL_COVERAGE_ON();` - enable coverage from this point onwards + * `__AFL_COVERAGE_OFF();` - disable coverage from this point onwards + * `__AFL_COVERAGE_DISCARD();` - reset all coverage gathered until this point + * `__AFL_COVERAGE_ABORT();` - mark this test case as unimportant. Whatever happens, afl-fuzz will ignore it. -## 2b) Building the GCC module +## 3) Selective instrumenation with AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST -The new code is part of the existing afl++ GCC_PLUGIN module in the -instrumentation/ subdirectory. There is nothing specifically to do for -the build :) +This feature is equivalent to llvm 12 sancov feature and allows to specify +on a filename and/or function name level to instrument these or skip them. -## 3) How to use the partial instrumentation mode +### 3a) How to use the partial instrumentation mode In order to build with partial instrumentation, you need to build with afl-clang-fast/afl-clang-fast++ or afl-clang-lto/afl-clang-lto++. @@ -90,7 +105,7 @@ fun: MallocFoo ``` Note that whitespace is ignored and comments (`# foo`) are supported. -## 4) UNIX-style pattern matching +### 3b) UNIX-style pattern matching You can add UNIX-style pattern matching in the "instrument file list" entries. See `man fnmatch` for the syntax. We do not set any of the `fnmatch` flags. diff --git a/src/afl-cc.c b/src/afl-cc.c index db2dcd14..964df57f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -827,6 +827,20 @@ static void edit_params(u32 argc, char **argv, char **envp) { "extern unsigned char *__afl_fuzz_ptr;" "unsigned char __afl_fuzz_alt[1048576];" "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "void __afl_coverage_discard();" + "void __afl_coverage_abort();" + "void __afl_coverage_on();" + "void __afl_coverage_off();"; + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE_START_OFF()=int __afl_selective_coverage_start_off = " + "1;"; + cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_ON()=__afl_coverage_on()"; + cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_OFF()=__afl_coverage_off()"; + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()"; + cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_ABORT()=__afl_coverage_abort()"; cc_params[cc_par_cnt++] = "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " "__afl_fuzz_alt_ptr)"; -- cgit 1.4.1 From faefad564be4b6bd8b311cb4990e8fd33acb5c1c Mon Sep 17 00:00:00 2001 From: hexcoder Date: Tue, 5 Jan 2021 16:49:04 +0000 Subject: Haiku afl-system-config disable debugger, afl-cc.c avoid -lrt --- afl-system-config | 9 +++++++++ src/afl-cc.c | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/afl-system-config b/afl-system-config index 919932c3..456cccac 100755 --- a/afl-system-config +++ b/afl-system-config @@ -84,5 +84,14 @@ if [ "$PLATFORM" = "Darwin" ] ; then fi DONE=1 fi +if [ "$PLATFORM" = "Haiku" ] ; then + SETTINGS=~/config/settings/system/debug_server/settings + [ -r ${SETTINGS} ] && grep -qE "default_action\s+kill" ${SETTINGS} && { echo "Nothing to do"; } || { \ + echo We change the debug_server default_action from user to silenty kill; \ + [ ! -r ${SETTINGS} ] && echo "default_action kill" >${SETTINGS} || { mv ${SETTINGS} s.tmp; sed -e "s/default_action\s\s*user/default_action kill/" s.tmp > ${SETTINGS}; rm s.tmp; }; \ + echo Settings applied.; \ + } + DONE=1 +fi test -z "$DONE" && echo Error: Unknown platform: $PLATFORM exit 0 diff --git a/src/afl-cc.c b/src/afl-cc.c index 964df57f..27bf8cf0 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -792,8 +792,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { } -#if defined(USEMMAP) && !defined(__HAIKU__) +#if defined(USEMMAP) +#if !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; +#endif #endif cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; @@ -950,9 +952,11 @@ static void edit_params(u32 argc, char **argv, char **envp) { alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif - #ifdef USEMMAP + #if defined(USEMMAP) + #if !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; #endif + #endif } @@ -1622,12 +1626,17 @@ int main(int argc, char **argv, char **envp) { if (have_lto) SAYF("afl-cc LTO with ld=%s %s\n", AFL_REAL_LD, AFL_CLANG_FLTO); if (have_llvm) - SAYF("afl-cc LLVM version %d with the the binary path \"%s\".\n", + SAYF("afl-cc LLVM version %d using binary path \"%s\".\n", LLVM_MAJOR, LLVM_BINDIR); #endif -#ifdef USEMMAP +#if defined(USEMMAP) +#if !defined(__HAIKU__) + cc_params[cc_par_cnt++] = "-lrt"; SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); +#else + SAYF("Compiled with shm_open support.\n"); +#endif #else SAYF("Compiled with shmat support.\n"); #endif -- cgit 1.4.1 From 4c78bb70802a85ffad6aee0f234f26901cd563f9 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 5 Jan 2021 22:13:55 +0100 Subject: cpp fix --- instrumentation/afl-compiler-rt.o.c | 4 ++-- src/afl-cc.c | 25 +++++++++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index daa50ffa..bb62fb38 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1409,8 +1409,8 @@ void __afl_coverage_on() { // discard all coverage up to this point void __afl_coverage_discard() { - memset(__afl_area_ptr, 0, __afl_map_size); - __afl_area_ptr[0] = 1; + memset(__afl_area_ptr_backup, 0, __afl_map_size); + __afl_area_ptr_backup[0] = 1; if (__afl_cmp_map) { memset(__afl_cmp_map, 0, sizeof(struct cmp_map)); } diff --git a/src/afl-cc.c b/src/afl-cc.c index 27bf8cf0..e61dc6d2 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -829,12 +829,25 @@ static void edit_params(u32 argc, char **argv, char **envp) { "extern unsigned char *__afl_fuzz_ptr;" "unsigned char __afl_fuzz_alt[1048576];" "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; - cc_params[cc_par_cnt++] = - "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" - "void __afl_coverage_discard();" - "void __afl_coverage_abort();" - "void __afl_coverage_on();" - "void __afl_coverage_off();"; + if (plusplus_mode) { + + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "extern \"C\" void __afl_coverage_discard();" + "extern \"C\" void __afl_coverage_abort();" + "extern \"C\" void __afl_coverage_on();" + "extern \"C\" void __afl_coverage_off();"; + + } else { + + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "void __afl_coverage_discard();" + "void __afl_coverage_abort();" + "void __afl_coverage_on();" + "void __afl_coverage_off();"; + + } + cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_START_OFF()=int __afl_selective_coverage_start_off = " "1;"; -- cgit 1.4.1 From 52c221fc484317d2cd3926ae31ac891bad8cc93a Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 6 Jan 2021 09:35:47 +0100 Subject: selective coverage fix --- instrumentation/afl-compiler-rt.o.c | 10 +++------- src/afl-cc.c | 30 ++++++++++++++++-------------- 2 files changed, 19 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index bb62fb38..add303d7 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1435,19 +1435,15 @@ void __afl_coverage_abort() { } // mark this area as especially interesting -void __afl_coverage_interesting(u32 id, u8 val) { +void __afl_coverage_interesting(u8 val, u32 id) { - if (val) { + if (id) { __afl_area_ptr[id] = val; } else { - do { - - __afl_area_ptr[id] = (u8)rand(); - - } while (!__afl_area_ptr[id]); + __afl_area_ptr[(rand() % __afl_map_size)] = val; } diff --git a/src/afl-cc.c b/src/afl-cc.c index e61dc6d2..999ee7f1 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -793,9 +793,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if defined(USEMMAP) -#if !defined(__HAIKU__) + #if !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; -#endif + #endif #endif cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; @@ -829,13 +829,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { "extern unsigned char *__afl_fuzz_ptr;" "unsigned char __afl_fuzz_alt[1048576];" "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; + if (plusplus_mode) { - "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" - "extern \"C\" void __afl_coverage_discard();" - "extern \"C\" void __afl_coverage_abort();" - "extern \"C\" void __afl_coverage_on();" - "extern \"C\" void __afl_coverage_off();"; + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "extern \"C\" void __afl_coverage_discard();" + "extern \"C\" void __afl_coverage_abort();" + "extern \"C\" void __afl_coverage_on();" + "extern \"C\" void __afl_coverage_off();"; } else { @@ -966,9 +968,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { #endif #if defined(USEMMAP) - #if !defined(__HAIKU__) + #if !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; - #endif + #endif #endif } @@ -1639,17 +1641,17 @@ int main(int argc, char **argv, char **envp) { if (have_lto) SAYF("afl-cc LTO with ld=%s %s\n", AFL_REAL_LD, AFL_CLANG_FLTO); if (have_llvm) - SAYF("afl-cc LLVM version %d using binary path \"%s\".\n", - LLVM_MAJOR, LLVM_BINDIR); + SAYF("afl-cc LLVM version %d using binary path \"%s\".\n", LLVM_MAJOR, + LLVM_BINDIR); #endif #if defined(USEMMAP) -#if !defined(__HAIKU__) + #if !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); -#else + #else SAYF("Compiled with shm_open support.\n"); -#endif + #endif #else SAYF("Compiled with shmat support.\n"); #endif -- cgit 1.4.1 From 9cdf5c415015e4e80b577c021b8b9fcf8a3d58fb Mon Sep 17 00:00:00 2001 From: buherator Date: Thu, 7 Jan 2021 22:35:34 +0100 Subject: User defined kill signal value (#678) * Adding AFL_KILL_SIGNAL environment variable Controlling the kill signal used to end forked processes. * Checking validity of AFL_KILL_SIGNAL env variable This commit also sets a valid value in the environment to avoid duplicating code in at_exit(). Changing data type of fsrv->kill_signal to u8 to match last_kill_signal. * Adding afl_kill_signal to AFL (environment) state This commit simply introduces a struct member for future use. The env variable is not used from the afl struct but from fsrv, where its validity is checked, resulting in a FATAL in case of errors. --- include/afl-fuzz.h | 2 +- include/envs.h | 1 + include/forkserver.h | 2 ++ src/afl-forkserver.c | 44 +++++++++++++++++++++++++++++++++++++++----- src/afl-fuzz-state.c | 11 +++++++++-- src/afl-fuzz.c | 13 +++++++++++-- 6 files changed, 63 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index ede54f0e..988a907d 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -381,7 +381,7 @@ typedef struct afl_env_vars { *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload, *afl_max_det_extras, *afl_statsd_host, *afl_statsd_port, *afl_crash_exitcode, *afl_statsd_tags_flavor, *afl_testcache_size, - *afl_testcache_entries; + *afl_testcache_entries, *afl_kill_signal; } afl_env_vars_t; diff --git a/include/envs.h b/include/envs.h index e4e49c4d..97367fae 100644 --- a/include/envs.h +++ b/include/envs.h @@ -61,6 +61,7 @@ static char *afl_environment_variables[] = { "AFL_IMPORT_FIRST", "AFL_INST_LIBS", "AFL_INST_RATIO", + "AFL_KILL_SIGNAL", "AFL_KEEP_TRACES", "AFL_KEEP_ASSEMBLY", "AFL_LD_HARD_FAIL", diff --git a/include/forkserver.h b/include/forkserver.h index 8e029266..3019e289 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -99,6 +99,8 @@ typedef struct afl_forkserver { void (*add_extra_func)(void *afl_ptr, u8 *mem, u32 len); + u8 kill_signal; + } afl_forkserver_t; typedef enum fsrv_run_result { diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index d6195cb5..70fb9572 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -95,6 +95,29 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->uses_asan = false; fsrv->init_child_func = fsrv_exec_child; + fsrv->kill_signal = SIGKILL; + + char *kill_signal_env = get_afl_env("AFL_KILL_SIGNAL"); + if (kill_signal_env) { + + char *endptr; + u8 signal_code; + signal_code = (u8)strtoul(kill_signal_env, &endptr, 10); + /* Did we manage to parse the full string? */ + if (*endptr != '\0' || endptr == kill_signal_env) { + + FATAL("Invalid kill signal value!"); + + } + + fsrv->kill_signal = signal_code; + + } else { + + /* Using hardcoded code for SIGKILL for the sake of simplicity */ + setenv("AFL_KILL_SIGNAL", "9", 1); + + } list_append(&fsrv_list, fsrv); @@ -126,6 +149,8 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->init_child_func = from->init_child_func; // Note: do not copy ->add_extra_func + fsrv_to->kill_signal = from->kill_signal; + list_append(&fsrv_list, fsrv_to); } @@ -559,12 +584,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if (!time_ms) { - kill(fsrv->fsrv_pid, SIGKILL); + kill(fsrv->fsrv_pid, fsrv->kill_signal); } else if (time_ms > fsrv->init_tmout) { fsrv->last_run_timed_out = 1; - kill(fsrv->fsrv_pid, SIGKILL); + kill(fsrv->fsrv_pid, fsrv->kill_signal); } else { @@ -944,10 +969,10 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, static void afl_fsrv_kill(afl_forkserver_t *fsrv) { - if (fsrv->child_pid > 0) { kill(fsrv->child_pid, SIGKILL); } + if (fsrv->child_pid > 0) { kill(fsrv->child_pid, fsrv->kill_signal); } if (fsrv->fsrv_pid > 0) { - kill(fsrv->fsrv_pid, SIGKILL); + kill(fsrv->fsrv_pid, fsrv->kill_signal); if (waitpid(fsrv->fsrv_pid, NULL, 0) <= 0) { WARNF("error waitpid\n"); } } @@ -1091,7 +1116,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, /* If there was no response from forkserver after timeout seconds, we kill the child. The forkserver should inform us afterwards */ - kill(fsrv->child_pid, SIGKILL); + kill(fsrv->child_pid, fsrv->kill_signal); fsrv->last_run_timed_out = 1; if (read(fsrv->fsrv_st_fd, &fsrv->child_status, 4) < 4) { exec_ms = 0; } @@ -1137,6 +1162,15 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, /* Report outcome to caller. */ + /* TODO We use SIGTERM here as an indicator of Xen mode, + although it's not equivalent! */ + if (fsrv->kill_signal == SIGTERM && !*stop_soon_p && + fsrv->last_run_timed_out) { + + return FSRV_RUN_TMOUT; + + } + if (WIFSIGNALED(fsrv->child_status) && !*stop_soon_p) { fsrv->last_kill_signal = WTERMSIG(fsrv->child_status); diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 34456c0d..60c9684c 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -418,6 +418,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { (u8 *)get_afl_env(afl_environment_variables[i]); #endif + } else if (!strncmp(env, "AFL_KILL_SIGNAL", + + afl_environment_variable_len)) { + + afl->afl_env.afl_kill_signal = + (u8 *)get_afl_env(afl_environment_variables[i]); + } } else { @@ -524,8 +531,8 @@ void afl_states_stop(void) { LIST_FOREACH(&afl_states, afl_state_t, { - if (el->fsrv.child_pid > 0) kill(el->fsrv.child_pid, SIGKILL); - if (el->fsrv.fsrv_pid > 0) kill(el->fsrv.fsrv_pid, SIGKILL); + if (el->fsrv.child_pid > 0) kill(el->fsrv.child_pid, el->fsrv.kill_signal); + if (el->fsrv.fsrv_pid > 0) kill(el->fsrv.fsrv_pid, el->fsrv.kill_signal); }); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 063134fb..00625f2e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -76,8 +76,17 @@ static void at_exit() { } - if (pid1 > 0) { kill(pid1, SIGKILL); } - if (pid2 > 0) { kill(pid2, SIGKILL); } + u8 kill_signal = SIGKILL; + + /* AFL_KILL_SIGNAL should already be initialized by afl_fsrv_init() */ + if (getenv("AFL_KILL_SIGNAL")) { + + kill_signal = atoi(getenv("AFL_KILL_SIGNAL")); + + } + + if (pid1 > 0) { kill(pid1, kill_signal); } + if (pid2 > 0) { kill(pid2, kill_signal); } } -- cgit 1.4.1 From a06b25538fd7b6eef6755094aa4678c2cb5333fd Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Thu, 7 Jan 2021 23:21:10 +0100 Subject: cleaned up AFL_KILL_SIGNAL --- afl-cmin | 1 + docs/Changelog.md | 2 +- docs/env_variables.md | 4 ++++ src/afl-forkserver.c | 45 +++++++-------------------------------------- src/afl-fuzz.c | 30 +++++++++++++++++++++++++++++- src/afl-showmap.c | 37 +++++++++++++++++++++++++++++++++---- src/afl-tmin.c | 29 +++++++++++++++++++++++++++++ 7 files changed, 104 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/afl-cmin b/afl-cmin index eef2b7ef..726e90ab 100755 --- a/afl-cmin +++ b/afl-cmin @@ -120,6 +120,7 @@ function usage() { "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" \ "AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" \ "AFL_KEEP_TRACES: leave the temporary /.traces directory\n" \ +"AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n" "AFL_PATH: path for the afl-showmap binary if not found anywhere else\n" \ "AFL_SKIP_BIN_CHECK: skip check for target binary\n" exit 1 diff --git a/docs/Changelog.md b/docs/Changelog.md index 03b8e036..81ac91b0 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -32,7 +32,7 @@ sending a mail to . already building with all cores, the gcc plugin needs only one. - added dummy Makefile to instrumentation/ - Updated utils/afl_frida to be 5% faster - + - Added AFL_KILL_SIGNAL env variable for custom targets (thanks @v-p-b) ### Version ++3.00c (release) - llvm_mode/ and gcc_plugin/ moved to instrumentation/ diff --git a/docs/env_variables.md b/docs/env_variables.md index e6b9381b..26128b01 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -350,6 +350,10 @@ checks or alter some of the more exotic semantics of the tool: - Note that `AFL_POST_LIBRARY` is deprecated, use `AFL_CUSTOM_MUTATOR_LIBRARY` instead (see below). + - `AFL_KILL_SIGNAL`: Set the signal ID to be delivered to child processes on timeout. + Unless you implement your own targets or instrumentation, you likely don't have to set it. + By default, on timeout and on exit, `SIGKILL` (`AFL_KILL_SIGNAL=9`) will be delivered to the child. + - Setting `AFL_CUSTOM_MUTATOR_LIBRARY` to a shared library with afl_custom_fuzz() creates additional mutations through this library. If afl-fuzz is compiled with Python (which is autodetected during builing diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 70fb9572..b7aa87f8 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -84,6 +84,7 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->init_tmout = EXEC_TIMEOUT * FORK_WAIT_MULT; fsrv->mem_limit = MEM_LIMIT; fsrv->out_file = NULL; + fsrv->kill_signal = SIGKILL; /* exec related stuff */ fsrv->child_pid = -1; @@ -95,30 +96,6 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->uses_asan = false; fsrv->init_child_func = fsrv_exec_child; - fsrv->kill_signal = SIGKILL; - - char *kill_signal_env = get_afl_env("AFL_KILL_SIGNAL"); - if (kill_signal_env) { - - char *endptr; - u8 signal_code; - signal_code = (u8)strtoul(kill_signal_env, &endptr, 10); - /* Did we manage to parse the full string? */ - if (*endptr != '\0' || endptr == kill_signal_env) { - - FATAL("Invalid kill signal value!"); - - } - - fsrv->kill_signal = signal_code; - - } else { - - /* Using hardcoded code for SIGKILL for the sake of simplicity */ - setenv("AFL_KILL_SIGNAL", "9", 1); - - } - list_append(&fsrv_list, fsrv); } @@ -139,6 +116,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->no_unlink = from->no_unlink; fsrv_to->uses_crash_exitcode = from->uses_crash_exitcode; fsrv_to->crash_exitcode = from->crash_exitcode; + fsrv_to->kill_signal = from->kill_signal; // These are forkserver specific. fsrv_to->out_dir_fd = -1; @@ -149,8 +127,6 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->init_child_func = from->init_child_func; // Note: do not copy ->add_extra_func - fsrv_to->kill_signal = from->kill_signal; - list_append(&fsrv_list, fsrv_to); } @@ -1162,25 +1138,18 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, /* Report outcome to caller. */ - /* TODO We use SIGTERM here as an indicator of Xen mode, - although it's not equivalent! */ - if (fsrv->kill_signal == SIGTERM && !*stop_soon_p && - fsrv->last_run_timed_out) { + /* Did we timeout? */ + if (unlikely(fsrv->last_run_timed_out)) { + fsrv->last_kill_signal = fsrv->kill_signal; return FSRV_RUN_TMOUT; } - if (WIFSIGNALED(fsrv->child_status) && !*stop_soon_p) { + /* Did we crash? */ + if (unlikely(WIFSIGNALED(fsrv->child_status) && !*stop_soon_p)) { fsrv->last_kill_signal = WTERMSIG(fsrv->child_status); - - if (fsrv->last_run_timed_out && fsrv->last_kill_signal == SIGKILL) { - - return FSRV_RUN_TMOUT; - - } - return FSRV_RUN_CRASH; } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 00625f2e..5c363c63 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -194,10 +194,11 @@ static void usage(u8 *argv0, int more_help) { "AFL_EXPAND_HAVOC_NOW: immediately enable expand havoc mode (default: after 60 minutes and a cycle without finds)\n" "AFL_FAST_CAL: limit the calibration stage to three cycles for speedup\n" "AFL_FORCE_UI: force showing the status screen (for virtual consoles)\n" - "AFL_HANG_TMOUT: override timeout value (in milliseconds)\n" "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" + "AFL_HANG_TMOUT: override timeout value (in milliseconds)\n" "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: don't warn about core dump handlers\n" "AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n" + "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" " the target was compiled for\n" "AFL_MAX_DET_EXTRAS: if more entries are in the dictionary list than this value\n" @@ -986,6 +987,33 @@ int main(int argc, char **argv_orig, char **envp) { #endif + afl->fsrv.kill_signal = SIGKILL; + if (afl->afl_env.afl_kill_signal) { + + char *endptr; + u8 signal_code; + signal_code = (u8)strtoul(afl->afl_env.afl_kill_signal, &endptr, 10); + /* Did we manage to parse the full string? */ + if (*endptr != '\0' || endptr == (char *)afl->afl_env.afl_kill_signal) { + + FATAL("Invalid AFL_KILL_SIGNAL: %s (expected unsigned int)", + afl->afl_env.afl_kill_signal); + + } + + afl->fsrv.kill_signal = signal_code; + + } else { + + char *sigstr = alloc_printf("%d", (int)SIGKILL); + if (!sigstr) { FATAL("Failed to alloc mem for signal buf"); } + + /* Set the env for signal handler */ + setenv("AFL_KILL_SIGNAL", sigstr, 1); + free(sigstr); + + } + setup_signal_handlers(); check_asan_opts(afl); diff --git a/src/afl-showmap.c b/src/afl-showmap.c index d50601fc..2c9c38ed 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -693,12 +693,13 @@ static void usage(u8 *argv0) { "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as " "crash\n" "AFL_DEBUG: enable extra developer output\n" - "AFL_MAP_SIZE: the shared memory size for that target. must be >= the " - "size\n" - " the target was compiled for\n" - "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during " "startup (in milliseconds)\n" + "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, " + "etc. (default: SIGKILL)\n" + "AFL_MAP_SIZE: the shared memory size for that target. must be >= the " + "size the target was compiled for\n" + "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_QUIET: do not print extra informational output\n", argv0, MEM_LIMIT, doc_path); @@ -1115,6 +1116,34 @@ int main(int argc, char **argv_orig, char **envp) { } + fsrv->kill_signal = SIGKILL; + char *afl_kill_signal_env = getenv("AFL_KILL_SIGNAL"); + if (afl_kill_signal_env && afl_kill_signal_env[0]) { + + char *endptr; + u8 signal_code; + signal_code = (u8)strtoul(afl_kill_signal_env, &endptr, 10); + /* Did we manage to parse the full string? */ + if (*endptr != '\0' || endptr == afl_kill_signal_env) { + + FATAL("Invalid AFL_KILL_SIGNAL: %s (expected unsigned int)", + afl_kill_signal_env); + + } + + fsrv->kill_signal = signal_code; + + } else { + + char *sigstr = alloc_printf("%d", (int)SIGKILL); + if (!sigstr) { FATAL("Failed to alloc mem for signal buf"); } + + /* Set the env for signal handler */ + setenv("AFL_KILL_SIGNAL", sigstr, 1); + free(sigstr); + + } + if (getenv("AFL_CRASH_EXITCODE")) { long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10); diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 09d97f58..342de9c8 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -855,6 +855,7 @@ static void usage(u8 *argv0) { "Environment variables used:\n" "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" + "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" @@ -1134,6 +1135,34 @@ int main(int argc, char **argv_orig, char **envp) { } + fsrv->kill_signal = SIGKILL; + char *afl_kill_signal_env = getenv("AFL_KILL_SIGNAL"); + if (afl_kill_signal_env && afl_kill_signal_env[0]) { + + char *endptr; + u8 signal_code; + signal_code = (u8)strtoul(afl_kill_signal_env, &endptr, 10); + /* Did we manage to parse the full string? */ + if (*endptr != '\0' || endptr == afl_kill_signal_env) { + + FATAL("Invalid AFL_KILL_SIGNAL: %s (expected unsigned int)", + afl_kill_signal_env); + + } + + fsrv->kill_signal = signal_code; + + } else { + + char *sigstr = alloc_printf("%d", (int)SIGKILL); + if (!sigstr) { FATAL("Failed to alloc mem for signal buf"); } + + /* Set the env for signal handler */ + setenv("AFL_KILL_SIGNAL", sigstr, 1); + free(sigstr); + + } + if (getenv("AFL_CRASH_EXITCODE")) { long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10); -- cgit 1.4.1 From 7b8c8cf12fde0feab25a1d794e010a5778cf9be8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 8 Jan 2021 00:47:52 +0100 Subject: fix --- src/afl-fuzz-queue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index de750f36..c375703d 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -56,7 +56,7 @@ double compute_weight(afl_state_t *afl, struct queue_entry *q, } if (likely(afl->schedule < RARE)) { weight *= (avg_exec_us / q->exec_us); } - weight *= (q->bitmap_size / avg_bitmap_size); + weight *= (log(q->bitmap_size) / avg_bitmap_size); weight *= (log(q->tc_ref) / avg_top_size); if (unlikely(q->favored)) weight *= 5; @@ -103,7 +103,7 @@ void create_alias_table(afl_state_t *afl) { if (likely(!q->disabled)) { avg_exec_us += q->exec_us; - avg_bitmap_size += q->bitmap_size; + avg_bitmap_size += log(q->bitmap_size); avg_top_size += log(q->tc_ref); ++active; -- cgit 1.4.1 From 34732e3c5ede9020ff1802f0f0827e3731217dce Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 8 Jan 2021 11:36:40 +0100 Subject: refactored kill signal env parsing --- include/common.h | 5 +++++ src/afl-common.c | 34 ++++++++++++++++++++++++++++++++++ src/afl-fuzz.c | 32 ++++---------------------------- src/afl-showmap.c | 29 ++--------------------------- src/afl-tmin.c | 29 ++--------------------------- 5 files changed, 47 insertions(+), 82 deletions(-) (limited to 'src') diff --git a/include/common.h b/include/common.h index 125c3abf..9490ec5f 100644 --- a/include/common.h +++ b/include/common.h @@ -56,6 +56,11 @@ extern u8 *doc_path; /* path to documentation dir */ u8 *find_binary(u8 *fname); +/* Parses the kill signal environment variable, FATALs on error. + If the env is not set, sets the env to default_signal for the signal handlers + and returns the default_signal. */ +int parse_afl_kill_signal_env(u8 *afl_kill_signal_env, int default_signal); + /* Read a bitmap from file fname to memory This is for the -B option again. */ diff --git a/src/afl-common.c b/src/afl-common.c index 21cb6ab4..3a7d0ce5 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -424,6 +424,40 @@ u8 *find_binary(u8 *fname) { } +/* Parses the kill signal environment variable, FATALs on error. + If the env is not set, sets the env to default_signal for the signal handlers + and returns the default_signal. */ +int parse_afl_kill_signal_env(u8 *afl_kill_signal_env, int default_signal) { + + if (afl_kill_signal_env && afl_kill_signal_env[0]) { + + char *endptr; + u8 signal_code; + signal_code = (u8)strtoul(afl_kill_signal_env, &endptr, 10); + /* Did we manage to parse the full string? */ + if (*endptr != '\0' || endptr == (char *)afl_kill_signal_env) { + + FATAL("Invalid AFL_KILL_SIGNAL: %s (expected unsigned int)", + afl_kill_signal_env); + + } + + return signal_code; + + } else { + + char *sigstr = alloc_printf("%d", default_signal); + if (!sigstr) { FATAL("Failed to alloc mem for signal buf"); } + + /* Set the env for signal handler */ + setenv("AFL_KILL_SIGNAL", sigstr, 1); + free(sigstr); + return default_signal; + + } + +} + void check_environment_vars(char **envp) { if (be_quiet) { return; } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 5c363c63..37f8db8a 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -76,9 +76,9 @@ static void at_exit() { } - u8 kill_signal = SIGKILL; + int kill_signal = SIGKILL; - /* AFL_KILL_SIGNAL should already be initialized by afl_fsrv_init() */ + /* AFL_KILL_SIGNAL should already be a valid int at this point */ if (getenv("AFL_KILL_SIGNAL")) { kill_signal = atoi(getenv("AFL_KILL_SIGNAL")); @@ -987,32 +987,8 @@ int main(int argc, char **argv_orig, char **envp) { #endif - afl->fsrv.kill_signal = SIGKILL; - if (afl->afl_env.afl_kill_signal) { - - char *endptr; - u8 signal_code; - signal_code = (u8)strtoul(afl->afl_env.afl_kill_signal, &endptr, 10); - /* Did we manage to parse the full string? */ - if (*endptr != '\0' || endptr == (char *)afl->afl_env.afl_kill_signal) { - - FATAL("Invalid AFL_KILL_SIGNAL: %s (expected unsigned int)", - afl->afl_env.afl_kill_signal); - - } - - afl->fsrv.kill_signal = signal_code; - - } else { - - char *sigstr = alloc_printf("%d", (int)SIGKILL); - if (!sigstr) { FATAL("Failed to alloc mem for signal buf"); } - - /* Set the env for signal handler */ - setenv("AFL_KILL_SIGNAL", sigstr, 1); - free(sigstr); - - } + afl->fsrv.kill_signal = + parse_afl_kill_signal_env(afl->afl_env.afl_kill_signal, SIGKILL); setup_signal_handlers(); check_asan_opts(afl); diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 2c9c38ed..5c9d38e0 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1116,33 +1116,8 @@ int main(int argc, char **argv_orig, char **envp) { } - fsrv->kill_signal = SIGKILL; - char *afl_kill_signal_env = getenv("AFL_KILL_SIGNAL"); - if (afl_kill_signal_env && afl_kill_signal_env[0]) { - - char *endptr; - u8 signal_code; - signal_code = (u8)strtoul(afl_kill_signal_env, &endptr, 10); - /* Did we manage to parse the full string? */ - if (*endptr != '\0' || endptr == afl_kill_signal_env) { - - FATAL("Invalid AFL_KILL_SIGNAL: %s (expected unsigned int)", - afl_kill_signal_env); - - } - - fsrv->kill_signal = signal_code; - - } else { - - char *sigstr = alloc_printf("%d", (int)SIGKILL); - if (!sigstr) { FATAL("Failed to alloc mem for signal buf"); } - - /* Set the env for signal handler */ - setenv("AFL_KILL_SIGNAL", sigstr, 1); - free(sigstr); - - } + fsrv->kill_signal = + parse_afl_kill_signal_env(getenv("AFL_KILL_SIGNAL"), SIGKILL); if (getenv("AFL_CRASH_EXITCODE")) { diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 342de9c8..6e2d7708 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -1135,33 +1135,8 @@ int main(int argc, char **argv_orig, char **envp) { } - fsrv->kill_signal = SIGKILL; - char *afl_kill_signal_env = getenv("AFL_KILL_SIGNAL"); - if (afl_kill_signal_env && afl_kill_signal_env[0]) { - - char *endptr; - u8 signal_code; - signal_code = (u8)strtoul(afl_kill_signal_env, &endptr, 10); - /* Did we manage to parse the full string? */ - if (*endptr != '\0' || endptr == afl_kill_signal_env) { - - FATAL("Invalid AFL_KILL_SIGNAL: %s (expected unsigned int)", - afl_kill_signal_env); - - } - - fsrv->kill_signal = signal_code; - - } else { - - char *sigstr = alloc_printf("%d", (int)SIGKILL); - if (!sigstr) { FATAL("Failed to alloc mem for signal buf"); } - - /* Set the env for signal handler */ - setenv("AFL_KILL_SIGNAL", sigstr, 1); - free(sigstr); - - } + fsrv->kill_signal = + parse_afl_kill_signal_env(getenv("AFL_KILL_SIGNAL"), SIGKILL); if (getenv("AFL_CRASH_EXITCODE")) { -- cgit 1.4.1 From 3d1a25ce4e8862b82662c64fbea000692f300c62 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 8 Jan 2021 11:54:35 +0100 Subject: added check to silence scan-build --- src/afl-fuzz-run.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 41557707..cc2ef891 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -430,6 +430,13 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, /* OK, let's collect some stats about the performance of this test case. This is used for fuzzing air time calculations in calculate_score(). */ + if (unlikely(!afl->stage_max)) { + + // Pretty sure this cannot happen, yet scan-build complains. + FATAL("BUG: stage_max should not be 0 here! Please report this condition."); + + } + q->exec_us = (stop_us - start_us) / afl->stage_max; q->bitmap_size = count_bytes(afl, afl->fsrv.trace_bits); q->handicap = handicap; -- cgit 1.4.1 From 3196a5f666acc29e42e8e5e49099bab4a7b2deba Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 8 Jan 2021 18:17:52 +0100 Subject: cleaned up and added comments to forkserver exits --- src/afl-forkserver.c | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index b7aa87f8..413676bf 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -1138,38 +1138,43 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, /* Report outcome to caller. */ - /* Did we timeout? */ - if (unlikely(fsrv->last_run_timed_out)) { + /* Was the run unsuccessful? */ + if (unlikely(*(u32 *)fsrv->trace_bits == EXEC_FAIL_SIG)) { - fsrv->last_kill_signal = fsrv->kill_signal; - return FSRV_RUN_TMOUT; + return FSRV_RUN_ERROR; } - /* Did we crash? */ - if (unlikely(WIFSIGNALED(fsrv->child_status) && !*stop_soon_p)) { + /* Did we timeout? */ + if (unlikely(fsrv->last_run_timed_out)) { - fsrv->last_kill_signal = WTERMSIG(fsrv->child_status); - return FSRV_RUN_CRASH; + fsrv->last_kill_signal = fsrv->kill_signal; + return FSRV_RUN_TMOUT; } - /* MSAN in uses_asan mode uses a special exit code as it doesn't support - abort_on_error. - On top, a user may specify a custom AFL_CRASH_EXITCODE. Handle both here. */ - - if ((fsrv->uses_asan && WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) || - (fsrv->uses_crash_exitcode && - WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode)) { - - fsrv->last_kill_signal = 0; + /* Did we crash? + In a normal case, (abort) WIFSIGNALED(child_status) will be set. + MSAN in uses_asan mode uses a special exit code as it doesn't support + abort_on_error. On top, a user may specify a custom AFL_CRASH_EXITCODE. + Handle all three cases here. */ + + if (unlikely( + /* A normal crash/abort */ + (WIFSIGNALED(fsrv->child_status)) || + /* special handling for msan */ + (fsrv->uses_asan && WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) || + /* the custom crash_exitcode was returned by the target */ + (fsrv->uses_crash_exitcode && + WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode))) { + + /* For a proper crash, set last_kill_signal to WTERMSIG, else set it to 0 */ + fsrv->last_kill_signal = WIFSIGNALED(fsrv->child_status)? WTERMSIG(fsrv->child_status): 0; return FSRV_RUN_CRASH; } - // Fauxserver should handle this now. - if (*(u32 *)fsrv->trace_bits == EXEC_FAIL_SIG) return FSRV_RUN_ERROR; - + /* success :) */ return FSRV_RUN_OK; } -- cgit 1.4.1 From 4581ad3df9a3e98b065c61d1f392398973669479 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 8 Jan 2021 18:19:40 +0100 Subject: code fmt --- src/afl-forkserver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 413676bf..b6b3cd74 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -1169,7 +1169,8 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode))) { /* For a proper crash, set last_kill_signal to WTERMSIG, else set it to 0 */ - fsrv->last_kill_signal = WIFSIGNALED(fsrv->child_status)? WTERMSIG(fsrv->child_status): 0; + fsrv->last_kill_signal = + WIFSIGNALED(fsrv->child_status) ? WTERMSIG(fsrv->child_status) : 0; return FSRV_RUN_CRASH; } -- cgit 1.4.1 From dc81f681c9cb0af66ae6f820ed421a2b7100a404 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 10 Jan 2021 13:12:44 +0100 Subject: fix weight calc, thanks to Marcel --- src/afl-fuzz-queue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 99f564e9..66938635 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -57,7 +57,7 @@ double compute_weight(afl_state_t *afl, struct queue_entry *q, if (likely(afl->schedule < RARE)) { weight *= (avg_exec_us / q->exec_us); } weight *= (log(q->bitmap_size) / avg_bitmap_size); - weight *= (log(q->tc_ref) / avg_top_size); + weight *= (1 + (q->tc_ref / avg_top_size)); if (unlikely(q->favored)) weight *= 5; return weight; @@ -104,7 +104,7 @@ void create_alias_table(afl_state_t *afl) { avg_exec_us += q->exec_us; avg_bitmap_size += log(q->bitmap_size); - avg_top_size += log(q->tc_ref); + avg_top_size += q->tc_ref; ++active; } -- cgit 1.4.1 From efd80424311147523c0aa4f6436066771788dc44 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 12 Jan 2021 13:42:00 +0100 Subject: fix lto cmplog instability --- src/afl-fuzz-run.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index cc2ef891..bded8e2d 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -296,11 +296,11 @@ static void write_with_gap(afl_state_t *afl, u8 *mem, u32 len, u32 skip_at, u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, u32 handicap, u8 from_queue) { + if (unlikely(afl->shm.cmplog_mode)) { q->exec_cksum = 0; } + u8 fault = 0, new_bits = 0, var_detected = 0, hnb = 0, first_run = (q->exec_cksum == 0); - u64 start_us, stop_us; - s32 old_sc = afl->stage_cur, old_sm = afl->stage_max; u32 use_tmout = afl->fsrv.exec_tmout; u8 *old_sn = afl->stage_name; -- cgit 1.4.1 From b9ba2805e537f8033075f90f31b52767f31267dc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 12 Jan 2021 17:31:29 +0100 Subject: minor changes --- src/afl-forkserver.c | 4 ++-- src/afl-fuzz-run.c | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index b6b3cd74..7535720d 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -972,10 +972,10 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { hash64(fsrv->shmem_fuzz, *fsrv->shmem_fuzz_len, 0xa5b35705), *fsrv->shmem_fuzz_len); fprintf(stderr, "SHM :"); - for (int i = 0; i < *fsrv->shmem_fuzz_len; i++) + for (u32 i = 0; i < *fsrv->shmem_fuzz_len; i++) fprintf(stderr, "%02x", fsrv->shmem_fuzz[i]); fprintf(stderr, "\nORIG:"); - for (int i = 0; i < *fsrv->shmem_fuzz_len; i++) + for (u32 i = 0; i < *fsrv->shmem_fuzz_len; i++) fprintf(stderr, "%02x", buf[i]); fprintf(stderr, "\n"); diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index bded8e2d..b597488b 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -230,10 +230,10 @@ static void write_with_gap(afl_state_t *afl, u8 *mem, u32 len, u32 skip_at, hash64(afl->fsrv.shmem_fuzz, *afl->fsrv.shmem_fuzz_len, 0xa5b35705), *afl->fsrv.shmem_fuzz_len); fprintf(stderr, "SHM :"); - for (int i = 0; i < *afl->fsrv.shmem_fuzz_len; i++) + for (u32 i = 0; i < *afl->fsrv.shmem_fuzz_len; i++) fprintf(stderr, "%02x", afl->fsrv.shmem_fuzz[i]); fprintf(stderr, "\nORIG:"); - for (int i = 0; i < *afl->fsrv.shmem_fuzz_len; i++) + for (u32 i = 0; i < *afl->fsrv.shmem_fuzz_len; i++) fprintf(stderr, "%02x", (u8)((u8 *)mem)[i]); fprintf(stderr, "\n"); @@ -300,7 +300,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, u8 fault = 0, new_bits = 0, var_detected = 0, hnb = 0, first_run = (q->exec_cksum == 0); - u64 start_us, stop_us; + u64 start_us, stop_us, diff_us; s32 old_sc = afl->stage_cur, old_sm = afl->stage_max; u32 use_tmout = afl->fsrv.exec_tmout; u8 *old_sn = afl->stage_name; @@ -422,9 +422,19 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } - stop_us = get_cur_time_us(); + if (unlikely(afl->fixed_seed)) { - afl->total_cal_us += stop_us - start_us; + diff_us = (afl->fsrv.exec_tmout - 1) * afl->stage_max; + + } else { + + stop_us = get_cur_time_us(); + diff_us = stop_us - start_us; + if (unlikely(!diff_us)) { ++diff_us; } + + } + + afl->total_cal_us += diff_us; afl->total_cal_cycles += afl->stage_max; /* OK, let's collect some stats about the performance of this test case. @@ -437,7 +447,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } - q->exec_us = (stop_us - start_us) / afl->stage_max; + q->exec_us = diff_us / afl->stage_max; q->bitmap_size = count_bytes(afl, afl->fsrv.trace_bits); q->handicap = handicap; q->cal_failed = 0; -- cgit 1.4.1 From 0ddbffd80e6378bdaf8565caeca3990630574d3f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 12 Jan 2021 17:36:10 +0100 Subject: fix --- src/afl-common.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-common.c b/src/afl-common.c index 3a7d0ce5..cf996548 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -927,14 +927,14 @@ u32 get_map_size(void) { if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) { map_size = atoi(ptr); - if (map_size < 8 || map_size > (1 << 29)) { + if (!map_size || map_size > (1 << 29)) { - FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 8U, + FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 32U, 1U << 29); } - if (map_size % 8) { map_size = (((map_size >> 3) + 1) << 3); } + if (map_size % 32) { map_size = (((map_size >> 5) + 1) << 5); } } -- cgit 1.4.1 From 53c7aaa57bf5473c5d6a4553c0178410fef17cbc Mon Sep 17 00:00:00 2001 From: murx- Date: Thu, 14 Jan 2021 13:46:03 +0100 Subject: Update documentation to reflect new default power schedule --- README.md | 2 +- docs/power_schedules.md | 4 ++-- docs/status_screen.md | 7 ++++++- src/afl-fuzz.c | 4 ++-- 4 files changed, 11 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/README.md b/README.md index bea673f9..a5cb6393 100644 --- a/README.md +++ b/README.md @@ -591,7 +591,7 @@ For every secondary fuzzer there should be a variation, e.g.: All other secondaries should be used like this: * A third to a half with the MOpt mutator enabled: `-L 0` * run with a different power schedule, available are: - `explore (default), fast, coe, lin, quad, exploit, mmopt, rare, seek` + `fast (default), explore, coe, lin, quad, exploit, mmopt, rare, seek` which you can set with e.g. `-p seek` You can also use different fuzzers. diff --git a/docs/power_schedules.md b/docs/power_schedules.md index 06fefa12..493f9609 100644 --- a/docs/power_schedules.md +++ b/docs/power_schedules.md @@ -13,8 +13,8 @@ We find that AFL's exploitation-based constant schedule assigns **too much energ | AFL flag | Power Schedule | | ------------- | -------------------------- | -| `-p explore` (default)| ![EXPLORE](http://latex.codecogs.com/gif.latex?p%28i%29%3D%5Cfrac%7B%5Calpha%28i%29%7D%7B%5Cbeta%7D) | -| `-p fast` | ![FAST](http://latex.codecogs.com/gif.latex?p(i)=\\min\\left(\\frac{\\alpha(i)}{\\beta}\\cdot\\frac{2^{s(i)}}{f(i)},M\\right)) | +| `-p explore` | ![EXPLORE](http://latex.codecogs.com/gif.latex?p%28i%29%3D%5Cfrac%7B%5Calpha%28i%29%7D%7B%5Cbeta%7D) | +| `-p fast` (default)| ![FAST](http://latex.codecogs.com/gif.latex?p(i)=\\min\\left(\\frac{\\alpha(i)}{\\beta}\\cdot\\frac{2^{s(i)}}{f(i)},M\\right)) | | `-p coe` | ![COE](http://latex.codecogs.com/gif.latex?p%28i%29%3D%5Cbegin%7Bcases%7D%200%20%26%20%5Ctext%7B%20if%20%7D%20f%28i%29%20%3E%20%5Cmu%5C%5C%20%5Cmin%5Cleft%28%5Cfrac%7B%5Calpha%28i%29%7D%7B%5Cbeta%7D%5Ccdot%202%5E%7Bs%28i%29%7D%2C%20M%5Cright%29%20%26%20%5Ctext%7B%20otherwise.%7D%20%5Cend%7Bcases%7D) | | `-p quad` | ![QUAD](http://latex.codecogs.com/gif.latex?p%28i%29%20%3D%20%5Cmin%5Cleft%28%5Cfrac%7B%5Calpha%28i%29%7D%7B%5Cbeta%7D%5Ccdot%5Cfrac%7Bs%28i%29%5E2%7D%7Bf%28i%29%7D%2CM%5Cright%29) | | `-p lin` | ![LIN](http://latex.codecogs.com/gif.latex?p%28i%29%20%3D%20%5Cmin%5Cleft%28%5Cfrac%7B%5Calpha%28i%29%7D%7B%5Cbeta%7D%5Ccdot%5Cfrac%7Bs%28i%29%7D%7Bf%28i%29%7D%2CM%5Cright%29) | diff --git a/docs/status_screen.md b/docs/status_screen.md index 0cede6ff..0329d960 100644 --- a/docs/status_screen.md +++ b/docs/status_screen.md @@ -29,13 +29,18 @@ With that out of the way, let's talk about what's actually on the screen... ### The status bar +``` +american fuzzy lop ++3.01a (default) [fast] {0} +``` + The top line shows you which mode afl-fuzz is running in (normal: "american fuzy lop", crash exploration mode: "peruvian rabbit mode") and the version of afl++. Next to the version is the banner, which, if not set with -T by hand, will either show the binary name being fuzzed, or the -M/-S main/secondary name for parallel fuzzing. -Finally, the last item is the power schedule mode being run (default: explore). +Second to last is the power schedule mode being run (default: fast). +Finally, the last item is the CPU id. ### Process timing diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 37f8db8a..6a2b28cf 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -103,8 +103,8 @@ static void usage(u8 *argv0, int more_help) { "Execution control settings:\n" " -p schedule - power schedules compute a seed's performance score:\n" - " -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " "or @@)\n" -- cgit 1.4.1 From 7ba17d182f7010c616fe9ecf4ba291cd772f52bc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 14 Jan 2021 21:26:46 +0100 Subject: more sync in deterministic mode --- docs/Changelog.md | 2 ++ src/afl-fuzz.c | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 81ac91b0..e0f8e9bf 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -17,6 +17,8 @@ sending a mail to . - afl-fuzz - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) + - if determinstic mode is active (-D, or -M without -d) then we sync + after every queue entry as this can take very long time otherwise - switched to a faster RNG - added hghwng's patch for faster trace map analysis - afl-cc diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 6a2b28cf..bb2674f0 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1781,15 +1781,27 @@ int main(int argc, char **argv_orig, char **envp) { } while (skipped_fuzz && afl->queue_cur && !afl->stop_soon); - if (!afl->stop_soon && afl->sync_id) { + if (likely(!afl->stop_soon && afl->sync_id)) { - if (unlikely(afl->is_main_node)) { + if (likely(afl->skip_deterministic)) { - if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) { sync_fuzzers(afl); } + if (unlikely(afl->is_main_node)) { + + if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) { + + sync_fuzzers(afl); + + } + + } else { + + if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); } + + } } else { - if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); } + sync_fuzzers(afl); } -- cgit 1.4.1 From d5049da5e4d8ab7d0ec594cc9440437deba861bb Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 15 Jan 2021 14:50:51 +0100 Subject: better error message --- src/afl-forkserver.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 7535720d..39f044f2 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -808,6 +808,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "before receiving any input\n" " from the fuzzer! There are several probable explanations:\n\n" + " - The target binary requires a large map and crashes before " + "reporting.\n" + " Set a high value (e.g. AFL_MAP_SIZE=1024000) or use " + "AFL_DEBUG=1 to see the\n" + " message from the target binary\n\n" + " - The binary is just buggy and explodes entirely on its own. " "If so, you\n" " need to fix the underlying problem or find a better " @@ -829,6 +835,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "before receiving any input\n" " from the fuzzer! There are several probable explanations:\n\n" + " - The target binary requires a large map and crashes before " + "reporting.\n" + " Set a high value (e.g. AFL_MAP_SIZE=1024000) or use " + "AFL_DEBUG=1 to see the\n" + " message from the target binary\n\n" + " - The current memory limit (%s) is too restrictive, causing " "the\n" " target to hit an OOM condition in the dynamic linker. Try " -- cgit 1.4.1 From 8eb00a5dfab01330f576d173038a9561951371ec Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 15 Jan 2021 14:55:58 +0100 Subject: remove warning --- src/afl-cc.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 999ee7f1..8fb42718 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1785,15 +1785,6 @@ int main(int argc, char **argv, char **envp) { } - if (!be_quiet && !lto_mode && - ((ptr2 = getenv("AFL_MAP_SIZE")) || (ptr2 = getenv("AFL_MAPSIZE")))) { - - u32 map_size = atoi(ptr2); - if (map_size != MAP_SIZE) - WARNF("AFL_MAP_SIZE is not supported by afl-clang-fast"); - - } - if (debug) { DEBUGF("cd '%s';", getthecwd()); -- cgit 1.4.1 From a0e884cf8bffe1a0394d106375f6a23edd2b60e6 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 15 Jan 2021 16:56:40 +0100 Subject: merge cmplog --- include/afl-fuzz.h | 21 +- include/cmplog.h | 17 +- include/debug.h | 24 +- include/types.h | 43 +- instrumentation/afl-compiler-rt.o.c | 89 +- instrumentation/cmplog-instructions-pass.cc | 586 +++++++++-- src/afl-cc.c | 49 +- src/afl-fuzz-init.c | 38 + src/afl-fuzz-one.c | 81 +- src/afl-fuzz-queue.c | 1 + src/afl-fuzz-redqueen.c | 1437 +++++++++++++++++++++++---- src/afl-fuzz-state.c | 1 + src/afl-fuzz.c | 60 +- 13 files changed, 2055 insertions(+), 392 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 988a907d..8a2122dc 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -145,12 +145,22 @@ extern s16 interesting_16[INTERESTING_8_LEN + INTERESTING_16_LEN]; extern s32 interesting_32[INTERESTING_8_LEN + INTERESTING_16_LEN + INTERESTING_32_LEN]; +struct tainted { + + u32 pos; + u32 len; + struct tainted *next; + struct tainted *prev; + +}; + struct queue_entry { u8 *fname; /* File name for the test case */ u32 len; /* Input length */ - u8 cal_failed; /* Calibration failed? */ + u8 colorized, /* Do not run redqueen stage again */ + cal_failed; /* Calibration failed? */ bool trim_done, /* Trimmed? */ was_fuzzed, /* historical, but needed for MOpt */ passed_det, /* Deterministic stages passed? */ @@ -158,7 +168,6 @@ struct queue_entry { var_behavior, /* Variable behavior? */ favored, /* Currently favored? */ fs_redundant, /* Marked as redundant in the fs? */ - fully_colorized, /* Do not run redqueen stage again */ is_ascii, /* Is the input just ascii text? */ disabled; /* Is disabled from fuzz selection */ @@ -183,7 +192,11 @@ struct queue_entry { u8 *testcase_buf; /* The testcase buffer, if loaded. */ - struct queue_entry *next; /* Next element, if any */ + u8 * cmplog_colorinput; /* the result buf of colorization */ + struct tainted *taint; /* Taint information from CmpLog */ + + struct queue_entry *mother, /* queue entry this based on */ + *next; /* Next element, if any */ }; @@ -636,6 +649,8 @@ typedef struct afl_state { /* cmplog forkserver ids */ s32 cmplog_fsrv_ctl_fd, cmplog_fsrv_st_fd; u32 cmplog_prev_timed_out; + u32 cmplog_max_filesize; + u32 cmplog_lvl; struct afl_pass_stat *pass_stats; struct cmp_map * orig_cmp_map; diff --git a/include/cmplog.h b/include/cmplog.h index bf557785..6392c503 100644 --- a/include/cmplog.h +++ b/include/cmplog.h @@ -30,8 +30,10 @@ #include "config.h" +#define CMPLOG_LVL_MAX 3 + #define CMP_MAP_W 65536 -#define CMP_MAP_H 256 +#define CMP_MAP_H 32 #define CMP_MAP_RTN_H (CMP_MAP_H / 4) #define SHAPE_BYTES(x) (x + 1) @@ -41,13 +43,12 @@ struct cmp_header { - unsigned hits : 20; - - unsigned cnt : 20; - unsigned id : 16; - - unsigned shape : 5; // from 0 to 31 + unsigned hits : 24; + unsigned id : 24; + unsigned shape : 5; unsigned type : 1; + unsigned attribute : 4; + unsigned reserved : 6; } __attribute__((packed)); @@ -55,6 +56,8 @@ struct cmp_operands { u64 v0; u64 v1; + u64 v0_128; + u64 v1_128; }; diff --git a/include/debug.h b/include/debug.h index ef5b195b..fc1f39cb 100644 --- a/include/debug.h +++ b/include/debug.h @@ -295,8 +295,8 @@ static inline const char *colorfilter(const char *x) { \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ "\n[-] PROGRAM ABORT : " cRST x); \ - SAYF(cLRD "\n Location : " cRST "%s(), %s:%d\n\n", __func__, \ - __FILE__, __LINE__); \ + SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __func__, \ + __FILE__, (u32)__LINE__); \ exit(1); \ \ } while (0) @@ -308,8 +308,8 @@ static inline const char *colorfilter(const char *x) { \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ "\n[-] PROGRAM ABORT : " cRST x); \ - SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%d\n\n", __func__, \ - __FILE__, __LINE__); \ + SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __func__, \ + __FILE__, (u32)__LINE__); \ abort(); \ \ } while (0) @@ -322,8 +322,8 @@ static inline const char *colorfilter(const char *x) { fflush(stdout); \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ "\n[-] SYSTEM ERROR : " cRST x); \ - SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%d\n", __func__, \ - __FILE__, __LINE__); \ + SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __func__, \ + __FILE__, (u32)__LINE__); \ SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ exit(1); \ \ @@ -344,12 +344,12 @@ static inline const char *colorfilter(const char *x) { /* Show a prefixed debug output. */ -#define DEBUGF(x...) \ - do { \ - \ - SAYF(cMGN "[D] " cBRI "DEBUG: " cRST x); \ - SAYF(cRST ""); \ - \ +#define DEBUGF(x...) \ + do { \ + \ + fprintf(stderr, cMGN "[D] " cBRI "DEBUG: " cRST x); \ + fprintf(stderr, cRST ""); \ + \ } while (0) /* Error-checking versions of read() and write() that call RPFATAL() as diff --git a/include/types.h b/include/types.h index 3e3bc953..d5c31597 100644 --- a/include/types.h +++ b/include/types.h @@ -26,9 +26,11 @@ #include #include -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef unsigned __int128 uint128_t; +typedef uint128_t u128; /* Extended forkserver option values */ @@ -57,10 +59,12 @@ typedef uint32_t u32; typedef unsigned long long u64; -typedef int8_t s8; -typedef int16_t s16; -typedef int32_t s32; -typedef int64_t s64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; +typedef __int128 int128_t; +typedef int128_t s128; #ifndef MIN #define MIN(a, b) \ @@ -114,6 +118,31 @@ typedef int64_t s64; \ }) +// It is impossible to define 128 bit constants, so ... +#define SWAPN(_x, _l) \ + ({ \ + \ + u128 _res = (_x), _ret; \ + char *d = (char *)&_ret, *s = (char *)&_res; \ + int i; \ + for (i = 0; i < 16; i++) \ + d[15 - i] = s[i]; \ + u32 sr = 128U - ((_l) << 3U); \ + (_ret >>= sr); \ + (u128) _ret; \ + \ + }) + +#define SWAPNN(_x, _y, _l) \ + ({ \ + \ + char *d = (char *)(_x), *s = (char *)(_y); \ + u32 i, l = (_l)-1; \ + for (i = 0; i <= l; i++) \ + d[l - i] = s[i]; \ + \ + }) + #ifdef AFL_LLVM_PASS #if defined(__linux__) || !defined(__ANDROID__) #define AFL_SR(s) (srandom(s)) diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index b735d8df..5d75af78 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -161,7 +161,7 @@ void send_forkserver_error(int error) { u32 status; if (!error || error > 0xffff) return; status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); - if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; + if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) { return; } } @@ -544,11 +544,11 @@ static void __afl_start_snapshots(void) { if (__afl_dictionary_len && __afl_dictionary) status |= FS_OPT_AUTODICT; memcpy(tmp, &status, 4); - if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; + if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; } if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) { - if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); + if (read(FORKSRV_FD, &was_killed, 4) != 4) { _exit(1); } if (getenv("AFL_DEBUG")) { @@ -1207,7 +1207,9 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { ///// CmpLog instrumentation -void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { +void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2, uint8_t attr) { + + // fprintf(stderr, "hook1 arg0=%02x arg1=%02x attr=%u\n", arg1, arg2, attr); if (unlikely(!__afl_cmp_map)) return; @@ -1216,6 +1218,7 @@ void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { k &= CMP_MAP_W - 1; __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + __afl_cmp_map->headers[k].attribute = attr; u32 hits = __afl_cmp_map->headers[k].hits; __afl_cmp_map->headers[k].hits = hits + 1; @@ -1230,7 +1233,7 @@ void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { } -void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { +void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2, uint8_t attr) { if (unlikely(!__afl_cmp_map)) return; @@ -1239,6 +1242,7 @@ void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { k &= CMP_MAP_W - 1; __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + __afl_cmp_map->headers[k].attribute = attr; u32 hits = __afl_cmp_map->headers[k].hits; __afl_cmp_map->headers[k].hits = hits + 1; @@ -1251,7 +1255,9 @@ void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { } -void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { +void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2, uint8_t attr) { + + // fprintf(stderr, "hook4 arg0=%x arg1=%x attr=%u\n", arg1, arg2, attr); if (unlikely(!__afl_cmp_map)) return; @@ -1260,6 +1266,7 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { k &= CMP_MAP_W - 1; __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + __afl_cmp_map->headers[k].attribute = attr; u32 hits = __afl_cmp_map->headers[k].hits; __afl_cmp_map->headers[k].hits = hits + 1; @@ -1272,7 +1279,9 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { } -void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) { +void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2, uint8_t attr) { + + // fprintf(stderr, "hook8 arg0=%lx arg1=%lx attr=%u\n", arg1, arg2, attr); if (unlikely(!__afl_cmp_map)) return; @@ -1281,6 +1290,7 @@ void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) { k &= CMP_MAP_W - 1; __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + __afl_cmp_map->headers[k].attribute = attr; u32 hits = __afl_cmp_map->headers[k].hits; __afl_cmp_map->headers[k].hits = hits + 1; @@ -1293,16 +1303,77 @@ void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) { } +// support for u24 to u120 via llvm _ExitInt(). size is in bytes minus 1 +void __cmplog_ins_hookN(uint128_t arg1, uint128_t arg2, uint8_t attr, + uint8_t size) { + + // fprintf(stderr, "hookN arg0=%llx:%llx arg1=%llx:%llx bytes=%u attr=%u\n", + // (u64)(arg1 >> 64), (u64)arg1, (u64)(arg2 >> 64), (u64)arg2, size + 1, + // attr); + + if (unlikely(!__afl_cmp_map)) return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + __afl_cmp_map->headers[k].attribute = attr; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + + __afl_cmp_map->headers[k].shape = size; + + hits &= CMP_MAP_H - 1; + __afl_cmp_map->log[k][hits].v0 = (u64)arg1; + __afl_cmp_map->log[k][hits].v1 = (u64)arg2; + + if (size > 7) { + + __afl_cmp_map->log[k][hits].v0_128 = (u64)(arg1 >> 64); + __afl_cmp_map->log[k][hits].v1_128 = (u64)(arg2 >> 64); + + } + +} + +void __cmplog_ins_hook16(uint128_t arg1, uint128_t arg2, uint8_t attr) { + + if (unlikely(!__afl_cmp_map)) return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + __afl_cmp_map->headers[k].attribute = attr; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + + __afl_cmp_map->headers[k].shape = 15; + + hits &= CMP_MAP_H - 1; + __afl_cmp_map->log[k][hits].v0 = (u64)arg1; + __afl_cmp_map->log[k][hits].v1 = (u64)arg2; + __afl_cmp_map->log[k][hits].v0_128 = (u64)(arg1 >> 64); + __afl_cmp_map->log[k][hits].v1_128 = (u64)(arg2 >> 64); + +} + #if defined(__APPLE__) #pragma weak __sanitizer_cov_trace_const_cmp1 = __cmplog_ins_hook1 #pragma weak __sanitizer_cov_trace_const_cmp2 = __cmplog_ins_hook2 #pragma weak __sanitizer_cov_trace_const_cmp4 = __cmplog_ins_hook4 #pragma weak __sanitizer_cov_trace_const_cmp8 = __cmplog_ins_hook8 + #pragma weak __sanitizer_cov_trace_const_cmp16 = __cmplog_ins_hook16 #pragma weak __sanitizer_cov_trace_cmp1 = __cmplog_ins_hook1 #pragma weak __sanitizer_cov_trace_cmp2 = __cmplog_ins_hook2 #pragma weak __sanitizer_cov_trace_cmp4 = __cmplog_ins_hook4 #pragma weak __sanitizer_cov_trace_cmp8 = __cmplog_ins_hook8 + #pragma weak __sanitizer_cov_trace_cmp16 = __cmplog_ins_hook16 #else void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) __attribute__((alias("__cmplog_ins_hook1"))); @@ -1312,6 +1383,8 @@ void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2) __attribute__((alias("__cmplog_ins_hook4"))); void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2) __attribute__((alias("__cmplog_ins_hook8"))); +void __sanitizer_cov_trace_const_cmp16(uint128_t arg1, uint128_t arg2) + __attribute__((alias("__cmplog_ins_hook16"))); void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) __attribute__((alias("__cmplog_ins_hook1"))); @@ -1321,6 +1394,8 @@ void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) __attribute__((alias("__cmplog_ins_hook4"))); void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) __attribute__((alias("__cmplog_ins_hook8"))); +void __sanitizer_cov_trace_cmp16(uint128_t arg1, uint128_t arg2) + __attribute__((alias("__cmplog_ins_hook16"))); #endif /* defined(__APPLE__) */ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index 3499ccf0..a74fb6c8 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -85,9 +85,25 @@ class CmpLogInstructions : public ModulePass { char CmpLogInstructions::ID = 0; +template +Iterator Unique(Iterator first, Iterator last) { + + while (first != last) { + + Iterator next(first); + last = std::remove(++next, last, *first); + first = next; + + } + + return last; + +} + bool CmpLogInstructions::hookInstrs(Module &M) { std::vector icomps; + std::vector switches; LLVMContext & C = M.getContext(); Type * VoidTy = Type::getVoidTy(C); @@ -95,13 +111,15 @@ bool CmpLogInstructions::hookInstrs(Module &M) { IntegerType *Int16Ty = IntegerType::getInt16Ty(C); IntegerType *Int32Ty = IntegerType::getInt32Ty(C); IntegerType *Int64Ty = IntegerType::getInt64Ty(C); + IntegerType *Int128Ty = IntegerType::getInt128Ty(C); #if LLVM_VERSION_MAJOR < 9 Constant * #else FunctionCallee #endif - c1 = M.getOrInsertFunction("__cmplog_ins_hook1", VoidTy, Int8Ty, Int8Ty + c1 = M.getOrInsertFunction("__cmplog_ins_hook1", VoidTy, Int8Ty, Int8Ty, + Int8Ty #if LLVM_VERSION_MAJOR < 5 , NULL @@ -118,7 +136,8 @@ bool CmpLogInstructions::hookInstrs(Module &M) { #else FunctionCallee #endif - c2 = M.getOrInsertFunction("__cmplog_ins_hook2", VoidTy, Int16Ty, Int16Ty + c2 = M.getOrInsertFunction("__cmplog_ins_hook2", VoidTy, Int16Ty, Int16Ty, + Int8Ty #if LLVM_VERSION_MAJOR < 5 , NULL @@ -135,7 +154,8 @@ bool CmpLogInstructions::hookInstrs(Module &M) { #else FunctionCallee #endif - c4 = M.getOrInsertFunction("__cmplog_ins_hook4", VoidTy, Int32Ty, Int32Ty + c4 = M.getOrInsertFunction("__cmplog_ins_hook4", VoidTy, Int32Ty, Int32Ty, + Int8Ty #if LLVM_VERSION_MAJOR < 5 , NULL @@ -152,7 +172,8 @@ bool CmpLogInstructions::hookInstrs(Module &M) { #else FunctionCallee #endif - c8 = M.getOrInsertFunction("__cmplog_ins_hook8", VoidTy, Int64Ty, Int64Ty + c8 = M.getOrInsertFunction("__cmplog_ins_hook8", VoidTy, Int64Ty, Int64Ty, + Int8Ty #if LLVM_VERSION_MAJOR < 5 , NULL @@ -164,6 +185,42 @@ bool CmpLogInstructions::hookInstrs(Module &M) { FunctionCallee cmplogHookIns8 = c8; #endif +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c16 = M.getOrInsertFunction("__cmplog_ins_hook16", VoidTy, Int128Ty, + Int128Ty, Int8Ty +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogHookIns16 = cast(c16); +#else + FunctionCallee cmplogHookIns16 = c16; +#endif + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + cN = M.getOrInsertFunction("__cmplog_ins_hookN", VoidTy, Int128Ty, + Int128Ty, Int8Ty, Int8Ty +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogHookInsN = cast(cN); +#else + FunctionCallee cmplogHookInsN = cN; +#endif + /* iterate over all functions, bbs and instruction and add suitable calls */ for (auto &F : M) { @@ -174,35 +231,16 @@ bool CmpLogInstructions::hookInstrs(Module &M) { for (auto &IN : BB) { CmpInst *selectcmpInst = nullptr; - if ((selectcmpInst = dyn_cast(&IN))) { - if (selectcmpInst->getPredicate() == CmpInst::ICMP_EQ || - selectcmpInst->getPredicate() == CmpInst::ICMP_NE || - selectcmpInst->getPredicate() == CmpInst::ICMP_UGT || - selectcmpInst->getPredicate() == CmpInst::ICMP_SGT || - selectcmpInst->getPredicate() == CmpInst::ICMP_ULT || - selectcmpInst->getPredicate() == CmpInst::ICMP_SLT || - selectcmpInst->getPredicate() == CmpInst::ICMP_UGE || - selectcmpInst->getPredicate() == CmpInst::ICMP_SGE || - selectcmpInst->getPredicate() == CmpInst::ICMP_ULE || - selectcmpInst->getPredicate() == CmpInst::ICMP_SLE || - selectcmpInst->getPredicate() == CmpInst::FCMP_OGE || - selectcmpInst->getPredicate() == CmpInst::FCMP_UGE || - selectcmpInst->getPredicate() == CmpInst::FCMP_OLE || - selectcmpInst->getPredicate() == CmpInst::FCMP_ULE || - selectcmpInst->getPredicate() == CmpInst::FCMP_OGT || - selectcmpInst->getPredicate() == CmpInst::FCMP_UGT || - selectcmpInst->getPredicate() == CmpInst::FCMP_OLT || - selectcmpInst->getPredicate() == CmpInst::FCMP_ULT || - selectcmpInst->getPredicate() == CmpInst::FCMP_UEQ || - selectcmpInst->getPredicate() == CmpInst::FCMP_OEQ || - selectcmpInst->getPredicate() == CmpInst::FCMP_UNE || - selectcmpInst->getPredicate() == CmpInst::FCMP_ONE) { - - icomps.push_back(selectcmpInst); + icomps.push_back(selectcmpInst); - } + } + + SwitchInst *switchInst = nullptr; + if ((switchInst = dyn_cast(BB.getTerminator()))) { + + if (switchInst->getNumCases() > 1) { switches.push_back(switchInst); } } @@ -212,101 +250,473 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (!icomps.size()) return false; - // if (!be_quiet) errs() << "Hooking " << icomps.size() << " cmp - // instructions\n"; + // unique the collected switches + switches.erase(Unique(switches.begin(), switches.end()), switches.end()); + + // Instrument switch values for cmplog + if (switches.size()) { + + if (!be_quiet) + errs() << "Hooking " << switches.size() << " switch instructions\n"; - for (auto &selectcmpInst : icomps) { + for (auto &SI : switches) { - IRBuilder<> IRB(selectcmpInst->getParent()); - IRB.SetInsertPoint(selectcmpInst); + Value * Val = SI->getCondition(); + unsigned int max_size = Val->getType()->getIntegerBitWidth(), cast_size; + unsigned char do_cast = 0; - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); + if (!SI->getNumCases() || max_size <= 8) { - IntegerType * intTyOp0 = NULL; - IntegerType * intTyOp1 = NULL; - unsigned max_size = 0; - std::vector args; + // if (!be_quiet) errs() << "skip trivial switch..\n"; + continue; - if (selectcmpInst->getOpcode() == Instruction::FCmp) { + } + + IRBuilder<> IRB(SI->getParent()); + IRB.SetInsertPoint(SI); + + if (max_size % 8) { + + max_size = (((max_size / 8) + 1) * 8); + do_cast = 1; + + } + + if (max_size > 128) { + + if (!be_quiet) { + + fprintf(stderr, + "Cannot handle this switch bit size: %u (truncating)\n", + max_size); + + } + + max_size = 128; + do_cast = 1; + + } + + // do we need to cast? + switch (max_size) { + + case 8: + case 16: + case 32: + case 64: + case 128: + cast_size = max_size; + break; + default: + cast_size = 128; + do_cast = 1; + + } + + Value *CompareTo = Val; + + if (do_cast) { + + ConstantInt *cint = dyn_cast(Val); + if (cint) { + + uint64_t val = cint->getZExtValue(); + // fprintf(stderr, "ConstantInt: %lu\n", val); + switch (cast_size) { + + case 8: + CompareTo = ConstantInt::get(Int8Ty, val); + break; + case 16: + CompareTo = ConstantInt::get(Int16Ty, val); + break; + case 32: + CompareTo = ConstantInt::get(Int32Ty, val); + break; + case 64: + CompareTo = ConstantInt::get(Int64Ty, val); + break; + case 128: + CompareTo = ConstantInt::get(Int128Ty, val); + break; + + } - auto ty0 = op0->getType(); - if (ty0->isHalfTy() + } else { + + CompareTo = IRB.CreateBitCast(Val, IntegerType::get(C, cast_size)); + + } + + } + + for (SwitchInst::CaseIt i = SI->case_begin(), e = SI->case_end(); i != e; + ++i) { + +#if LLVM_VERSION_MAJOR < 5 + ConstantInt *cint = i.getCaseValue(); +#else + ConstantInt *cint = i->getCaseValue(); +#endif + + if (cint) { + + std::vector args; + args.push_back(CompareTo); + + Value *new_param = cint; + + if (do_cast) { + + uint64_t val = cint->getZExtValue(); + // fprintf(stderr, "ConstantInt: %lu\n", val); + switch (cast_size) { + + case 8: + new_param = ConstantInt::get(Int8Ty, val); + break; + case 16: + new_param = ConstantInt::get(Int16Ty, val); + break; + case 32: + new_param = ConstantInt::get(Int32Ty, val); + break; + case 64: + new_param = ConstantInt::get(Int64Ty, val); + break; + case 128: + new_param = ConstantInt::get(Int128Ty, val); + break; + + } + + } + + if (new_param) { + + args.push_back(new_param); + ConstantInt *attribute = ConstantInt::get(Int8Ty, 1); + args.push_back(attribute); + if (cast_size != max_size) { + + ConstantInt *bitsize = + ConstantInt::get(Int8Ty, (max_size / 8) - 1); + args.push_back(bitsize); + + } + + switch (cast_size) { + + case 8: + IRB.CreateCall(cmplogHookIns1, args); + break; + case 16: + IRB.CreateCall(cmplogHookIns2, args); + break; + case 32: + IRB.CreateCall(cmplogHookIns4, args); + break; + case 64: + IRB.CreateCall(cmplogHookIns8, args); + break; + case 128: + if (max_size == 128) { + + IRB.CreateCall(cmplogHookIns16, args); + + } else { + + IRB.CreateCall(cmplogHookInsN, args); + + } + + break; + + } + + } + + } + + } + + } + + } + + if (icomps.size()) { + + // if (!be_quiet) errs() << "Hooking " << icomps.size() << + // " cmp instructions\n"; + + for (auto &selectcmpInst : icomps) { + + IRBuilder<> IRB(selectcmpInst->getParent()); + IRB.SetInsertPoint(selectcmpInst); + + Value *op0 = selectcmpInst->getOperand(0); + Value *op1 = selectcmpInst->getOperand(1); + + IntegerType * intTyOp0 = NULL; + IntegerType * intTyOp1 = NULL; + unsigned max_size = 0, cast_size = 0; + unsigned char attr = 0, do_cast = 0; + std::vector args; + + CmpInst *cmpInst = dyn_cast(selectcmpInst); + + if (!cmpInst) { continue; } + + switch (cmpInst->getPredicate()) { + + case CmpInst::ICMP_NE: + case CmpInst::FCMP_UNE: + case CmpInst::FCMP_ONE: + break; + case CmpInst::ICMP_EQ: + case CmpInst::FCMP_UEQ: + case CmpInst::FCMP_OEQ: + attr += 1; + break; + case CmpInst::ICMP_UGT: + case CmpInst::ICMP_SGT: + case CmpInst::FCMP_OGT: + case CmpInst::FCMP_UGT: + attr += 2; + break; + case CmpInst::ICMP_UGE: + case CmpInst::ICMP_SGE: + case CmpInst::FCMP_OGE: + case CmpInst::FCMP_UGE: + attr += 3; + break; + case CmpInst::ICMP_ULT: + case CmpInst::ICMP_SLT: + case CmpInst::FCMP_OLT: + case CmpInst::FCMP_ULT: + attr += 4; + break; + case CmpInst::ICMP_ULE: + case CmpInst::ICMP_SLE: + case CmpInst::FCMP_OLE: + case CmpInst::FCMP_ULE: + attr += 5; + break; + default: + break; + + } + + if (selectcmpInst->getOpcode() == Instruction::FCmp) { + + auto ty0 = op0->getType(); + if (ty0->isHalfTy() #if LLVM_VERSION_MAJOR >= 11 - || ty0->isBFloatTy() + || ty0->isBFloatTy() #endif - ) - max_size = 16; - else if (ty0->isFloatTy()) - max_size = 32; - else if (ty0->isDoubleTy()) - max_size = 64; + ) + max_size = 16; + else if (ty0->isFloatTy()) + max_size = 32; + else if (ty0->isDoubleTy()) + max_size = 64; + else if (ty0->isX86_FP80Ty()) + max_size = 80; + else if (ty0->isFP128Ty() || ty0->isPPC_FP128Ty()) + max_size = 128; + + attr += 8; + do_cast = 1; - if (max_size) { + } else { - Value *V0 = IRB.CreateBitCast(op0, IntegerType::get(C, max_size)); - intTyOp0 = dyn_cast(V0->getType()); - Value *V1 = IRB.CreateBitCast(op1, IntegerType::get(C, max_size)); - intTyOp1 = dyn_cast(V1->getType()); + intTyOp0 = dyn_cast(op0->getType()); + intTyOp1 = dyn_cast(op1->getType()); if (intTyOp0 && intTyOp1) { max_size = intTyOp0->getBitWidth() > intTyOp1->getBitWidth() ? intTyOp0->getBitWidth() : intTyOp1->getBitWidth(); - args.push_back(V0); - args.push_back(V1); - } else { + } + + } + + if (!max_size) { continue; } + + // _ExtInt() with non-8th values + if (max_size % 8) { + + max_size = (((max_size / 8) + 1) * 8); + do_cast = 1; + + } + + if (max_size > 128) { + + if (!be_quiet) { - max_size = 0; + fprintf(stderr, + "Cannot handle this compare bit size: %u (truncating)\n", + max_size); } + max_size = 128; + do_cast = 1; + + } + + // do we need to cast? + switch (max_size) { + + case 8: + case 16: + case 32: + case 64: + case 128: + cast_size = max_size; + break; + default: + cast_size = 128; + do_cast = 1; + } - } else { + if (do_cast) { + + // F*cking LLVM optimized out any kind of bitcasts of ConstantInt values + // creating illegal calls. WTF. So we have to work around this. + + ConstantInt *cint = dyn_cast(op0); + if (cint) { + + uint64_t val = cint->getZExtValue(); + // fprintf(stderr, "ConstantInt: %lu\n", val); + ConstantInt *new_param = NULL; + switch (cast_size) { + + case 8: + new_param = ConstantInt::get(Int8Ty, val); + break; + case 16: + new_param = ConstantInt::get(Int16Ty, val); + break; + case 32: + new_param = ConstantInt::get(Int32Ty, val); + break; + case 64: + new_param = ConstantInt::get(Int64Ty, val); + break; + case 128: + new_param = ConstantInt::get(Int128Ty, val); + break; + + } + + if (!new_param) { continue; } + args.push_back(new_param); + + } else { + + Value *V0 = IRB.CreateBitCast(op0, IntegerType::get(C, cast_size)); + args.push_back(V0); + + } + + cint = dyn_cast(op1); + if (cint) { + + uint64_t val = cint->getZExtValue(); + ConstantInt *new_param = NULL; + switch (cast_size) { + + case 8: + new_param = ConstantInt::get(Int8Ty, val); + break; + case 16: + new_param = ConstantInt::get(Int16Ty, val); + break; + case 32: + new_param = ConstantInt::get(Int32Ty, val); + break; + case 64: + new_param = ConstantInt::get(Int64Ty, val); + break; + case 128: + new_param = ConstantInt::get(Int128Ty, val); + break; + + } + + if (!new_param) { continue; } + args.push_back(new_param); + + } else { + + Value *V1 = IRB.CreateBitCast(op1, IntegerType::get(C, cast_size)); + args.push_back(V1); - intTyOp0 = dyn_cast(op0->getType()); - intTyOp1 = dyn_cast(op1->getType()); + } - if (intTyOp0 && intTyOp1) { + } else { - max_size = intTyOp0->getBitWidth() > intTyOp1->getBitWidth() - ? intTyOp0->getBitWidth() - : intTyOp1->getBitWidth(); args.push_back(op0); args.push_back(op1); } - } + ConstantInt *attribute = ConstantInt::get(Int8Ty, attr); + args.push_back(attribute); + + if (cast_size != max_size) { + + ConstantInt *bitsize = ConstantInt::get(Int8Ty, (max_size / 8) - 1); + args.push_back(bitsize); + + } + + // fprintf(stderr, "_ExtInt(%u) castTo %u with attr %u didcast %u\n", + // max_size, cast_size, attr, do_cast); + + switch (cast_size) { - if (max_size < 8 || max_size > 64 || !intTyOp0 || !intTyOp1) continue; - - switch (max_size) { - - case 8: - IRB.CreateCall(cmplogHookIns1, args); - break; - case 16: - IRB.CreateCall(cmplogHookIns2, args); - break; - case 32: - IRB.CreateCall(cmplogHookIns4, args); - break; - case 64: - IRB.CreateCall(cmplogHookIns8, args); - break; - default: - break; + case 8: + IRB.CreateCall(cmplogHookIns1, args); + break; + case 16: + IRB.CreateCall(cmplogHookIns2, args); + break; + case 32: + IRB.CreateCall(cmplogHookIns4, args); + break; + case 64: + IRB.CreateCall(cmplogHookIns8, args); + break; + case 128: + if (max_size == 128) { + + IRB.CreateCall(cmplogHookIns16, args); + + } else { + + IRB.CreateCall(cmplogHookInsN, args); + + } + + break; + + } } } - return true; + if (switches.size() || icomps.size()) + return true; + else + return false; } diff --git a/src/afl-cc.c b/src/afl-cc.c index 8fb42718..02c9c7c5 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -528,10 +528,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/cmplog-routines-pass.so", obj_path); - cc_params[cc_par_cnt++] = alloc_printf( - "-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/cmplog-instructions-pass.so", obj_path); + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); } else { @@ -541,18 +541,18 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = alloc_printf("%s/cmplog-routines-pass.so", obj_path); - // reuse split switches from laf cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = - alloc_printf("%s/split-switches-pass.so", obj_path); + alloc_printf("%s/cmplog-instructions-pass.so", obj_path); + // reuse split switches from laf cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = - alloc_printf("%s/cmplog-instructions-pass.so", obj_path); + alloc_printf("%s/split-switches-pass.so", obj_path); } @@ -792,10 +792,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { } -#if defined(USEMMAP) - #if !defined(__HAIKU__) +#if defined(USEMMAP) && !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; - #endif #endif cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; @@ -858,6 +856,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()"; cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_ABORT()=__afl_coverage_abort()"; + cc_params[cc_par_cnt++] = "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " "__afl_fuzz_alt_ptr)"; @@ -967,10 +966,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif - #if defined(USEMMAP) - #if !defined(__HAIKU__) + #if defined(USEMMAP) && !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; - #endif #endif } @@ -1278,7 +1275,6 @@ int main(int argc, char **argv, char **envp) { } - // this is a hidden option if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 || strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0) { @@ -1349,29 +1345,28 @@ int main(int argc, char **argv, char **envp) { if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) { - ptr2 += strlen("ngram"); - while (*ptr2 && (*ptr2 < '0' || *ptr2 > '9')) - ptr2++; + u8 *ptr3 = ptr2 + strlen("ngram"); + while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) + ptr3++; - if (!*ptr2) { + if (!*ptr3) { - if ((ptr2 = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL) + if ((ptr3 = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL) FATAL( "you must set the NGRAM size with (e.g. for value 2) " "AFL_LLVM_INSTRUMENT=ngram-2"); } - ngram_size = atoi(ptr2); + ngram_size = atoi(ptr3); if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) FATAL( "NGRAM instrumentation option must be between 2 and " - "NGRAM_SIZE_MAX " - "(%u)", + "NGRAM_SIZE_MAX (%u)", NGRAM_SIZE_MAX); instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM); - ptr2 = alloc_printf("%u", ngram_size); - setenv("AFL_LLVM_NGRAM_SIZE", ptr2, 1); + u8 *ptr4 = alloc_printf("%u", ngram_size); + setenv("AFL_LLVM_NGRAM_SIZE", ptr4, 1); } @@ -1507,6 +1502,7 @@ int main(int argc, char **argv, char **envp) { "((instrumentation/README.ngram.md)\n" " INSTRIM: Dominator tree (for LLVM <= 6.0) " "(instrumentation/README.instrim.md)\n\n"); + #undef NATIVE_MSG SAYF( @@ -1641,16 +1637,15 @@ int main(int argc, char **argv, char **envp) { if (have_lto) SAYF("afl-cc LTO with ld=%s %s\n", AFL_REAL_LD, AFL_CLANG_FLTO); if (have_llvm) - SAYF("afl-cc LLVM version %d using binary path \"%s\".\n", LLVM_MAJOR, + SAYF("afl-cc LLVM version %d using the binary path \"%s\".\n", LLVM_MAJOR, LLVM_BINDIR); #endif -#if defined(USEMMAP) +#ifdef USEMMAP #if !defined(__HAIKU__) - cc_params[cc_par_cnt++] = "-lrt"; - SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); - #else SAYF("Compiled with shm_open support.\n"); + #else + SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); #endif #else SAYF("Compiled with shmat support.\n"); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index dbffa4f9..cbff6d7e 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -729,6 +729,30 @@ void read_testcases(afl_state_t *afl, u8 *directory) { add_to_queue(afl, fn2, st.st_size >= MAX_FILE ? MAX_FILE : st.st_size, passed_det); + if (unlikely(afl->shm.cmplog_mode)) { + + if (afl->cmplog_lvl == 1) { + + if (!afl->cmplog_max_filesize || + afl->cmplog_max_filesize < st.st_size) { + + afl->cmplog_max_filesize = st.st_size; + + } + + } else if (afl->cmplog_lvl == 2) { + + if (!afl->cmplog_max_filesize || + afl->cmplog_max_filesize > st.st_size) { + + afl->cmplog_max_filesize = st.st_size; + + } + + } + + } + if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) { u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); @@ -756,6 +780,20 @@ void read_testcases(afl_state_t *afl, u8 *directory) { } + if (unlikely(afl->shm.cmplog_mode)) { + + if (afl->cmplog_max_filesize < 1024) { + + afl->cmplog_max_filesize = 1024; + + } else { + + afl->cmplog_max_filesize = (((afl->cmplog_max_filesize >> 10) + 1) << 10); + + } + + } + afl->last_path_time = 0; afl->queued_at_start = afl->queued_paths; diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index f9509e86..596bae22 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -165,7 +165,7 @@ static u8 could_be_arith(u32 old_val, u32 new_val, u8 blen) { /* See if one-byte adjustments to any byte could produce this result. */ - for (i = 0; i < blen; ++i) { + for (i = 0; (u8)i < blen; ++i) { u8 a = old_val >> (8 * i), b = new_val >> (8 * i); @@ -193,7 +193,7 @@ static u8 could_be_arith(u32 old_val, u32 new_val, u8 blen) { diffs = 0; - for (i = 0; i < blen / 2; ++i) { + for (i = 0; (u8)i < blen / 2; ++i) { u16 a = old_val >> (16 * i), b = new_val >> (16 * i); @@ -290,7 +290,7 @@ static u8 could_be_interest(u32 old_val, u32 new_val, u8 blen, u8 check_le) { /* See if two-byte insertions over old_val could give us new_val. */ - for (i = 0; (s32)i < blen - 1; ++i) { + for (i = 0; (u8)i < blen - 1; ++i) { for (j = 0; j < sizeof(interesting_16) / 2; ++j) { @@ -545,14 +545,31 @@ u8 fuzz_one_original(afl_state_t *afl) { else orig_perf = perf_score = calculate_score(afl, afl->queue_cur); - if (unlikely(perf_score == 0)) { goto abandon_entry; } + if (unlikely(perf_score <= 0)) { goto abandon_entry; } - if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { + if (unlikely(afl->shm.cmplog_mode && + afl->queue_cur->colorized < afl->cmplog_lvl && + (u32)len <= afl->cmplog_max_filesize)) { - if (input_to_state_stage(afl, in_buf, out_buf, len, - afl->queue_cur->exec_cksum)) { + if (unlikely(len < 4)) { - goto abandon_entry; + afl->queue_cur->colorized = 0xff; + + } else { + + if (afl->cmplog_lvl == 3 || + (afl->cmplog_lvl == 2 && afl->queue_cur->tc_ref) || + !(afl->fsrv.total_execs % afl->queued_paths) || + get_cur_time() - afl->last_path_time > 15000) { + + if (input_to_state_stage(afl, in_buf, out_buf, len, + afl->queue_cur->exec_cksum)) { + + goto abandon_entry; + + } + + } } @@ -2796,7 +2813,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { } - s32 len, temp_len; + u32 len, temp_len; u32 i; u32 j; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; @@ -2952,14 +2969,31 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { else orig_perf = perf_score = calculate_score(afl, afl->queue_cur); - if (unlikely(perf_score == 0)) { goto abandon_entry; } + if (unlikely(perf_score <= 0)) { goto abandon_entry; } - if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { + if (unlikely(afl->shm.cmplog_mode && + afl->queue_cur->colorized < afl->cmplog_lvl && + (u32)len <= afl->cmplog_max_filesize)) { - if (input_to_state_stage(afl, in_buf, out_buf, len, - afl->queue_cur->exec_cksum)) { + if (unlikely(len < 4)) { - goto abandon_entry; + afl->queue_cur->colorized = 0xff; + + } else { + + if (afl->cmplog_lvl == 3 || + (afl->cmplog_lvl == 2 && afl->queue_cur->tc_ref) || + !(afl->fsrv.total_execs % afl->queued_paths) || + get_cur_time() - afl->last_path_time > 15000) { + + if (input_to_state_stage(afl, in_buf, out_buf, len, + afl->queue_cur->exec_cksum)) { + + goto abandon_entry; + + } + + } } @@ -3315,7 +3349,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 1; ++i) { + for (i = 0; i < len - 1; ++i) { /* Let's consult the effector map... */ @@ -3357,7 +3391,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 3; ++i) { + for (i = 0; i < len - 3; ++i) { /* Let's consult the effector map... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] && @@ -3489,7 +3523,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 1; ++i) { + for (i = 0; i < len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -3615,7 +3649,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 3; ++i) { + for (i = 0; i < len - 3; ++i) { u32 orig = *(u32 *)(out_buf + i); @@ -3805,7 +3839,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 1; ++i) { + for (i = 0; i < len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -3891,7 +3925,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 3; ++i) { + for (i = 0; i < len - 3; ++i) { u32 orig = *(u32 *)(out_buf + i); @@ -4120,7 +4154,7 @@ skip_user_extras: /* See the comment in the earlier code; extras are sorted by size. */ - if ((s32)(afl->a_extras[j].len) > (s32)(len - i) || + if ((afl->a_extras[j].len) > (len - i) || !memcmp(afl->a_extras[j].data, out_buf + i, afl->a_extras[j].len) || !memchr(eff_map + EFF_APOS(i), 1, EFF_SPAN_ALEN(i, afl->a_extras[j].len))) { @@ -4837,7 +4871,7 @@ pacemaker_fuzzing: u32 copy_from, copy_to, copy_len; copy_len = choose_block_len(afl, new_len - 1); - if ((s32)copy_len > temp_len) copy_len = temp_len; + if (copy_len > temp_len) copy_len = temp_len; copy_from = rand_below(afl, new_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); @@ -5033,8 +5067,7 @@ pacemaker_fuzzing: the last differing byte. Bail out if the difference is just a single byte or so. */ - locate_diffs(in_buf, new_buf, MIN(len, (s32)target->len), &f_diff, - &l_diff); + locate_diffs(in_buf, new_buf, MIN(len, target->len), &f_diff, &l_diff); if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 66938635..aec57a6e 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -433,6 +433,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { q->passed_det = passed_det; q->trace_mini = NULL; q->testcase_buf = NULL; + q->mother = afl->queue_cur; #ifdef INTROSPECTION q->bitsmap_size = afl->bitsmap_size; diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 28585afe..955a9232 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -28,6 +28,8 @@ #include "afl-fuzz.h" #include "cmplog.h" +//#define _DEBUG + ///// Colorization struct range { @@ -35,6 +37,8 @@ struct range { u32 start; u32 end; struct range *next; + struct range *prev; + u8 ok; }; @@ -44,6 +48,8 @@ static struct range *add_range(struct range *ranges, u32 start, u32 end) { r->start = start; r->end = end; r->next = ranges; + r->ok = 0; + if (likely(ranges)) ranges->prev = r; return r; } @@ -51,45 +57,61 @@ static struct range *add_range(struct range *ranges, u32 start, u32 end) { static struct range *pop_biggest_range(struct range **ranges) { struct range *r = *ranges; - struct range *prev = NULL; struct range *rmax = NULL; - struct range *prev_rmax = NULL; u32 max_size = 0; while (r) { - u32 s = r->end - r->start; - if (s >= max_size) { + if (!r->ok) { + + u32 s = 1 + r->end - r->start; + + if (s >= max_size) { + + max_size = s; + rmax = r; - max_size = s; - prev_rmax = prev; - rmax = r; + } } - prev = r; r = r->next; } - if (rmax) { + return rmax; - if (prev_rmax) { +} - prev_rmax->next = rmax->next; +#ifdef _DEBUG +// static int logging = 0; +static void dump(char *txt, u8 *buf, u32 len) { - } else { + u32 i; + fprintf(stderr, "DUMP %s %llx ", txt, hash64(buf, len, 0)); + for (i = 0; i < len; i++) + fprintf(stderr, "%02x", buf[i]); + fprintf(stderr, "\n"); - *ranges = rmax->next; +} - } +static void dump_file(char *path, char *name, u32 counter, u8 *buf, u32 len) { - } + char fn[4096]; + if (!path) path = "."; + snprintf(fn, sizeof(fn), "%s/%s%d", path, name, counter); + int fd = open(fn, O_RDWR | O_CREAT | O_TRUNC, 0644); + if (fd >= 0) { - return rmax; + write(fd, buf, len); + close(fd); + + } } +#endif + static u8 get_exec_checksum(afl_state_t *afl, u8 *buf, u32 len, u64 *cksum) { if (unlikely(common_fuzz_stuff(afl, buf, len))) { return 1; } @@ -99,107 +121,270 @@ static u8 get_exec_checksum(afl_state_t *afl, u8 *buf, u32 len, u64 *cksum) { } -static void xor_replace(u8 *buf, u32 len) { +/* replace everything with different values but stay in the same type */ +static void type_replace(afl_state_t *afl, u8 *buf, u32 len) { u32 i; + u8 c; for (i = 0; i < len; ++i) { - buf[i] ^= 0xff; + // wont help for UTF or non-latin charsets + do { + + switch (buf[i]) { + + case 'A' ... 'F': + c = 'A' + rand_below(afl, 1 + 'F' - 'A'); + break; + case 'a' ... 'f': + c = 'a' + rand_below(afl, 1 + 'f' - 'a'); + break; + case '0': + c = '1'; + break; + case '1': + c = '0'; + break; + case '2' ... '9': + c = '2' + rand_below(afl, 1 + '9' - '2'); + break; + case 'G' ... 'Z': + c = 'G' + rand_below(afl, 1 + 'Z' - 'G'); + break; + case 'g' ... 'z': + c = 'g' + rand_below(afl, 1 + 'z' - 'g'); + break; + case '!' ... '*': + c = '!' + rand_below(afl, 1 + '*' - '!'); + break; + case ',' ... '.': + c = ',' + rand_below(afl, 1 + '.' - ','); + break; + case ':' ... '@': + c = ':' + rand_below(afl, 1 + '@' - ':'); + break; + case '[' ... '`': + c = '[' + rand_below(afl, 1 + '`' - '['); + break; + case '{' ... '~': + c = '{' + rand_below(afl, 1 + '~' - '{'); + break; + case '+': + c = '/'; + break; + case '/': + c = '+'; + break; + case ' ': + c = '\t'; + break; + case '\t': + c = ' '; + break; + /* + case '\r': + case '\n': + // nothing ... + break; + */ + default: + c = (buf[i] ^ 0xff); + + } + + } while (c == buf[i]); + + buf[i] = c; } } -static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum) { +static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum, + struct tainted **taints) { - struct range *ranges = add_range(NULL, 0, len); - u8 * backup = ck_alloc_nozero(len); + struct range * ranges = add_range(NULL, 0, len - 1), *rng; + struct tainted *taint = NULL; + u8 * backup = ck_alloc_nozero(len); + u8 * changed = ck_alloc_nozero(len); u64 orig_hit_cnt, new_hit_cnt; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; afl->stage_name = "colorization"; afl->stage_short = "colorization"; - afl->stage_max = 1000; + afl->stage_max = (len << 1); - struct range *rng = NULL; afl->stage_cur = 0; + memcpy(backup, buf, len); + memcpy(changed, buf, len); + type_replace(afl, changed, len); + while ((rng = pop_biggest_range(&ranges)) != NULL && afl->stage_cur < afl->stage_max) { - u32 s = rng->end - rng->start; + u32 s = 1 + rng->end - rng->start; + + memcpy(buf + rng->start, changed + rng->start, s); - if (s != 0) { + u64 cksum; + u64 start_us = get_cur_time_us(); + if (unlikely(get_exec_checksum(afl, buf, len, &cksum))) { - /* Range not empty */ + goto checksum_fail; - memcpy(backup, buf + rng->start, s); - xor_replace(buf + rng->start, s); + } + + u64 stop_us = get_cur_time_us(); + + /* Discard if the mutations change the path or if it is too decremental + in speed - how could the same path have a much different speed + though ...*/ + if (cksum != exec_cksum || + (unlikely(stop_us - start_us > 3 * afl->queue_cur->exec_us) && + likely(!afl->fixed_seed))) { + + memcpy(buf + rng->start, backup + rng->start, s); - u64 cksum; - u64 start_us = get_cur_time_us(); - if (unlikely(get_exec_checksum(afl, buf, len, &cksum))) { + if (s > 1) { // to not add 0 size ranges - goto checksum_fail; + ranges = add_range(ranges, rng->start, rng->start - 1 + s / 2); + ranges = add_range(ranges, rng->start + s / 2, rng->end); } - u64 stop_us = get_cur_time_us(); + if (ranges == rng) { + + ranges = rng->next; + if (ranges) { ranges->prev = NULL; } + + } else if (rng->next) { + + rng->prev->next = rng->next; + rng->next->prev = rng->prev; - /* Discard if the mutations change the paths or if it is too decremental - in speed */ - if (cksum != exec_cksum || - ((stop_us - start_us > 2 * afl->queue_cur->exec_us) && - likely(!afl->fixed_seed))) { + } else { - ranges = add_range(ranges, rng->start, rng->start + s / 2); - ranges = add_range(ranges, rng->start + s / 2 + 1, rng->end); - memcpy(buf + rng->start, backup, s); + if (rng->prev) { rng->prev->next = NULL; } } + free(rng); + + } else { + + rng->ok = 1; + } - ck_free(rng); - rng = NULL; ++afl->stage_cur; } - if (afl->stage_cur < afl->stage_max) { afl->queue_cur->fully_colorized = 1; } + rng = ranges; + while (rng) { - new_hit_cnt = afl->queued_paths + afl->unique_crashes; - afl->stage_finds[STAGE_COLORIZATION] += new_hit_cnt - orig_hit_cnt; - afl->stage_cycles[STAGE_COLORIZATION] += afl->stage_cur; - ck_free(backup); + rng = rng->next; - ck_free(rng); - rng = NULL; + } - while (ranges) { + u32 i = 1; + u32 positions = 0; + while (i) { + restart: + i = 0; + struct range *r = NULL; + u32 pos = (u32)-1; rng = ranges; - ranges = rng->next; - ck_free(rng); - rng = NULL; - } + while (rng) { - return 0; + if (rng->ok == 1 && rng->start < pos) { -checksum_fail: - if (rng) { ck_free(rng); } - ck_free(backup); + if (taint && taint->pos + taint->len == rng->start) { + + taint->len += (1 + rng->end - rng->start); + positions += (1 + rng->end - rng->start); + rng->ok = 2; + goto restart; + + } else { + + r = rng; + pos = rng->start; + + } + + } + + rng = rng->next; + + } + + if (r) { + + struct tainted *t = ck_alloc_nozero(sizeof(struct tainted)); + t->pos = r->start; + t->len = 1 + r->end - r->start; + positions += (1 + r->end - r->start); + if (likely(taint)) { taint->prev = t; } + t->next = taint; + t->prev = NULL; + taint = t; + r->ok = 2; + i = 1; + } + + } + + *taints = taint; + + /* temporary: clean ranges */ while (ranges) { rng = ranges; ranges = rng->next; ck_free(rng); - rng = NULL; } + new_hit_cnt = afl->queued_paths + afl->unique_crashes; + +#ifdef _DEBUG + /* + char fn[4096]; + snprintf(fn, sizeof(fn), "%s/introspection_color.txt", afl->out_dir); + FILE *f = fopen(fn, "a"); + if (f) { + + */ + FILE *f = stderr; + fprintf(f, + "Colorization: fname=%s len=%u result=%u execs=%u found=%llu " + "taint=%u\n", + afl->queue_cur->fname, len, afl->queue_cur->colorized, afl->stage_cur, + new_hit_cnt - orig_hit_cnt, positions); +/* + fclose(f); + + } + +*/ +#endif + + afl->stage_finds[STAGE_COLORIZATION] += new_hit_cnt - orig_hit_cnt; + afl->stage_cycles[STAGE_COLORIZATION] += afl->stage_cur; + ck_free(backup); + ck_free(changed); + + return 0; + +checksum_fail: + ck_free(backup); + ck_free(changed); + return 1; } @@ -212,12 +397,19 @@ static u8 its_fuzz(afl_state_t *afl, u8 *buf, u32 len, u8 *status) { orig_hit_cnt = afl->queued_paths + afl->unique_crashes; +#ifdef _DEBUG + dump("DATA", buf, len); +#endif + if (unlikely(common_fuzz_stuff(afl, buf, len))) { return 1; } new_hit_cnt = afl->queued_paths + afl->unique_crashes; if (unlikely(new_hit_cnt != orig_hit_cnt)) { +#ifdef _DEBUG + fprintf(stderr, "NEW FIND\n"); +#endif *status = 1; } else { @@ -278,11 +470,33 @@ static int strntoull(const char *str, size_t sz, char **end, int base, } static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, - u64 pattern, u64 repl, u64 o_pattern, u32 idx, - u8 *orig_buf, u8 *buf, u32 len, u8 do_reverse, - u8 *status) { - - if (!buf) { FATAL("BUG: buf was NULL. Please report this.\n"); } + u64 pattern, u64 repl, u64 o_pattern, + u64 changed_val, u8 attr, u32 idx, u32 taint_len, + u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, + u8 do_reverse, u8 lvl, u8 *status) { + + // (void)(changed_val); // TODO + // we can use the information in changed_val to see if there is a + // computable i2s transformation. + // if (pattern != o_pattern && repl != changed_val) { + + // u64 in_diff = pattern - o_pattern, out_diff = repl - changed_val; + // if (in_diff != out_diff) { + + // switch(in_diff) { + + // detect uppercase <-> lowercase, base64, hex encoding, etc.: + // repl = reverse_transform(TYPE, pattern); + // } + // } + // } + // not 100% but would have a chance to be detected + + // fprintf(stderr, + // "Encode: %llx->%llx into %llx(<-%llx) at pos=%u " + // "taint_len=%u shape=%u attr=%u\n", + // o_pattern, pattern, repl, changed_val, idx, taint_len, + // h->shape + 1, attr); u64 *buf_64 = (u64 *)&buf[idx]; u32 *buf_32 = (u32 *)&buf[idx]; @@ -293,76 +507,215 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u16 *o_buf_16 = (u16 *)&orig_buf[idx]; u8 * o_buf_8 = &orig_buf[idx]; - u32 its_len = len - idx; - // *status = 0; + u32 its_len = MIN(len - idx, taint_len); u8 * endptr; u8 use_num = 0, use_unum = 0; unsigned long long unum; long long num; - if (afl->queue_cur->is_ascii) { + // reverse atoi()/strnu?toll() is expensive, so we only to it in lvl == 3 + if (lvl & 4) { - endptr = buf_8; - if (strntoll(buf_8, len - idx, (char **)&endptr, 0, &num)) { + if (afl->queue_cur->is_ascii) { - if (!strntoull(buf_8, len - idx, (char **)&endptr, 0, &unum)) - use_unum = 1; + endptr = buf_8; + if (strntoll(buf_8, len - idx, (char **)&endptr, 0, &num)) { - } else + if (!strntoull(buf_8, len - idx, (char **)&endptr, 0, &unum)) + use_unum = 1; - use_num = 1; + } else - } + use_num = 1; - if (use_num && (u64)num == pattern) { + } - size_t old_len = endptr - buf_8; - size_t num_len = snprintf(NULL, 0, "%lld", num); +#ifdef _DEBUG + if (idx == 0) + fprintf(stderr, "ASCII is=%u use_num=%u use_unum=%u idx=%u %llx==%llx\n", + afl->queue_cur->is_ascii, use_num, use_unum, idx, num, pattern); +#endif - u8 *new_buf = afl_realloc((void **)&afl->out_scratch_buf, len + num_len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - memcpy(new_buf, buf, idx); + // num is likely not pattern as atoi("AAA") will be zero... + if (use_num && ((u64)num == pattern || !num)) { - snprintf(new_buf + idx, num_len, "%lld", num); - memcpy(new_buf + idx + num_len, buf_8 + old_len, len - idx - old_len); + u8 tmp_buf[32]; + size_t num_len = snprintf(tmp_buf, sizeof(tmp_buf), "%lld", repl); + size_t old_len = endptr - buf_8; - if (unlikely(its_fuzz(afl, new_buf, len, status))) { return 1; } + u8 *new_buf = afl_realloc((void **)&afl->out_scratch_buf, len + num_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } - } else if (use_unum && unum == pattern) { + memcpy(new_buf, buf, idx); + memcpy(new_buf + idx, tmp_buf, num_len); + memcpy(new_buf + idx + num_len, buf_8 + old_len, len - idx - old_len); - size_t old_len = endptr - buf_8; - size_t num_len = snprintf(NULL, 0, "%llu", unum); + if (new_buf[idx + num_len] >= '0' && new_buf[idx + num_len] <= '9') { + + new_buf[idx + num_len] = ' '; + + } - u8 *new_buf = afl_realloc((void **)&afl->out_scratch_buf, len + num_len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - memcpy(new_buf, buf, idx); + if (unlikely(its_fuzz(afl, new_buf, len, status))) { return 1; } - snprintf(new_buf + idx, num_len, "%llu", unum); - memcpy(new_buf + idx + num_len, buf_8 + old_len, len - idx - old_len); + } else if (use_unum && (unum == pattern || !unum)) { - if (unlikely(its_fuzz(afl, new_buf, len, status))) { return 1; } + u8 tmp_buf[32]; + size_t num_len = snprintf(tmp_buf, sizeof(tmp_buf), "%llu", repl); + size_t old_len = endptr - buf_8; + + u8 *new_buf = afl_realloc((void **)&afl->out_scratch_buf, len + num_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } + + memcpy(new_buf, buf, idx); + memcpy(new_buf + idx, tmp_buf, num_len); + memcpy(new_buf + idx + num_len, buf_8 + old_len, len - idx - old_len); + + if (new_buf[idx + num_len] >= '0' && new_buf[idx + num_len] <= '9') { + + new_buf[idx + num_len] = ' '; + + } + + if (unlikely(its_fuzz(afl, new_buf, len, status))) { return 1; } + + } } - if (SHAPE_BYTES(h->shape) >= 8 && *status != 1) { + // we only allow this for ascii2integer (above) + if (unlikely(pattern == o_pattern)) { return 0; } - if (its_len >= 8 && *buf_64 == pattern && *o_buf_64 == o_pattern) { + if ((lvl & 1) || ((lvl & 2) && (attr >= 8 && attr <= 15)) || attr >= 16) { - *buf_64 = repl; - if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - *buf_64 = pattern; + if (SHAPE_BYTES(h->shape) >= 8 && *status != 1) { + + // if (its_len >= 8 && (attr == 0 || attr >= 8)) + // fprintf(stderr, + // "TestU64: %u>=4 %x==%llx" + // " %x==%llx (idx=%u attr=%u) <= %llx<-%llx\n", + // its_len, *buf_32, pattern, *o_buf_32, o_pattern, idx, attr, + // repl, changed_val); + + // if this is an fcmp (attr & 8 == 8) then do not compare the patterns - + // due to a bug in llvm dynamic float bitcasts do not work :( + // the value 16 means this is a +- 1.0 test case + if (its_len >= 8 && + ((*buf_64 == pattern && *o_buf_64 == o_pattern) || attr >= 16)) { + + u64 tmp_64 = *buf_64; + *buf_64 = repl; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + if (*status == 1) { memcpy(cbuf + idx, buf_64, 8); } + *buf_64 = tmp_64; + + // fprintf(stderr, "Status=%u\n", *status); + + } + + // reverse encoding + if (do_reverse && *status != 1) { + + if (unlikely(cmp_extend_encoding(afl, h, SWAP64(pattern), SWAP64(repl), + SWAP64(o_pattern), SWAP64(changed_val), + attr, idx, taint_len, orig_buf, buf, + cbuf, len, 0, lvl, status))) { + + return 1; + + } + + } } - // reverse encoding - if (do_reverse && *status != 1) { + if (SHAPE_BYTES(h->shape) >= 4 && *status != 1) { - if (unlikely(cmp_extend_encoding(afl, h, SWAP64(pattern), SWAP64(repl), - SWAP64(o_pattern), idx, orig_buf, buf, - len, 0, status))) { + // if (its_len >= 4 && (attr <= 1 || attr >= 8)) + // fprintf(stderr, + // "TestU32: %u>=4 %x==%llx" + // " %x==%llx (idx=%u attr=%u) <= %llx<-%llx\n", + // its_len, *buf_32, pattern, *o_buf_32, o_pattern, idx, attr, + // repl, changed_val); - return 1; + if (its_len >= 4 && + ((*buf_32 == (u32)pattern && *o_buf_32 == (u32)o_pattern) || + attr >= 16)) { + + u32 tmp_32 = *buf_32; + *buf_32 = (u32)repl; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + if (*status == 1) { memcpy(cbuf + idx, buf_32, 4); } + *buf_32 = tmp_32; + + // fprintf(stderr, "Status=%u\n", *status); + + } + + // reverse encoding + if (do_reverse && *status != 1) { + + if (unlikely(cmp_extend_encoding(afl, h, SWAP32(pattern), SWAP32(repl), + SWAP32(o_pattern), SWAP32(changed_val), + attr, idx, taint_len, orig_buf, buf, + cbuf, len, 0, lvl, status))) { + + return 1; + + } + + } + + } + + if (SHAPE_BYTES(h->shape) >= 2 && *status != 1) { + + if (its_len >= 2 && + ((*buf_16 == (u16)pattern && *o_buf_16 == (u16)o_pattern) || + attr >= 16)) { + + u16 tmp_16 = *buf_16; + *buf_16 = (u16)repl; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + if (*status == 1) { memcpy(cbuf + idx, buf_16, 2); } + *buf_16 = tmp_16; + + } + + // reverse encoding + if (do_reverse && *status != 1) { + + if (unlikely(cmp_extend_encoding(afl, h, SWAP16(pattern), SWAP16(repl), + SWAP16(o_pattern), SWAP16(changed_val), + attr, idx, taint_len, orig_buf, buf, + cbuf, len, 0, lvl, status))) { + + return 1; + + } + + } + + } + + if (*status != 1) { // u8 + + // if (its_len >= 1 && (attr <= 1 || attr >= 8)) + // fprintf(stderr, + // "TestU8: %u>=1 %x==%x %x==%x (idx=%u attr=%u) <= %x<-%x\n", + // its_len, *buf_8, pattern, *o_buf_8, o_pattern, idx, attr, + // repl, changed_val); + + if (its_len >= 1 && + ((*buf_8 == (u8)pattern && *o_buf_8 == (u8)o_pattern) || + attr >= 16)) { + + u8 tmp_8 = *buf_8; + *buf_8 = (u8)repl; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + if (*status == 1) { cbuf[idx] = *buf_8; } + *buf_8 = tmp_8; } @@ -370,49 +723,205 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - if (SHAPE_BYTES(h->shape) >= 4 && *status != 1) { + // here we add and subract 1 from the value, but only if it is not an + // == or != comparison + // Bits: 1 = Equal, 2 = Greater, 3 = Lesser, 4 = Float - if (its_len >= 4 && *buf_32 == (u32)pattern && - *o_buf_32 == (u32)o_pattern) { + if (lvl < 4) { return 0; } - *buf_32 = (u32)repl; - if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - *buf_32 = pattern; + if (attr >= 8 && attr < 16) { // lesser/greater integer comparison + + u64 repl_new; + if (SHAPE_BYTES(h->shape) == 4 && its_len >= 4) { + + float *f = (float *)&repl; + float g = *f; + g += 1.0; + u32 *r = (u32 *)&g; + repl_new = (u32)*r; + + } else if (SHAPE_BYTES(h->shape) == 8 && its_len >= 8) { + + double *f = (double *)&repl; + double g = *f; + g += 1.0; + + u64 *r = (u64 *)&g; + repl_new = *r; + + } else { + + return 0; } - // reverse encoding - if (do_reverse && *status != 1) { + changed_val = repl_new; + + if (unlikely(cmp_extend_encoding(afl, h, pattern, repl_new, o_pattern, + changed_val, 16, idx, taint_len, orig_buf, + buf, cbuf, len, 1, lvl, status))) { + + return 1; + + } + + if (SHAPE_BYTES(h->shape) == 4) { + + float *f = (float *)&repl; + float g = *f; + g -= 1.0; + u32 *r = (u32 *)&g; + repl_new = (u32)*r; + + } else if (SHAPE_BYTES(h->shape) == 8) { + + double *f = (double *)&repl; + double g = *f; + g -= 1.0; + u64 *r = (u64 *)&g; + repl_new = *r; + + } else { + + return 0; + + } + + changed_val = repl_new; + + if (unlikely(cmp_extend_encoding(afl, h, pattern, repl_new, o_pattern, + changed_val, 16, idx, taint_len, orig_buf, + buf, cbuf, len, 1, lvl, status))) { + + return 1; + + } + + // transform double to float, llvm likes to do that internally ... + if (SHAPE_BYTES(h->shape) == 8 && its_len >= 4) { + + double *f = (double *)&repl; + float g = (float)*f; + repl_new = 0; +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + memcpy((char *)&repl_new, (char *)&g, 4); +#else + memcpy(((char *)&repl_new) + 4, (char *)&g, 4); +#endif + changed_val = repl_new; + h->shape = 3; // modify shape - if (unlikely(cmp_extend_encoding(afl, h, SWAP32(pattern), SWAP32(repl), - SWAP32(o_pattern), idx, orig_buf, buf, - len, 0, status))) { + // fprintf(stderr, "DOUBLE2FLOAT %llx\n", repl_new); + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + h->shape = 7; return 1; } + h->shape = 7; // recover shape + + } + + } else if (attr > 1 && attr < 8) { // lesser/greater integer comparison + + u64 repl_new; + + repl_new = repl + 1; + changed_val = repl_new; + if (unlikely(cmp_extend_encoding(afl, h, pattern, repl_new, o_pattern, + changed_val, 32, idx, taint_len, orig_buf, + buf, cbuf, len, 1, lvl, status))) { + + return 1; + + } + + repl_new = repl - 1; + changed_val = repl_new; + if (unlikely(cmp_extend_encoding(afl, h, pattern, repl_new, o_pattern, + changed_val, 32, idx, taint_len, orig_buf, + buf, cbuf, len, 1, lvl, status))) { + + return 1; + } } - if (SHAPE_BYTES(h->shape) >= 2 && *status != 1) { + return 0; - if (its_len >= 2 && *buf_16 == (u16)pattern && - *o_buf_16 == (u16)o_pattern) { +} - *buf_16 = (u16)repl; +static u8 cmp_extend_encoding128(afl_state_t *afl, struct cmp_header *h, + u128 pattern, u128 repl, u128 o_pattern, + u128 changed_val, u8 attr, u32 idx, + u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf, + u32 len, u8 do_reverse, u8 lvl, u8 *status) { + + u128 *buf_128 = (u128 *)&buf[idx]; + u64 * buf0 = (u64 *)&buf[idx]; + u64 * buf1 = (u64 *)(buf + idx + 8); + u128 *o_buf_128 = (u128 *)&orig_buf[idx]; + u32 its_len = MIN(len - idx, taint_len); + u64 v10 = (u64)repl; + u64 v11 = (u64)(repl >> 64); + + // if this is an fcmp (attr & 8 == 8) then do not compare the patterns - + // due to a bug in llvm dynamic float bitcasts do not work :( + // the value 16 means this is a +- 1.0 test case + if (its_len >= 16) { + +#ifdef _DEBUG + fprintf(stderr, "TestU128: %u>=16 (idx=%u attr=%u) (%u)\n", its_len, idx, + attr, do_reverse); + u64 v00 = (u64)pattern; + u64 v01 = pattern >> 64; + u64 ov00 = (u64)o_pattern; + u64 ov01 = o_pattern >> 64; + u64 ov10 = (u64)changed_val; + u64 ov11 = changed_val >> 64; + u64 b00 = (u64)*buf_128; + u64 b01 = *buf_128 >> 64; + u64 ob00 = (u64)*o_buf_128; + u64 ob01 = *o_buf_128 >> 64; + fprintf(stderr, + "TestU128: %llx:%llx==%llx:%llx" + " %llx:%llx==%llx:%llx <= %llx:%llx<-%llx:%llx\n", + b01, b00, v01, v00, ob01, ob00, ov01, ov00, v11, v10, ov11, ov10); +#endif + + if (*buf_128 == pattern && *o_buf_128 == o_pattern) { + + u128 tmp_128 = *buf_128; + // *buf_128 = repl; <- this crashes +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + *buf0 = v10; + *buf1 = v11; +#else + *buf1 = v10; + *buf0 = v11; +#endif if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - *buf_16 = (u16)pattern; + if (*status == 1) { memcpy(cbuf + idx, buf_128, 16); } + *buf_128 = tmp_128; + +#ifdef _DEBUG + fprintf(stderr, "Status=%u\n", *status); +#endif } // reverse encoding if (do_reverse && *status != 1) { - if (unlikely(cmp_extend_encoding(afl, h, SWAP16(pattern), SWAP16(repl), - SWAP16(o_pattern), idx, orig_buf, buf, - len, 0, status))) { + if (unlikely(cmp_extend_encoding128( + afl, h, SWAPN(pattern, 128), SWAPN(repl, 128), + SWAPN(o_pattern, 128), SWAPN(changed_val, 128), attr, idx, + taint_len, orig_buf, buf, cbuf, len, 0, lvl, status))) { return 1; @@ -422,14 +931,82 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - /* avoid CodeQL warning on unsigned overflow */ - if (/* SHAPE_BYTES(h->shape) >= 1 && */ *status != 1) { + return 0; - if (its_len >= 1 && *buf_8 == (u8)pattern && *o_buf_8 == (u8)o_pattern) { +} - *buf_8 = (u8)repl; +// uh a pointer read from (long double*) reads 12 bytes, not 10 ... +// so lets make this complicated. +static u8 cmp_extend_encoding_ld(afl_state_t *afl, struct cmp_header *h, + u8 *pattern, u8 *repl, u8 *o_pattern, + u8 *changed_val, u8 attr, u32 idx, + u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf, + u32 len, u8 do_reverse, u8 lvl, u8 *status) { + + u8 *buf_ld = &buf[idx], *o_buf_ld = &orig_buf[idx], backup[10]; + u32 its_len = MIN(len - idx, taint_len); + + if (its_len >= 10) { + +#ifdef _DEBUG + fprintf(stderr, "TestUld: %u>=10 (len=%u idx=%u attr=%u) (%u)\n", its_len, + len, idx, attr, do_reverse); + fprintf(stderr, "TestUld: "); + u32 i; + for (i = 0; i < 10; i++) + fprintf(stderr, "%02x", pattern[i]); + fprintf(stderr, "=="); + for (i = 0; i < 10; i++) + fprintf(stderr, "%02x", buf_ld[i]); + fprintf(stderr, " "); + for (i = 0; i < 10; i++) + fprintf(stderr, "%02x", o_pattern[i]); + fprintf(stderr, "=="); + for (i = 0; i < 10; i++) + fprintf(stderr, "%02x", o_buf_ld[i]); + fprintf(stderr, " <= "); + for (i = 0; i < 10; i++) + fprintf(stderr, "%02x", repl[i]); + fprintf(stderr, "=="); + for (i = 0; i < 10; i++) + fprintf(stderr, "%02x", changed_val[i]); + fprintf(stderr, "\n"); +#endif + + if (!memcmp(pattern, buf_ld, 10) && !memcmp(o_pattern, o_buf_ld, 10)) { + + // if this is an fcmp (attr & 8 == 8) then do not compare the patterns - + // due to a bug in llvm dynamic float bitcasts do not work :( + // the value 16 means this is a +- 1.0 test case + + memcpy(backup, buf_ld, 10); + memcpy(buf_ld, repl, 10); if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - *buf_8 = (u8)pattern; + if (*status == 1) { memcpy(cbuf + idx, repl, 10); } + memcpy(buf_ld, backup, 10); + +#ifdef _DEBUG + fprintf(stderr, "Status=%u\n", *status); +#endif + + } + + } + + // reverse encoding + if (do_reverse && *status != 1) { + + u8 sp[10], sr[10], osp[10], osr[10]; + SWAPNN(sp, pattern, 10); + SWAPNN(sr, repl, 10); + SWAPNN(osp, o_pattern, 10); + SWAPNN(osr, changed_val, 10); + + if (unlikely(cmp_extend_encoding_ld(afl, h, sp, sr, osp, osr, attr, idx, + taint_len, orig_buf, buf, cbuf, len, 0, + lvl, status))) { + + return 1; } @@ -445,10 +1022,6 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { u32 k; u8 cons_ff = 0, cons_0 = 0; - - if (shape > sizeof(v)) - FATAL("shape is greater than %zu, please report!", sizeof(v)); - for (k = 0; k < shape; ++k) { if (b[k] == 0) { @@ -457,7 +1030,7 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { } else if (b[k] == 0xff) { - ++cons_ff; + ++cons_0; } else { @@ -493,28 +1066,126 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { } -static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { +static void try_to_add_to_dict128(afl_state_t *afl, u128 v) { - struct cmp_header *h = &afl->shm.cmp_map->headers[key]; - u32 i, j, idx; + u8 *b = (u8 *)&v; - u32 loggeds = h->hits; + u32 k; + u8 cons_ff = 0, cons_0 = 0; + for (k = 0; k < 16; ++k) { + + if (b[k] == 0) { + + ++cons_0; + + } else if (b[k] == 0xff) { + + ++cons_0; + + } else { + + cons_0 = cons_ff = 0; + + } + + // too many uninteresting values? try adding 2 64-bit values + if (cons_0 > 6 || cons_ff > 6) { + + u64 v64 = (u64)v; + try_to_add_to_dict(afl, v64, 8); + v64 = (u64)(v >> 64); + try_to_add_to_dict(afl, v64, 8); + + return; + + } + + } + + maybe_add_auto(afl, (u8 *)&v, 16); + u128 rev = SWAPN(v, 128); + maybe_add_auto(afl, (u8 *)&rev, 16); + +} + +static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) { + + u8 *b = (u8 *)&v; + + u32 k; + u8 cons_ff = 0, cons_0 = 0; +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + for (k = 0; k < size; ++k) { + +#else + for (k = 16 - size; k < 16; ++k) { + +#endif + if (b[k] == 0) { + + ++cons_0; + + } else if (b[k] == 0xff) { + + ++cons_0; + + } else { + + cons_0 = cons_ff = 0; + + } + + } + + maybe_add_auto(afl, (u8 *)&v, size); + u128 rev = SWAPN(v, size); + maybe_add_auto(afl, (u8 *)&rev, size); + +} + +static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, + u32 len, u32 lvl, struct tainted *taint) { + + struct cmp_header *h = &afl->shm.cmp_map->headers[key]; + struct tainted * t; + u32 i, j, idx, taint_len; + u32 have_taint = 1, is_128 = 0, is_n = 0, is_ld = 0; + u32 loggeds = h->hits; if (h->hits > CMP_MAP_H) { loggeds = CMP_MAP_H; } u8 status = 0; - // opt not in the paper - u32 fails; - u8 found_one = 0; + u8 found_one = 0; /* loop cmps are useless, detect and ignore them */ - u64 s_v0, s_v1; - u8 s_v0_fixed = 1, s_v1_fixed = 1; - u8 s_v0_inc = 1, s_v1_inc = 1; - u8 s_v0_dec = 1, s_v1_dec = 1; + u128 s128_v0 = 0, s128_v1 = 0, orig_s128_v0 = 0, orig_s128_v1 = 0; + long double ld0, ld1, o_ld0, o_ld1; + u64 s_v0, s_v1; + u8 s_v0_fixed = 1, s_v1_fixed = 1; + u8 s_v0_inc = 1, s_v1_inc = 1; + u8 s_v0_dec = 1, s_v1_dec = 1; - for (i = 0; i < loggeds; ++i) { + switch (SHAPE_BYTES(h->shape)) { + + case 1: + case 2: + case 4: + case 8: + break; + case 16: + is_128 = 1; + break; + case 10: + if (h->attribute & 8) { is_ld = 1; } + // fall through + default: + is_n = 1; - fails = 0; + } + + // FCmp not in if level 1 only + if ((h->attribute & 8) && lvl < 2) return 0; + + for (i = 0; i < loggeds; ++i) { struct cmp_operands *o = &afl->shm.cmp_map->log[key][i]; @@ -551,55 +1222,242 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } - for (idx = 0; idx < len && fails < 8; ++idx) { +#ifdef _DEBUG + fprintf(stderr, "Handling: %llx->%llx vs %llx->%llx attr=%u shape=%u\n", + orig_o->v0, o->v0, orig_o->v1, o->v1, h->attribute, + SHAPE_BYTES(h->shape)); +#endif + + if (taint) { + + t = taint; + + while (t->next) { + + t = t->next; + + } + + } else { + + have_taint = 0; + t = NULL; + + } + + if (unlikely(is_128 || is_n)) { + + s128_v0 = ((u128)o->v0) + (((u128)o->v0_128) << 64); + s128_v1 = ((u128)o->v1) + (((u128)o->v1_128) << 64); + orig_s128_v0 = ((u128)orig_o->v0) + (((u128)orig_o->v0_128) << 64); + orig_s128_v1 = ((u128)orig_o->v1) + (((u128)orig_o->v1_128) << 64); + + if (is_ld) { + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + memcpy((char *)&ld0, (char *)&s128_v0, sizeof(long double)); + memcpy((char *)&ld1, (char *)&s128_v1, sizeof(long double)); + memcpy((char *)&o_ld0, (char *)&orig_s128_v0, sizeof(long double)); + memcpy((char *)&o_ld1, (char *)&orig_s128_v1, sizeof(long double)); +#else + memcpy((char *)&ld0, (char *)(&s128_v0) + 6, sizeof(long double)); + memcpy((char *)&ld1, (char *)(&s128_v1) + 6, sizeof(long double)); + memcpy((char *)&o_ld0, (char *)(&orig_s128_v0) + 6, + sizeof(long double)); + memcpy((char *)&o_ld1, (char *)(&orig_s128_v1) + 6, + sizeof(long double)); +#endif + + } + + } + + for (idx = 0; idx < len; ++idx) { + + if (have_taint) { + + if (!t || idx < t->pos) { + + continue; + + } else { + + taint_len = t->pos + t->len - idx; + + if (idx == t->pos + t->len - 1) { t = t->prev; } + + } + + } else { + + taint_len = len - idx; + + } status = 0; - if (unlikely(cmp_extend_encoding(afl, h, o->v0, o->v1, orig_o->v0, idx, - orig_buf, buf, len, 1, &status))) { - return 1; + if (is_ld) { // long double special case + + if (ld0 != o_ld0 && o_ld1 != o_ld0) { + + if (unlikely(cmp_extend_encoding_ld( + afl, h, (u8 *)&ld0, (u8 *)&ld1, (u8 *)&o_ld0, (u8 *)&o_ld1, + h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, + lvl, &status))) { + + return 1; + + } + + } + + if (status == 1) { + + found_one = 1; + break; + + } + + if (ld1 != o_ld1 && o_ld0 != o_ld1) { + + if (unlikely(cmp_extend_encoding_ld( + afl, h, (u8 *)&ld1, (u8 *)&ld0, (u8 *)&o_ld1, (u8 *)&o_ld0, + h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, + lvl, &status))) { + + return 1; + + } + + } + + if (status == 1) { + + found_one = 1; + break; + + } + + } + + if (is_128) { // u128 special case + + if (s128_v0 != orig_s128_v0 && orig_s128_v0 != orig_s128_v1) { + + if (unlikely(cmp_extend_encoding128( + afl, h, s128_v0, s128_v1, orig_s128_v0, orig_s128_v1, + h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, + lvl, &status))) { + + return 1; + + } + + } + + if (status == 1) { + + found_one = 1; + break; + + } + + if (s128_v1 != orig_s128_v1 && orig_s128_v1 != orig_s128_v0) { + + if (unlikely(cmp_extend_encoding128( + afl, h, s128_v1, s128_v0, orig_s128_v1, orig_s128_v0, + h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, + lvl, &status))) { + + return 1; + + } + + } + + if (status == 1) { + + found_one = 1; + break; + + } } - if (status == 2) { + // even for u128 and long double do cmp_extend_encoding() because + // if we got here their own special trials failed and it might just be + // a cast from e.g. u64 to u128 from the input data. - ++fails; + if ((o->v0 != orig_o->v0 || lvl >= 4) && orig_o->v0 != orig_o->v1) { - } else if (status == 1) { + if (unlikely(cmp_extend_encoding( + afl, h, o->v0, o->v1, orig_o->v0, orig_o->v1, h->attribute, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, &status))) { + return 1; + + } + + } + + if (status == 1) { + + found_one = 1; break; } status = 0; - if (unlikely(cmp_extend_encoding(afl, h, o->v1, o->v0, orig_o->v1, idx, - orig_buf, buf, len, 1, &status))) { + if ((o->v1 != orig_o->v1 || lvl >= 4) && orig_o->v0 != orig_o->v1) { - return 1; + if (unlikely(cmp_extend_encoding( + afl, h, o->v1, o->v0, orig_o->v1, orig_o->v0, h->attribute, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, &status))) { - } + return 1; - if (status == 2) { + } - ++fails; + } - } else if (status == 1) { + if (status == 1) { + found_one = 1; break; } } - if (status == 1) { found_one = 1; } +#ifdef _DEBUG + fprintf(stderr, + "END: %llx->%llx vs %llx->%llx attr=%u i=%u found=%u is128=%u " + "isN=%u size=%u\n", + orig_o->v0, o->v0, orig_o->v1, o->v1, h->attribute, i, found_one, + is_128, is_n, SHAPE_BYTES(h->shape)); +#endif // If failed, add to dictionary - if (fails == 8) { + if (!found_one) { if (afl->pass_stats[key].total == 0) { - try_to_add_to_dict(afl, o->v0, SHAPE_BYTES(h->shape)); - try_to_add_to_dict(afl, o->v1, SHAPE_BYTES(h->shape)); + if (unlikely(is_128)) { + + try_to_add_to_dict128(afl, s128_v0); + try_to_add_to_dict128(afl, s128_v1); + + } else if (unlikely(is_n)) { + + try_to_add_to_dictN(afl, s128_v0, SHAPE_BYTES(h->shape)); + try_to_add_to_dictN(afl, s128_v1, SHAPE_BYTES(h->shape)); + + } else { + + try_to_add_to_dict(afl, o->v0, SHAPE_BYTES(h->shape)); + try_to_add_to_dict(afl, o->v1, SHAPE_BYTES(h->shape)); + + } } @@ -630,20 +1488,19 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, - u8 *o_pattern, u32 idx, u8 *orig_buf, u8 *buf, - u32 len, u8 *status) { + u8 *o_pattern, u32 idx, u32 taint_len, + u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, + u8 *status) { u32 i; u32 its_len = MIN((u32)32, len - idx); - + its_len = MIN(its_len, taint_len); u8 save[32]; memcpy(save, &buf[idx], its_len); - *status = 0; - for (i = 0; i < its_len; ++i) { - if (pattern[i] != buf[idx + i] || o_pattern[i] != orig_buf[idx + i] || + if ((pattern[i] != buf[idx + i] && o_pattern[i] != orig_buf[idx + i]) || *status == 1) { break; @@ -654,6 +1511,8 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); } + } memcpy(&buf[idx], save, i); @@ -661,23 +1520,21 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } -static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { +static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, + u32 len, struct tainted *taint) { + struct tainted * t; struct cmp_header *h = &afl->shm.cmp_map->headers[key]; - u32 i, j, idx; + u32 i, j, idx, have_taint = 1, taint_len; u32 loggeds = h->hits; if (h->hits > CMP_MAP_RTN_H) { loggeds = CMP_MAP_RTN_H; } u8 status = 0; - // opt not in the paper - // u32 fails = 0; u8 found_one = 0; for (i = 0; i < loggeds; ++i) { - u32 fails = 0; - struct cmpfn_operands *o = &((struct cmpfn_operands *)afl->shm.cmp_map->log[key])[i]; @@ -696,50 +1553,84 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } - for (idx = 0; idx < len && fails < 8; ++idx) { + if (taint) { - if (unlikely(rtn_extend_encoding(afl, o->v0, o->v1, orig_o->v0, idx, - orig_buf, buf, len, &status))) { + t = taint; + while (t->next) { - return 1; + t = t->next; } - if (status == 2) { + } else { - ++fails; + have_taint = 0; + t = NULL; - } else if (status == 1) { + } - break; + for (idx = 0; idx < len; ++idx) { + + if (have_taint) { + + if (!t || idx < t->pos) { + + continue; + + } else { + + taint_len = t->pos + t->len - idx; + + if (idx == t->pos + t->len - 1) { t = t->prev; } + + } + + } else { + + taint_len = len - idx; } - if (unlikely(rtn_extend_encoding(afl, o->v1, o->v0, orig_o->v1, idx, - orig_buf, buf, len, &status))) { + status = 0; + + if (unlikely(rtn_extend_encoding(afl, o->v0, o->v1, orig_o->v0, idx, + taint_len, orig_buf, buf, cbuf, len, + &status))) { return 1; } - if (status == 2) { + if (status == 1) { + + found_one = 1; + break; + + } + + status = 0; + + if (unlikely(rtn_extend_encoding(afl, o->v1, o->v0, orig_o->v1, idx, + taint_len, orig_buf, buf, cbuf, len, + &status))) { - ++fails; + return 1; - } else if (status == 1) { + } + if (status == 1) { + + found_one = 1; break; } } - if (status == 1) { found_one = 1; } - // If failed, add to dictionary - if (fails == 8) { + if (!found_one) { - if (afl->pass_stats[key].total == 0) { + if (unlikely(!afl->pass_stats[key].total)) { maybe_add_auto(afl, o->v0, SHAPE_BYTES(h->shape)); maybe_add_auto(afl, o->v1, SHAPE_BYTES(h->shape)); @@ -791,7 +1682,44 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, memcpy(afl->orig_cmp_map, afl->shm.cmp_map, sizeof(struct cmp_map)); - if (unlikely(colorization(afl, buf, len, exec_cksum))) { return 1; } + struct tainted *taint = NULL; + + if (!afl->queue_cur->taint || !afl->queue_cur->cmplog_colorinput) { + + if (unlikely(colorization(afl, buf, len, exec_cksum, &taint))) { return 1; } + + // no taint? still try, create a dummy to prevent again colorization + if (!taint) { + + taint = ck_alloc(sizeof(struct tainted)); + taint->len = len; + + } + + } else { + + buf = afl->queue_cur->cmplog_colorinput; + taint = afl->queue_cur->taint; + // reget the cmplog information + if (unlikely(common_fuzz_cmplog_stuff(afl, buf, len))) { return 1; } + + } + +#ifdef _DEBUG + dump("ORIG", orig_buf, len); + dump("NEW ", buf, len); +#endif + + struct tainted *t = taint; + + while (t) { + +#ifdef _DEBUG + fprintf(stderr, "T: pos=%u len=%u\n", t->pos, t->len); +#endif + t = t->next; + + } // do it manually, forkserver clear only afl->fsrv.trace_bits memset(afl->shm.cmp_map->headers, 0, sizeof(afl->shm.cmp_map->headers)); @@ -807,15 +1735,38 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, afl->stage_max = 0; afl->stage_cur = 0; + u32 lvl; + u32 cmplog_done = afl->queue_cur->colorized; + u32 cmplog_lvl = afl->cmplog_lvl; + if (!cmplog_done) { + + lvl = 1; + + } else { + + lvl = 0; + + } + + if (cmplog_lvl >= 2 && cmplog_done < 2) { lvl += 2; } + if (cmplog_lvl >= 3 && cmplog_done < 3) { lvl += 4; } + + u8 *cbuf = afl_realloc((void **)&afl->in_scratch_buf, len + 128); + memcpy(cbuf, orig_buf, len); + u8 *virgin_backup = afl_realloc((void **)&afl->ex_buf, afl->shm.map_size); + memcpy(virgin_backup, afl->virgin_bits, afl->shm.map_size); + u32 k; for (k = 0; k < CMP_MAP_W; ++k) { if (!afl->shm.cmp_map->headers[k].hits) { continue; } - if (afl->pass_stats[k].total && - (rand_below(afl, afl->pass_stats[k].total) >= - afl->pass_stats[k].faileds || - afl->pass_stats[k].total == 0xff)) { + if (afl->pass_stats[k].faileds == 0xff || + afl->pass_stats[k].total == 0xff) { + +#ifdef _DEBUG + fprintf(stderr, "DISABLED %u\n", k); +#endif afl->shm.cmp_map->headers[k].hits = 0; // ignore this cmp @@ -841,11 +1792,19 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, if (afl->shm.cmp_map->headers[k].type == CMP_TYPE_INS) { - if (unlikely(cmp_fuzz(afl, k, orig_buf, buf, len))) { goto exit_its; } + if (unlikely(cmp_fuzz(afl, k, orig_buf, buf, cbuf, len, lvl, taint))) { + + goto exit_its; + + } } else { - if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, len))) { goto exit_its; } + if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, cbuf, len, taint))) { + + goto exit_its; + + } } @@ -854,12 +1813,86 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, r = 0; exit_its: + + afl->queue_cur->colorized = afl->cmplog_lvl; + if (afl->cmplog_lvl == CMPLOG_LVL_MAX) { + + ck_free(afl->queue_cur->cmplog_colorinput); + t = taint; + while (taint) { + + t = taint->next; + ck_free(taint); + taint = t; + + } + + afl->queue_cur->taint = NULL; + + } else { + + if (!afl->queue_cur->taint) { afl->queue_cur->taint = taint; } + + if (!afl->queue_cur->cmplog_colorinput) { + + afl->queue_cur->cmplog_colorinput = ck_alloc_nozero(len); + memcpy(afl->queue_cur->cmplog_colorinput, buf, len); + memcpy(buf, orig_buf, len); + + } + + } + + // copy the current virgin bits so we can recover the information + u8 *virgin_save = afl_realloc((void **)&afl->eff_buf, afl->shm.map_size); + memcpy(virgin_save, afl->virgin_bits, afl->shm.map_size); + // reset virgin bits to the backup previous to redqueen + memcpy(afl->virgin_bits, virgin_backup, afl->shm.map_size); + + u8 status = 0; + its_fuzz(afl, cbuf, len, &status); + + // now combine with the saved virgin bits +#ifdef WORD_SIZE_64 + u64 *v = (u64 *)afl->virgin_bits; + u64 *s = (u64 *)virgin_save; + u32 i; + for (i = 0; i < (afl->shm.map_size >> 3); i++) { + + v[i] &= s[i]; + + } + +#else + u32 *v = (u64 *)afl->virgin_bits; + u32 *s = (u64 *)virgin_save; + u32 i; + for (i = 0; i < (afl->shm.map_size >> 2); i++) { + + v[i] &= s[i]; + + } + +#endif + +#ifdef _DEBUG + dump("COMB", cbuf, len); + if (status == 1) { + + fprintf(stderr, "NEW COMBINED\n"); + + } else { + + fprintf(stderr, "NO new combined\n"); + + } + +#endif + new_hit_cnt = afl->queued_paths + afl->unique_crashes; afl->stage_finds[STAGE_ITS] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_ITS] += afl->fsrv.total_execs - orig_execs; - memcpy(buf, orig_buf, len); - return r; } diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 60c9684c..8423a3d1 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -102,6 +102,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->stats_update_freq = 1; afl->stats_avg_exec = 0; afl->skip_deterministic = 1; + afl->cmplog_lvl = 1; #ifndef NO_SPLICING afl->use_splicing = 1; #endif diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index bb2674f0..1e914ca6 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -77,13 +77,8 @@ static void at_exit() { } int kill_signal = SIGKILL; - /* AFL_KILL_SIGNAL should already be a valid int at this point */ - if (getenv("AFL_KILL_SIGNAL")) { - - kill_signal = atoi(getenv("AFL_KILL_SIGNAL")); - - } + if ((ptr = getenv("AFL_KILL_SIGNAL"))) { kill_signal = atoi(ptr); } if (pid1 > 0) { kill(pid1, kill_signal); } if (pid2 > 0) { kill(pid2, kill_signal); } @@ -103,13 +98,14 @@ static void usage(u8 *argv0, int more_help) { "Execution control settings:\n" " -p schedule - power schedules compute a seed's performance score:\n" - " -- see docs/power_schedules.md\n" + " fast(default), explore, exploit, seek, rare, mmopt, " + "coe, lin\n" + " quad -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " "or @@)\n" " -t msec - timeout for each run (auto-scaled, 50-%u ms)\n" - " -m megs - memory limit for child process (%u MB, 0 = no limit)\n" + " -m megs - memory limit for child process (%u MB, 0 = no limit " + "[default])\n" " -Q - use binary-only instrumentation (QEMU mode)\n" " -U - use unicorn-based instrumentation (Unicorn mode)\n" " -W - use qemu-based instrumentation with Wine (Wine " @@ -125,7 +121,9 @@ static void usage(u8 *argv0, int more_help) { " See docs/README.MOpt.md\n" " -c program - enable CmpLog by specifying a binary compiled for " "it.\n" - " if using QEMU, just use -c 0.\n\n" + " if using QEMU, just use -c 0.\n" + " -l cmplog_level - set the complexity/intensivity of CmpLog.\n" + " Values: 1 (default), 2 (intensive) and 3 (heavy)\n\n" "Fuzzing behavior settings:\n" " -Z - sequential queue selection instead of weighted " @@ -337,7 +335,6 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_DEBUG")) { debug = afl->debug = 1; } - // map_size = get_map_size(); afl_state_init(afl, map_size); afl->debug = debug; afl_fsrv_init(&afl->fsrv); @@ -358,7 +355,8 @@ int main(int argc, char **argv_orig, char **envp) { while ((opt = getopt( argc, argv, - "+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:Z")) > 0) { + "+b:B:c:CdDe:E:hi:I:f:F:l:L:m:M:nNo:p:P:RQs:S:t:T:UV:Wx:Z")) > + 0) { switch (opt) { @@ -787,6 +785,26 @@ int main(int argc, char **argv_orig, char **envp) { } break; + case 'l': { + + afl->cmplog_lvl = atoi(optarg); + if (afl->cmplog_lvl < 1 || afl->cmplog_lvl > CMPLOG_LVL_MAX) { + + FATAL( + "Bad complog level value, accepted values are 1 (default), 2 and " + "%u.", + CMPLOG_LVL_MAX); + + } + + if (afl->cmplog_lvl == CMPLOG_LVL_MAX) { + + afl->cmplog_max_filesize = MAX_FILE; + + } + + } break; + case 'L': { /* MOpt mode */ if (afl->limit_time_sig) { FATAL("Multiple -L options not supported"); } @@ -1635,6 +1653,14 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->use_splicing) { ++afl->cycles_wo_finds; + + if (unlikely(afl->shm.cmplog_mode && + afl->cmplog_max_filesize < MAX_FILE)) { + + afl->cmplog_max_filesize <<= 4; + + } + switch (afl->expand_havoc) { case 0: @@ -1652,6 +1678,7 @@ int main(int argc, char **argv_orig, char **envp) { } afl->expand_havoc = 2; + if (afl->cmplog_lvl < 2) afl->cmplog_lvl = 2; break; case 2: // if (!have_p) afl->schedule = EXPLOIT; @@ -1665,11 +1692,14 @@ int main(int argc, char **argv_orig, char **envp) { afl->expand_havoc = 4; break; case 4: - // if not in sync mode, enable deterministic mode? - // if (!afl->sync_id) afl->skip_deterministic = 0; afl->expand_havoc = 5; + if (afl->cmplog_lvl < 3) afl->cmplog_lvl = 3; break; case 5: + // if not in sync mode, enable deterministic mode? + if (!afl->sync_id) afl->skip_deterministic = 0; + afl->expand_havoc = 6; + case 6: // nothing else currently break; -- cgit 1.4.1 From c71ce79963ffd3e1203d1078b8a60f91c4ecebf1 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 17 Jan 2021 15:18:20 +0100 Subject: fix colorization --- GNUmakefile | 4 +-- include/afl-fuzz.h | 4 +-- instrumentation/afl-compiler-rt.o.c | 3 +- src/afl-fuzz-one.c | 6 ++-- src/afl-fuzz-redqueen.c | 56 ++++++++++++++++++++++--------------- src/afl-fuzz.c | 2 ++ src/afl-showmap.c | 25 ++++++++++++++--- 7 files changed, 65 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/GNUmakefile b/GNUmakefile index 7b05a1d5..c71a7d47 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -428,8 +428,8 @@ src/afl-sharedmem.o : $(COMM_HDR) src/afl-sharedmem.c include/sharedmem.h afl-fuzz: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o | test_x86 $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(LDFLAGS) -lm -afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86 - $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(LDFLAGS) +afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86 + $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS) afl-tmin: src/afl-tmin.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86 $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS) diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 8a2122dc..621e8745 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1136,9 +1136,9 @@ void read_foreign_testcases(afl_state_t *, int); u8 common_fuzz_cmplog_stuff(afl_state_t *afl, u8 *out_buf, u32 len); /* RedQueen */ -u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, - u64 exec_cksum); +u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len); +/* our RNG wrapper */ AFL_RAND_RETURN rand_next(afl_state_t *afl); /* probability between 0.0 and 1.0 */ diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 5d75af78..bbec52f9 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1209,7 +1209,8 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2, uint8_t attr) { - // fprintf(stderr, "hook1 arg0=%02x arg1=%02x attr=%u\n", arg1, arg2, attr); + // fprintf(stderr, "hook1 arg0=%02x arg1=%02x attr=%u\n", + // (u8) arg1, (u8) arg2, attr); if (unlikely(!__afl_cmp_map)) return; diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 596bae22..4ce22c08 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -562,8 +562,7 @@ u8 fuzz_one_original(afl_state_t *afl) { !(afl->fsrv.total_execs % afl->queued_paths) || get_cur_time() - afl->last_path_time > 15000) { - if (input_to_state_stage(afl, in_buf, out_buf, len, - afl->queue_cur->exec_cksum)) { + if (input_to_state_stage(afl, in_buf, out_buf, len)) { goto abandon_entry; @@ -2986,8 +2985,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { !(afl->fsrv.total_execs % afl->queued_paths) || get_cur_time() - afl->last_path_time > 15000) { - if (input_to_state_stage(afl, in_buf, out_buf, len, - afl->queue_cur->exec_cksum)) { + if (input_to_state_stage(afl, in_buf, out_buf, len)) { goto abandon_entry; diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 955a9232..052f59f1 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -88,7 +88,7 @@ static struct range *pop_biggest_range(struct range **ranges) { static void dump(char *txt, u8 *buf, u32 len) { u32 i; - fprintf(stderr, "DUMP %s %llx ", txt, hash64(buf, len, 0)); + fprintf(stderr, "DUMP %s %llx ", txt, hash64(buf, len, HASH_CONST)); for (i = 0; i < len; i++) fprintf(stderr, "%02x", buf[i]); fprintf(stderr, "\n"); @@ -117,6 +117,7 @@ static u8 get_exec_checksum(afl_state_t *afl, u8 *buf, u32 len, u64 *cksum) { if (unlikely(common_fuzz_stuff(afl, buf, len))) { return 1; } *cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); + return 0; } @@ -200,7 +201,7 @@ static void type_replace(afl_state_t *afl, u8 *buf, u32 len) { } -static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum, +static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, struct tainted **taints) { struct range * ranges = add_range(NULL, 0, len - 1), *rng; @@ -208,18 +209,31 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum, u8 * backup = ck_alloc_nozero(len); u8 * changed = ck_alloc_nozero(len); - u64 orig_hit_cnt, new_hit_cnt; + u64 orig_hit_cnt, new_hit_cnt, exec_cksum; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; afl->stage_name = "colorization"; afl->stage_short = "colorization"; afl->stage_max = (len << 1); - afl->stage_cur = 0; + + // in colorization we do not classify counts, hence we have to calculate + // the original checksum! + if (unlikely(get_exec_checksum(afl, buf, len, &exec_cksum))) { + + goto checksum_fail; + + } + memcpy(backup, buf, len); memcpy(changed, buf, len); type_replace(afl, changed, len); +#ifdef _DEBUG + dump("ORIG", buf, len); + dump("CHAN", changed, len); +#endif + while ((rng = pop_biggest_range(&ranges)) != NULL && afl->stage_cur < afl->stage_max) { @@ -227,7 +241,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum, memcpy(buf + rng->start, changed + rng->start, s); - u64 cksum; + u64 cksum = 0; u64 start_us = get_cur_time_us(); if (unlikely(get_exec_checksum(afl, buf, len, &cksum))) { @@ -633,11 +647,11 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, if (SHAPE_BYTES(h->shape) >= 4 && *status != 1) { // if (its_len >= 4 && (attr <= 1 || attr >= 8)) - // fprintf(stderr, - // "TestU32: %u>=4 %x==%llx" - // " %x==%llx (idx=%u attr=%u) <= %llx<-%llx\n", - // its_len, *buf_32, pattern, *o_buf_32, o_pattern, idx, attr, - // repl, changed_val); + // fprintf(stderr, + // "TestU32: %u>=4 %x==%llx" + // " %x==%llx (idx=%u attr=%u) <= %llx<-%llx\n", + // its_len, *buf_32, pattern, *o_buf_32, o_pattern, idx, attr, + // repl, changed_val); if (its_len >= 4 && ((*buf_32 == (u32)pattern && *o_buf_32 == (u32)o_pattern) || @@ -702,10 +716,10 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, if (*status != 1) { // u8 // if (its_len >= 1 && (attr <= 1 || attr >= 8)) - // fprintf(stderr, - // "TestU8: %u>=1 %x==%x %x==%x (idx=%u attr=%u) <= %x<-%x\n", - // its_len, *buf_8, pattern, *o_buf_8, o_pattern, idx, attr, - // repl, changed_val); + // fprintf(stderr, + // "TestU8: %u>=1 %x==%x %x==%x (idx=%u attr=%u) <= %x<-%x\n", + // its_len, *buf_8, (u8)pattern, *o_buf_8, (u8)o_pattern, idx, + // attr, (u8)repl, (u8)changed_val); if (its_len >= 1 && ((*buf_8 == (u8)pattern && *o_buf_8 == (u8)o_pattern) || @@ -1659,8 +1673,7 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, ///// Input to State stage // afl->queue_cur->exec_cksum -u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, - u64 exec_cksum) { +u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { u8 r = 1; if (unlikely(!afl->orig_cmp_map)) { @@ -1686,7 +1699,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, if (!afl->queue_cur->taint || !afl->queue_cur->cmplog_colorinput) { - if (unlikely(colorization(afl, buf, len, exec_cksum, &taint))) { return 1; } + if (unlikely(colorization(afl, buf, len, &taint))) { return 1; } // no taint? still try, create a dummy to prevent again colorization if (!taint) { @@ -1696,6 +1709,10 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, } +#ifdef _DEBUG + dump("NEW ", buf, len); +#endif + } else { buf = afl->queue_cur->cmplog_colorinput; @@ -1705,11 +1722,6 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, } -#ifdef _DEBUG - dump("ORIG", orig_buf, len); - dump("NEW ", buf, len); -#endif - struct tainted *t = taint; while (t) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 1e914ca6..88c40ee8 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1094,6 +1094,8 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->shm.cmplog_mode) { OKF("CmpLog level: %u", afl->cmplog_lvl); } + /* Dynamically allocate memory for AFLFast schedules */ if (afl->schedule >= FAST && afl->schedule <= RARE) { diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 5c9d38e0..5d98d646 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -42,6 +42,7 @@ #include "sharedmem.h" #include "forkserver.h" #include "common.h" +#include "hash.h" #include #include @@ -86,7 +87,8 @@ static u8 quiet_mode, /* Hide non-essential messages? */ binary_mode, /* Write output as a binary map */ keep_cores, /* Allow coredumps? */ remove_shm = 1, /* remove shmem? */ - collect_coverage; /* collect coverage */ + collect_coverage, /* collect coverage */ + no_classify; /* do not classify counts */ static volatile u8 stop_soon, /* Ctrl-C pressed? */ child_crashed; /* Child crashed? */ @@ -317,7 +319,9 @@ static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem, } - classify_counts(fsrv); + if (fsrv->trace_bits[0] == 1) { fsrv->trace_bits[0] = 0; } + + if (!no_classify) { classify_counts(fsrv); } if (!quiet_mode) { SAYF(cRST "-- Program output ends --\n"); } @@ -490,7 +494,9 @@ static void showmap_run_target(afl_forkserver_t *fsrv, char **argv) { } - classify_counts(fsrv); + if (fsrv->trace_bits[0] == 1) { fsrv->trace_bits[0] = 0; } + + if (!no_classify) { classify_counts(fsrv); } if (!quiet_mode) { SAYF(cRST "-- Program output ends --\n"); } @@ -680,6 +686,7 @@ static void usage(u8 *argv0) { " -q - sink program's output and don't show messages\n" " -e - show edge coverage only, ignore hit counts\n" " -r - show real tuple values instead of AFL filter values\n" + " -s - do not classify the map\n" " -c - allow core dumps\n\n" "This tool displays raw tuple data captured by AFL instrumentation.\n" @@ -729,10 +736,14 @@ int main(int argc, char **argv_orig, char **envp) { if (getenv("AFL_QUIET") != NULL) { be_quiet = 1; } - while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqCZQUWbcrh")) > 0) { + while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqCZQUWbcrsh")) > 0) { switch (opt) { + case 's': + no_classify = 1; + break; + case 'C': collect_coverage = 1; quiet_mode = 1; @@ -1213,6 +1224,12 @@ int main(int argc, char **argv_orig, char **envp) { showmap_run_target(fsrv, use_argv); tcnt = write_results_to_file(fsrv, out_file); + if (!quiet_mode) { + + OKF("Hash of coverage map: %llx", + hash64(fsrv->trace_bits, fsrv->map_size, HASH_CONST)); + + } } -- cgit 1.4.1 From c8c0983ab84f4f7acf2dd52937a3cf5e41157a6b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 17 Jan 2021 15:51:38 +0100 Subject: make combined test a define option --- src/afl-fuzz-redqueen.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 052f59f1..daa08f6a 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -29,6 +29,7 @@ #include "cmplog.h" //#define _DEBUG +//#define COMBINE ///// Colorization @@ -621,7 +622,9 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u64 tmp_64 = *buf_64; *buf_64 = repl; if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } +#ifdef COMBINE if (*status == 1) { memcpy(cbuf + idx, buf_64, 8); } +#endif *buf_64 = tmp_64; // fprintf(stderr, "Status=%u\n", *status); @@ -660,7 +663,9 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u32 tmp_32 = *buf_32; *buf_32 = (u32)repl; if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } +#ifdef COMBINE if (*status == 1) { memcpy(cbuf + idx, buf_32, 4); } +#endif *buf_32 = tmp_32; // fprintf(stderr, "Status=%u\n", *status); @@ -692,7 +697,9 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u16 tmp_16 = *buf_16; *buf_16 = (u16)repl; if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } +#ifdef COMBINE if (*status == 1) { memcpy(cbuf + idx, buf_16, 2); } +#endif *buf_16 = tmp_16; } @@ -728,7 +735,9 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u8 tmp_8 = *buf_8; *buf_8 = (u8)repl; if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } +#ifdef COMBINE if (*status == 1) { cbuf[idx] = *buf_8; } +#endif *buf_8 = tmp_8; } @@ -920,7 +929,9 @@ static u8 cmp_extend_encoding128(afl_state_t *afl, struct cmp_header *h, *buf0 = v11; #endif if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } +#ifdef COMBINE if (*status == 1) { memcpy(cbuf + idx, buf_128, 16); } +#endif *buf_128 = tmp_128; #ifdef _DEBUG @@ -996,7 +1007,9 @@ static u8 cmp_extend_encoding_ld(afl_state_t *afl, struct cmp_header *h, memcpy(backup, buf_ld, 10); memcpy(buf_ld, repl, 10); if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } +#ifdef COMBINE if (*status == 1) { memcpy(cbuf + idx, repl, 10); } +#endif memcpy(buf_ld, backup, 10); #ifdef _DEBUG @@ -1506,6 +1519,10 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, u8 *status) { +#ifndef COMBINE + (void)(cbuf); +#endif + u32 i; u32 its_len = MIN((u32)32, len - idx); its_len = MIN(its_len, taint_len); @@ -1525,7 +1542,9 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } +#ifdef COMBINE if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); } +#endif } @@ -1763,10 +1782,14 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { if (cmplog_lvl >= 2 && cmplog_done < 2) { lvl += 2; } if (cmplog_lvl >= 3 && cmplog_done < 3) { lvl += 4; } +#ifdef COMBINE u8 *cbuf = afl_realloc((void **)&afl->in_scratch_buf, len + 128); memcpy(cbuf, orig_buf, len); u8 *virgin_backup = afl_realloc((void **)&afl->ex_buf, afl->shm.map_size); memcpy(virgin_backup, afl->virgin_bits, afl->shm.map_size); +#else + u8 *cbuf = NULL; +#endif u32 k; for (k = 0; k < CMP_MAP_W; ++k) { @@ -1855,6 +1878,7 @@ exit_its: } +#ifdef COMBINE // copy the current virgin bits so we can recover the information u8 *virgin_save = afl_realloc((void **)&afl->eff_buf, afl->shm.map_size); memcpy(virgin_save, afl->virgin_bits, afl->shm.map_size); @@ -1865,7 +1889,7 @@ exit_its: its_fuzz(afl, cbuf, len, &status); // now combine with the saved virgin bits -#ifdef WORD_SIZE_64 + #ifdef WORD_SIZE_64 u64 *v = (u64 *)afl->virgin_bits; u64 *s = (u64 *)virgin_save; u32 i; @@ -1875,19 +1899,19 @@ exit_its: } -#else + #else u32 *v = (u64 *)afl->virgin_bits; u32 *s = (u64 *)virgin_save; - u32 i; + u32 i; for (i = 0; i < (afl->shm.map_size >> 2); i++) { v[i] &= s[i]; } -#endif + #endif -#ifdef _DEBUG + #ifdef _DEBUG dump("COMB", cbuf, len); if (status == 1) { @@ -1899,6 +1923,7 @@ exit_its: } + #endif #endif new_hit_cnt = afl->queued_paths + afl->unique_crashes; @@ -1909,3 +1934,7 @@ exit_its: } +#ifdef COMBINE + #undef COMBINE +#endif + -- cgit 1.4.1 From 6b375489ed5dced4d0f55c334382f418a12e825a Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 17 Jan 2021 16:50:10 +0100 Subject: better extint cmplog --- src/afl-fuzz-redqueen.c | 148 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 140 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index daa08f6a..28d34ea6 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -28,7 +28,7 @@ #include "afl-fuzz.h" #include "cmplog.h" -//#define _DEBUG +#define _DEBUG //#define COMBINE ///// Colorization @@ -233,6 +233,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, #ifdef _DEBUG dump("ORIG", buf, len); dump("CHAN", changed, len); + fprintf(stderr, "CKSUM %llx (%u)\n", exec_cksum, afl->fsrv.map_size); #endif while ((rng = pop_biggest_range(&ranges)) != NULL && @@ -722,11 +723,11 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, if (*status != 1) { // u8 - // if (its_len >= 1 && (attr <= 1 || attr >= 8)) - // fprintf(stderr, - // "TestU8: %u>=1 %x==%x %x==%x (idx=%u attr=%u) <= %x<-%x\n", - // its_len, *buf_8, (u8)pattern, *o_buf_8, (u8)o_pattern, idx, - // attr, (u8)repl, (u8)changed_val); + if (its_len >= 1 && (attr <= 1 || attr >= 8)) + fprintf(stderr, + "TestU8: %u>=1 %x==%x %x==%x (idx=%u attr=%u) <= %x<-%x\n", + its_len, *buf_8, (u8)pattern, *o_buf_8, (u8)o_pattern, idx, + attr, (u8)repl, (u8)changed_val); if (its_len >= 1 && ((*buf_8 == (u8)pattern && *o_buf_8 == (u8)o_pattern) || @@ -960,6 +961,93 @@ static u8 cmp_extend_encoding128(afl_state_t *afl, struct cmp_header *h, } +static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, + u128 pattern, u128 repl, u128 o_pattern, + u128 changed_val, u8 attr, u32 idx, + u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf, + u32 len, u8 do_reverse, u8 lvl, u8 *status) { + + u8 *ptr = (u8 *)&buf[idx]; + u8 *o_ptr = (u8 *)&orig_buf[idx]; + u32 its_len = MIN(len - idx, taint_len); + u32 shape = h->shape + 1; + u8 *p = (u8 *)&pattern; + u8 *o_p = (u8 *)&o_pattern; + u8 *r = (u8 *)&repl; + u8 *o_r = (u8 *)&changed_val; + u8 backup[16]; +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + size_t off = 0; +#else + size_t off = 16 - shape; +#endif + + if (its_len >= shape) { + +#ifdef _DEBUG + fprintf(stderr, "TestUN: %u>=%u (len=%u idx=%u attr=%u) (%u) ", its_len, + shape, len, idx, attr, do_reverse); + u32 i; + for (i = 0; i < shape; i++) + fprintf(stderr, "%02x", ptr[0]); + fprintf(stderr, "=="); + for (i = 0; i < shape; i++) + fprintf(stderr, "%02x", p[off + 0]); + fprintf(stderr, " "); + for (i = 0; i < shape; i++) + fprintf(stderr, "%02x", o_ptr[0]); + fprintf(stderr, "=="); + for (i = 0; i < shape; i++) + fprintf(stderr, "%02x", o_p[off + 0]); + fprintf(stderr, " <= "); + for (i = 0; i < shape; i++) + fprintf(stderr, "%02x", r[off + 0]); + fprintf(stderr, " ("); + for (i = 0; i < shape; i++) + fprintf(stderr, "%02x", o_r[off + 0]); + fprintf(stderr, ")\n"); +#endif + + if (!memcmp(ptr, p + off, shape) && !memcmp(o_ptr, o_p + off, shape)) { + + memcpy(backup, ptr, shape); + memcpy(ptr, r + off, shape); + + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + +#ifdef COMBINE + if (*status == 1) { memcpy(cbuf + idx, r, shape); } +#endif + + memcpy(ptr, backup, shape); + +#ifdef _DEBUG + fprintf(stderr, "Status=%u\n", *status); +#endif + + } + + // reverse encoding + if (do_reverse && *status != 1) { + + if (unlikely(cmp_extend_encoding128( + afl, h, SWAPN(pattern, (shape << 3)), SWAPN(repl, (shape << 3)), + SWAPN(o_pattern, (shape << 3)), SWAPN(changed_val, (shape << 3)), + attr, idx, taint_len, orig_buf, buf, cbuf, len, 0, lvl, + status))) { + + return 1; + + } + + } + + } + + return 0; + +} + // uh a pointer read from (long double*) reads 12 bytes, not 10 ... // so lets make this complicated. static u8 cmp_extend_encoding_ld(afl_state_t *afl, struct cmp_header *h, @@ -1365,9 +1453,53 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } - } + } else + + if (is_n) { // _ExtInt special case + + if (s128_v0 != orig_s128_v0 && orig_s128_v0 != orig_s128_v1) { + + if (unlikely(cmp_extend_encodingN( + afl, h, s128_v0, s128_v1, orig_s128_v0, orig_s128_v1, + h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, + lvl, &status))) { + + return 1; + + } + + } + + if (status == 1) { + + found_one = 1; + break; + + } + + if (s128_v1 != orig_s128_v1 && orig_s128_v1 != orig_s128_v0) { + + if (unlikely(cmp_extend_encodingN( + afl, h, s128_v1, s128_v0, orig_s128_v1, orig_s128_v0, + h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, + lvl, &status))) { + + return 1; + + } + + } + + if (status == 1) { + + found_one = 1; + break; + + } + + } else - if (is_128) { // u128 special case + if (is_128) { // u128 special case if (s128_v0 != orig_s128_v0 && orig_s128_v0 != orig_s128_v1) { -- cgit 1.4.1 From 8951f906230ee9b7b2c27d92281d9da81302c694 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 17 Jan 2021 17:03:08 +0100 Subject: no cmplog when no taint is found --- src/afl-fuzz-redqueen.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 28d34ea6..2cc578bb 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1855,8 +1855,11 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { // no taint? still try, create a dummy to prevent again colorization if (!taint) { - taint = ck_alloc(sizeof(struct tainted)); - taint->len = len; +#ifdef _DEBUG + fprintf(stderr, "TAINT FAILED\n"); +#endif + afl->queue_cur->colorized = CMPLOG_LVL_MAX; + return 0; } -- cgit 1.4.1 From 7b97410060f52b33f0c9894bb202690c453c4bcb Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 17 Jan 2021 23:47:04 +0100 Subject: cmplog introspection --- src/afl-fuzz-redqueen.c | 80 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 2cc578bb..56246d6e 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -29,7 +29,8 @@ #include "cmplog.h" #define _DEBUG -//#define COMBINE +#define COMBINE +#define CMPLOG_INTROSPECTION ///// Colorization @@ -210,6 +211,10 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u8 * backup = ck_alloc_nozero(len); u8 * changed = ck_alloc_nozero(len); +#if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) + u64 start_time = get_cur_time(); +#endif + u64 orig_hit_cnt, new_hit_cnt, exec_cksum; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; @@ -368,26 +373,30 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, new_hit_cnt = afl->queued_paths + afl->unique_crashes; -#ifdef _DEBUG - /* +#if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) + FILE *f = stderr; + if (afl->not_on_tty) { + char fn[4096]; snprintf(fn, sizeof(fn), "%s/introspection_color.txt", afl->out_dir); - FILE *f = fopen(fn, "a"); - if (f) { + f = fopen(fn, "a"); - */ - FILE *f = stderr; - fprintf(f, - "Colorization: fname=%s len=%u result=%u execs=%u found=%llu " - "taint=%u\n", - afl->queue_cur->fname, len, afl->queue_cur->colorized, afl->stage_cur, - new_hit_cnt - orig_hit_cnt, positions); -/* - fclose(f); + } + + if (f) { + + fprintf( + f, + "Colorization: fname=%s len=%u ms=%llu result=%u execs=%u found=%llu " + "taint=%u\n", + afl->queue_cur->fname, len, get_cur_time() - start_time, + afl->queue_cur->colorized, afl->stage_cur, new_hit_cnt - orig_hit_cnt, + positions); + + if (afl->not_on_tty) { fclose(f); } } -*/ #endif afl->stage_finds[STAGE_COLORIZATION] += new_hit_cnt - orig_hit_cnt; @@ -1863,6 +1872,15 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { } +#ifdef _DEBUG + else if (taint->pos == 0 && taint->len == len) { + + fprintf(stderr, "TAINT FULL\n"); + + } + +#endif + #ifdef _DEBUG dump("NEW ", buf, len); #endif @@ -1887,6 +1905,11 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { } +#if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) + u64 start_time = get_cur_time(); + u32 cmp_locations = 0; +#endif + // do it manually, forkserver clear only afl->fsrv.trace_bits memset(afl->shm.cmp_map->headers, 0, sizeof(afl->shm.cmp_map->headers)); @@ -1960,6 +1983,10 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { if (!afl->shm.cmp_map->headers[k].hits) { continue; } +#if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) + ++cmp_locations; +#endif + if (afl->shm.cmp_map->headers[k].type == CMP_TYPE_INS) { if (unlikely(cmp_fuzz(afl, k, orig_buf, buf, cbuf, len, lvl, taint))) { @@ -2065,6 +2092,29 @@ exit_its: afl->stage_finds[STAGE_ITS] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_ITS] += afl->fsrv.total_execs - orig_execs; +#if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) + FILE *f = stderr; + if (afl->not_on_tty) { + + char fn[4096]; + snprintf(fn, sizeof(fn), "%s/introspection_color.txt", afl->out_dir); + f = fopen(fn, "a"); + + } + + if (f) { + + fprintf(f, + "Cmplog: fname=%s len=%u ms=%llu result=%u finds=%llu entries=%u\n", + afl->queue_cur->fname, len, get_cur_time() - start_time, r, + new_hit_cnt - orig_hit_cnt, cmp_locations); + + if (afl->not_on_tty) { fclose(f); } + + } + +#endif + return r; } -- cgit 1.4.1 From a8b06291630d5e4b71ab1eb2fee856c1c576bf4b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 18 Jan 2021 09:26:16 +0100 Subject: introspection, favor extint over long double --- src/afl-cc.c | 3 +- src/afl-fuzz-redqueen.c | 202 +++++++++--------------------------------------- 2 files changed, 38 insertions(+), 167 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 02c9c7c5..d44f069a 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -683,6 +683,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (!strncmp(cur, "--afl", 5)) continue; if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue; if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; + if (!strncmp(cur, "-fno-unroll", 11)) continue; if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined")) continue; @@ -703,7 +704,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (!strcmp(cur, "-shared")) shared_linking = 1; if (!strncmp(cur, "-O", 2)) have_o = 1; - if (!strncmp(cur, "-f", 2) && strstr(cur, "unroll-loop")) have_unroll = 1; + if (!strncmp(cur, "-funroll-loop", 13)) have_unroll = 1; cc_params[cc_par_cnt++] = cur; diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 56246d6e..3cfe05bc 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -31,6 +31,7 @@ #define _DEBUG #define COMBINE #define CMPLOG_INTROSPECTION +#define ARITHMETIC_LESSER_GREATER ///// Colorization @@ -375,6 +376,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, #if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) FILE *f = stderr; + #ifndef _DEBUG if (afl->not_on_tty) { char fn[4096]; @@ -383,6 +385,8 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, } + #endif + if (f) { fprintf( @@ -393,7 +397,9 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, afl->queue_cur->colorized, afl->stage_cur, new_hit_cnt - orig_hit_cnt, positions); + #ifndef _DEBUG if (afl->not_on_tty) { fclose(f); } + #endif } @@ -758,11 +764,15 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // here we add and subract 1 from the value, but only if it is not an // == or != comparison - // Bits: 1 = Equal, 2 = Greater, 3 = Lesser, 4 = Float + // Bits: 1 = Equal, 2 = Greater, 4 = Lesser, 8 = Float + // 16 = modified float, 32 = modified integer (modified = wont match + // in original buffer) +#ifdef ARITHMETIC_LESSER_GREATER if (lvl < 4) { return 0; } - if (attr >= 8 && attr < 16) { // lesser/greater integer comparison + // lesser/greater FP comparison + if (!(attr & 1) && (attr & 6) && (attr >= 8 && attr < 16)) { u64 repl_new; if (SHAPE_BYTES(h->shape) == 4 && its_len >= 4) { @@ -836,11 +846,11 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, double *f = (double *)&repl; float g = (float)*f; repl_new = 0; -#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) memcpy((char *)&repl_new, (char *)&g, 4); -#else + #else memcpy(((char *)&repl_new) + 4, (char *)&g, 4); -#endif + #endif changed_val = repl_new; h->shape = 3; // modify shape @@ -850,7 +860,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { - h->shape = 7; + h->shape = 7; // recover shape return 1; } @@ -859,7 +869,9 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - } else if (attr > 1 && attr < 8) { // lesser/greater integer comparison + } else if (!(attr & 1) && (attr & 6) && attr < 8) { + + // lesser/greater integer comparison u64 repl_new; @@ -885,6 +897,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } +#endif /* ARITHMETIC_LESSER_GREATER */ + return 0; } @@ -1057,89 +1071,6 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, } -// uh a pointer read from (long double*) reads 12 bytes, not 10 ... -// so lets make this complicated. -static u8 cmp_extend_encoding_ld(afl_state_t *afl, struct cmp_header *h, - u8 *pattern, u8 *repl, u8 *o_pattern, - u8 *changed_val, u8 attr, u32 idx, - u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf, - u32 len, u8 do_reverse, u8 lvl, u8 *status) { - - u8 *buf_ld = &buf[idx], *o_buf_ld = &orig_buf[idx], backup[10]; - u32 its_len = MIN(len - idx, taint_len); - - if (its_len >= 10) { - -#ifdef _DEBUG - fprintf(stderr, "TestUld: %u>=10 (len=%u idx=%u attr=%u) (%u)\n", its_len, - len, idx, attr, do_reverse); - fprintf(stderr, "TestUld: "); - u32 i; - for (i = 0; i < 10; i++) - fprintf(stderr, "%02x", pattern[i]); - fprintf(stderr, "=="); - for (i = 0; i < 10; i++) - fprintf(stderr, "%02x", buf_ld[i]); - fprintf(stderr, " "); - for (i = 0; i < 10; i++) - fprintf(stderr, "%02x", o_pattern[i]); - fprintf(stderr, "=="); - for (i = 0; i < 10; i++) - fprintf(stderr, "%02x", o_buf_ld[i]); - fprintf(stderr, " <= "); - for (i = 0; i < 10; i++) - fprintf(stderr, "%02x", repl[i]); - fprintf(stderr, "=="); - for (i = 0; i < 10; i++) - fprintf(stderr, "%02x", changed_val[i]); - fprintf(stderr, "\n"); -#endif - - if (!memcmp(pattern, buf_ld, 10) && !memcmp(o_pattern, o_buf_ld, 10)) { - - // if this is an fcmp (attr & 8 == 8) then do not compare the patterns - - // due to a bug in llvm dynamic float bitcasts do not work :( - // the value 16 means this is a +- 1.0 test case - - memcpy(backup, buf_ld, 10); - memcpy(buf_ld, repl, 10); - if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } -#ifdef COMBINE - if (*status == 1) { memcpy(cbuf + idx, repl, 10); } -#endif - memcpy(buf_ld, backup, 10); - -#ifdef _DEBUG - fprintf(stderr, "Status=%u\n", *status); -#endif - - } - - } - - // reverse encoding - if (do_reverse && *status != 1) { - - u8 sp[10], sr[10], osp[10], osr[10]; - SWAPNN(sp, pattern, 10); - SWAPNN(sr, repl, 10); - SWAPNN(osp, o_pattern, 10); - SWAPNN(osr, changed_val, 10); - - if (unlikely(cmp_extend_encoding_ld(afl, h, sp, sr, osp, osr, attr, idx, - taint_len, orig_buf, buf, cbuf, len, 0, - lvl, status))) { - - return 1; - - } - - } - - return 0; - -} - static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { u8 *b = (u8 *)&v; @@ -1273,7 +1204,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, struct cmp_header *h = &afl->shm.cmp_map->headers[key]; struct tainted * t; u32 i, j, idx, taint_len; - u32 have_taint = 1, is_128 = 0, is_n = 0, is_ld = 0; + u32 have_taint = 1, is_128 = 0, is_n = 0; u32 loggeds = h->hits; if (h->hits > CMP_MAP_H) { loggeds = CMP_MAP_H; } @@ -1281,12 +1212,11 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, u8 found_one = 0; /* loop cmps are useless, detect and ignore them */ - u128 s128_v0 = 0, s128_v1 = 0, orig_s128_v0 = 0, orig_s128_v1 = 0; - long double ld0, ld1, o_ld0, o_ld1; - u64 s_v0, s_v1; - u8 s_v0_fixed = 1, s_v1_fixed = 1; - u8 s_v0_inc = 1, s_v1_inc = 1; - u8 s_v0_dec = 1, s_v1_dec = 1; + u128 s128_v0 = 0, s128_v1 = 0, orig_s128_v0 = 0, orig_s128_v1 = 0; + u64 s_v0, s_v1; + u8 s_v0_fixed = 1, s_v1_fixed = 1; + u8 s_v0_inc = 1, s_v1_inc = 1; + u8 s_v0_dec = 1, s_v1_dec = 1; switch (SHAPE_BYTES(h->shape)) { @@ -1298,9 +1228,6 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, case 16: is_128 = 1; break; - case 10: - if (h->attribute & 8) { is_ld = 1; } - // fall through default: is_n = 1; @@ -1376,24 +1303,6 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, orig_s128_v0 = ((u128)orig_o->v0) + (((u128)orig_o->v0_128) << 64); orig_s128_v1 = ((u128)orig_o->v1) + (((u128)orig_o->v1_128) << 64); - if (is_ld) { - -#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) - memcpy((char *)&ld0, (char *)&s128_v0, sizeof(long double)); - memcpy((char *)&ld1, (char *)&s128_v1, sizeof(long double)); - memcpy((char *)&o_ld0, (char *)&orig_s128_v0, sizeof(long double)); - memcpy((char *)&o_ld1, (char *)&orig_s128_v1, sizeof(long double)); -#else - memcpy((char *)&ld0, (char *)(&s128_v0) + 6, sizeof(long double)); - memcpy((char *)&ld1, (char *)(&s128_v1) + 6, sizeof(long double)); - memcpy((char *)&o_ld0, (char *)(&orig_s128_v0) + 6, - sizeof(long double)); - memcpy((char *)&o_ld1, (char *)(&orig_s128_v1) + 6, - sizeof(long double)); -#endif - - } - } for (idx = 0; idx < len; ++idx) { @@ -1420,51 +1329,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, status = 0; - if (is_ld) { // long double special case - - if (ld0 != o_ld0 && o_ld1 != o_ld0) { - - if (unlikely(cmp_extend_encoding_ld( - afl, h, (u8 *)&ld0, (u8 *)&ld1, (u8 *)&o_ld0, (u8 *)&o_ld1, - h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, - lvl, &status))) { - - return 1; - - } - - } - - if (status == 1) { - - found_one = 1; - break; - - } - - if (ld1 != o_ld1 && o_ld0 != o_ld1) { - - if (unlikely(cmp_extend_encoding_ld( - afl, h, (u8 *)&ld1, (u8 *)&ld0, (u8 *)&o_ld1, (u8 *)&o_ld0, - h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, - lvl, &status))) { - - return 1; - - } - - } - - if (status == 1) { - - found_one = 1; - break; - - } - - } else - - if (is_n) { // _ExtInt special case + if (is_n) { // _ExtInt special case if (s128_v0 != orig_s128_v0 && orig_s128_v0 != orig_s128_v1) { @@ -1552,7 +1417,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } - // even for u128 and long double do cmp_extend_encoding() because + // even for u128 and _ExtInt we do cmp_extend_encoding() because // if we got here their own special trials failed and it might just be // a cast from e.g. u64 to u128 from the input data. @@ -1995,7 +1860,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { } - } else { + } else if (lvl & 1) { if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, cbuf, len, taint))) { @@ -2064,7 +1929,7 @@ exit_its: #else u32 *v = (u64 *)afl->virgin_bits; u32 *s = (u64 *)virgin_save; - u32 i; + u32 i; for (i = 0; i < (afl->shm.map_size >> 2); i++) { v[i] &= s[i]; @@ -2094,6 +1959,7 @@ exit_its: #if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) FILE *f = stderr; + #ifndef _DEBUG if (afl->not_on_tty) { char fn[4096]; @@ -2102,6 +1968,8 @@ exit_its: } + #endif + if (f) { fprintf(f, @@ -2109,7 +1977,9 @@ exit_its: afl->queue_cur->fname, len, get_cur_time() - start_time, r, new_hit_cnt - orig_hit_cnt, cmp_locations); + #ifndef _DEBUG if (afl->not_on_tty) { fclose(f); } + #endif } -- cgit 1.4.1 From bbfaa6092db55dc49f771a97ad7da7985c343531 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 18 Jan 2021 11:12:20 +0100 Subject: refactoring --- src/afl-fuzz-redqueen.c | 294 +++++++++--------------------------------------- 1 file changed, 51 insertions(+), 243 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 3cfe05bc..94667254 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -28,10 +28,10 @@ #include "afl-fuzz.h" #include "cmplog.h" -#define _DEBUG -#define COMBINE +//#define _DEBUG +//#define COMBINE #define CMPLOG_INTROSPECTION -#define ARITHMETIC_LESSER_GREATER +//#define ARITHMETIC_LESSER_GREATER ///// Colorization @@ -524,7 +524,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // not 100% but would have a chance to be detected // fprintf(stderr, - // "Encode: %llx->%llx into %llx(<-%llx) at pos=%u " + // "Encode: %llx->%llx into %llx(<-%llx) at idx=%u " // "taint_len=%u shape=%u attr=%u\n", // o_pattern, pattern, repl, changed_val, idx, taint_len, // h->shape + 1, attr); @@ -622,12 +622,12 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, if (SHAPE_BYTES(h->shape) >= 8 && *status != 1) { - // if (its_len >= 8 && (attr == 0 || attr >= 8)) - // fprintf(stderr, - // "TestU64: %u>=4 %x==%llx" - // " %x==%llx (idx=%u attr=%u) <= %llx<-%llx\n", - // its_len, *buf_32, pattern, *o_buf_32, o_pattern, idx, attr, - // repl, changed_val); + // if (its_len >= 8) + // fprintf(stderr, + // "TestU64: %u>=8 (idx=%u attr=%u) %llx==%llx" + // " %llx==%llx <= %llx<-%llx\n", + // its_len, idx, attr, *buf_64, pattern, *o_buf_64, o_pattern, + // repl, changed_val); // if this is an fcmp (attr & 8 == 8) then do not compare the patterns - // due to a bug in llvm dynamic float bitcasts do not work :( @@ -667,10 +667,10 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // if (its_len >= 4 && (attr <= 1 || attr >= 8)) // fprintf(stderr, - // "TestU32: %u>=4 %x==%llx" - // " %x==%llx (idx=%u attr=%u) <= %llx<-%llx\n", - // its_len, *buf_32, pattern, *o_buf_32, o_pattern, idx, attr, - // repl, changed_val); + // "TestU32: %u>=4 (idx=%u attr=%u) %x==%x" + // " %x==%x <= %x<-%x\n", + // its_len, idx, attr, *buf_32, (u32)pattern, *o_buf_32, + // (u32)o_pattern, (u32)repl, (u32)changed_val); if (its_len >= 4 && ((*buf_32 == (u32)pattern && *o_buf_32 == (u32)o_pattern) || @@ -738,11 +738,11 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, if (*status != 1) { // u8 - if (its_len >= 1 && (attr <= 1 || attr >= 8)) - fprintf(stderr, - "TestU8: %u>=1 %x==%x %x==%x (idx=%u attr=%u) <= %x<-%x\n", - its_len, *buf_8, (u8)pattern, *o_buf_8, (u8)o_pattern, idx, - attr, (u8)repl, (u8)changed_val); + // if (its_len >= 1 && (attr <= 1 || attr >= 8)) + // fprintf(stderr, + // "TestU8: %u>=1 (idx=%u attr=%u) %x==%x %x==%x <= %x<-%x\n", + // its_len, idx, attr, *buf_8, (u8)pattern, *o_buf_8, + // (u8)o_pattern, (u8)repl, (u8)changed_val); if (its_len >= 1 && ((*buf_8 == (u8)pattern && *o_buf_8 == (u8)o_pattern) || @@ -903,87 +903,6 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } -static u8 cmp_extend_encoding128(afl_state_t *afl, struct cmp_header *h, - u128 pattern, u128 repl, u128 o_pattern, - u128 changed_val, u8 attr, u32 idx, - u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf, - u32 len, u8 do_reverse, u8 lvl, u8 *status) { - - u128 *buf_128 = (u128 *)&buf[idx]; - u64 * buf0 = (u64 *)&buf[idx]; - u64 * buf1 = (u64 *)(buf + idx + 8); - u128 *o_buf_128 = (u128 *)&orig_buf[idx]; - u32 its_len = MIN(len - idx, taint_len); - u64 v10 = (u64)repl; - u64 v11 = (u64)(repl >> 64); - - // if this is an fcmp (attr & 8 == 8) then do not compare the patterns - - // due to a bug in llvm dynamic float bitcasts do not work :( - // the value 16 means this is a +- 1.0 test case - if (its_len >= 16) { - -#ifdef _DEBUG - fprintf(stderr, "TestU128: %u>=16 (idx=%u attr=%u) (%u)\n", its_len, idx, - attr, do_reverse); - u64 v00 = (u64)pattern; - u64 v01 = pattern >> 64; - u64 ov00 = (u64)o_pattern; - u64 ov01 = o_pattern >> 64; - u64 ov10 = (u64)changed_val; - u64 ov11 = changed_val >> 64; - u64 b00 = (u64)*buf_128; - u64 b01 = *buf_128 >> 64; - u64 ob00 = (u64)*o_buf_128; - u64 ob01 = *o_buf_128 >> 64; - fprintf(stderr, - "TestU128: %llx:%llx==%llx:%llx" - " %llx:%llx==%llx:%llx <= %llx:%llx<-%llx:%llx\n", - b01, b00, v01, v00, ob01, ob00, ov01, ov00, v11, v10, ov11, ov10); -#endif - - if (*buf_128 == pattern && *o_buf_128 == o_pattern) { - - u128 tmp_128 = *buf_128; - // *buf_128 = repl; <- this crashes -#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) - *buf0 = v10; - *buf1 = v11; -#else - *buf1 = v10; - *buf0 = v11; -#endif - if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } -#ifdef COMBINE - if (*status == 1) { memcpy(cbuf + idx, buf_128, 16); } -#endif - *buf_128 = tmp_128; - -#ifdef _DEBUG - fprintf(stderr, "Status=%u\n", *status); -#endif - - } - - // reverse encoding - if (do_reverse && *status != 1) { - - if (unlikely(cmp_extend_encoding128( - afl, h, SWAPN(pattern, 128), SWAPN(repl, 128), - SWAPN(o_pattern, 128), SWAPN(changed_val, 128), attr, idx, - taint_len, orig_buf, buf, cbuf, len, 0, lvl, status))) { - - return 1; - - } - - } - - } - - return 0; - -} - static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, u128 pattern, u128 repl, u128 o_pattern, u128 changed_val, u8 attr, u32 idx, @@ -992,13 +911,12 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, u8 *ptr = (u8 *)&buf[idx]; u8 *o_ptr = (u8 *)&orig_buf[idx]; - u32 its_len = MIN(len - idx, taint_len); - u32 shape = h->shape + 1; u8 *p = (u8 *)&pattern; u8 *o_p = (u8 *)&o_pattern; u8 *r = (u8 *)&repl; - u8 *o_r = (u8 *)&changed_val; u8 backup[16]; + u32 its_len = MIN(len - idx, taint_len); + u32 shape = h->shape + 1; #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) size_t off = 0; #else @@ -1008,27 +926,28 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, if (its_len >= shape) { #ifdef _DEBUG - fprintf(stderr, "TestUN: %u>=%u (len=%u idx=%u attr=%u) (%u) ", its_len, - shape, len, idx, attr, do_reverse); + // fprintf(stderr, "TestUN: %u>=%u (len=%u idx=%u attr=%u off=%lu) (%u) ", + // its_len, shape, len, idx, attr, off, do_reverse); u32 i; + u8 *o_r = (u8 *)&changed_val; for (i = 0; i < shape; i++) - fprintf(stderr, "%02x", ptr[0]); + fprintf(stderr, "%02x", ptr[i]); fprintf(stderr, "=="); for (i = 0; i < shape; i++) - fprintf(stderr, "%02x", p[off + 0]); + fprintf(stderr, "%02x", p[off + i]); fprintf(stderr, " "); for (i = 0; i < shape; i++) - fprintf(stderr, "%02x", o_ptr[0]); + fprintf(stderr, "%02x", o_ptr[i]); fprintf(stderr, "=="); for (i = 0; i < shape; i++) - fprintf(stderr, "%02x", o_p[off + 0]); + fprintf(stderr, "%02x", o_p[off + i]); fprintf(stderr, " <= "); for (i = 0; i < shape; i++) - fprintf(stderr, "%02x", r[off + 0]); - fprintf(stderr, " ("); + fprintf(stderr, "%02x", r[off + i]); + fprintf(stderr, "<-"); for (i = 0; i < shape; i++) - fprintf(stderr, "%02x", o_r[off + 0]); - fprintf(stderr, ")\n"); + fprintf(stderr, "%02x", o_r[off + i]); + fprintf(stderr, "\n"); #endif if (!memcmp(ptr, p + off, shape) && !memcmp(o_ptr, o_p + off, shape)) { @@ -1053,7 +972,7 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, // reverse encoding if (do_reverse && *status != 1) { - if (unlikely(cmp_extend_encoding128( + if (unlikely(cmp_extend_encodingN( afl, h, SWAPN(pattern, (shape << 3)), SWAPN(repl, (shape << 3)), SWAPN(o_pattern, (shape << 3)), SWAPN(changed_val, (shape << 3)), attr, idx, taint_len, orig_buf, buf, cbuf, len, 0, lvl, @@ -1121,48 +1040,6 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { } -static void try_to_add_to_dict128(afl_state_t *afl, u128 v) { - - u8 *b = (u8 *)&v; - - u32 k; - u8 cons_ff = 0, cons_0 = 0; - for (k = 0; k < 16; ++k) { - - if (b[k] == 0) { - - ++cons_0; - - } else if (b[k] == 0xff) { - - ++cons_0; - - } else { - - cons_0 = cons_ff = 0; - - } - - // too many uninteresting values? try adding 2 64-bit values - if (cons_0 > 6 || cons_ff > 6) { - - u64 v64 = (u64)v; - try_to_add_to_dict(afl, v64, 8); - v64 = (u64)(v >> 64); - try_to_add_to_dict(afl, v64, 8); - - return; - - } - - } - - maybe_add_auto(afl, (u8 *)&v, 16); - u128 rev = SWAPN(v, 128); - maybe_add_auto(afl, (u8 *)&rev, 16); - -} - static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) { u8 *b = (u8 *)&v; @@ -1170,9 +1047,11 @@ static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) { u32 k; u8 cons_ff = 0, cons_0 = 0; #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + u32 off = 0; for (k = 0; k < size; ++k) { #else + u32 off = 16 - size; for (k = 16 - size; k < 16; ++k) { #endif @@ -1192,9 +1071,9 @@ static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) { } - maybe_add_auto(afl, (u8 *)&v, size); + maybe_add_auto(afl, (u8 *)&v + off, size); u128 rev = SWAPN(v, size); - maybe_add_auto(afl, (u8 *)&rev, size); + maybe_add_auto(afl, (u8 *)&rev + off, size); } @@ -1204,7 +1083,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, struct cmp_header *h = &afl->shm.cmp_map->headers[key]; struct tainted * t; u32 i, j, idx, taint_len; - u32 have_taint = 1, is_128 = 0, is_n = 0; + u32 have_taint = 1, is_n = 0; u32 loggeds = h->hits; if (h->hits > CMP_MAP_H) { loggeds = CMP_MAP_H; } @@ -1225,15 +1104,12 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, case 4: case 8: break; - case 16: - is_128 = 1; - break; default: is_n = 1; } - // FCmp not in if level 1 only + // FP handling only from lvl 2 onwards if ((h->attribute & 8) && lvl < 2) return 0; for (i = 0; i < loggeds; ++i) { @@ -1279,24 +1155,14 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, SHAPE_BYTES(h->shape)); #endif - if (taint) { - - t = taint; - - while (t->next) { - - t = t->next; - - } - - } else { + t = taint; + while (t->next) { - have_taint = 0; - t = NULL; + t = t->next; } - if (unlikely(is_128 || is_n)) { + if (unlikely(is_n)) { s128_v0 = ((u128)o->v0) + (((u128)o->v0_128) << 64); s128_v1 = ((u128)o->v1) + (((u128)o->v1_128) << 64); @@ -1329,7 +1195,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, status = 0; - if (is_n) { // _ExtInt special case + if (is_n) { // _ExtInt special case including u128 if (s128_v0 != orig_s128_v0 && orig_s128_v0 != orig_s128_v1) { @@ -1371,50 +1237,6 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } - } else - - if (is_128) { // u128 special case - - if (s128_v0 != orig_s128_v0 && orig_s128_v0 != orig_s128_v1) { - - if (unlikely(cmp_extend_encoding128( - afl, h, s128_v0, s128_v1, orig_s128_v0, orig_s128_v1, - h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, - lvl, &status))) { - - return 1; - - } - - } - - if (status == 1) { - - found_one = 1; - break; - - } - - if (s128_v1 != orig_s128_v1 && orig_s128_v1 != orig_s128_v0) { - - if (unlikely(cmp_extend_encoding128( - afl, h, s128_v1, s128_v0, orig_s128_v1, orig_s128_v0, - h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, - lvl, &status))) { - - return 1; - - } - - } - - if (status == 1) { - - found_one = 1; - break; - - } - } // even for u128 and _ExtInt we do cmp_extend_encoding() because @@ -1464,10 +1286,10 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, #ifdef _DEBUG fprintf(stderr, - "END: %llx->%llx vs %llx->%llx attr=%u i=%u found=%u is128=%u " + "END: %llx->%llx vs %llx->%llx attr=%u i=%u found=%u " "isN=%u size=%u\n", orig_o->v0, o->v0, orig_o->v1, o->v1, h->attribute, i, found_one, - is_128, is_n, SHAPE_BYTES(h->shape)); + is_n, SHAPE_BYTES(h->shape)); #endif // If failed, add to dictionary @@ -1475,12 +1297,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, if (afl->pass_stats[key].total == 0) { - if (unlikely(is_128)) { - - try_to_add_to_dict128(afl, s128_v0); - try_to_add_to_dict128(afl, s128_v1); - - } else if (unlikely(is_n)) { + if (unlikely(is_n)) { try_to_add_to_dictN(afl, s128_v0, SHAPE_BYTES(h->shape)); try_to_add_to_dictN(afl, s128_v1, SHAPE_BYTES(h->shape)); @@ -1592,19 +1409,10 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } - if (taint) { - - t = taint; - while (t->next) { - - t = t->next; - - } - - } else { + t = taint; + while (t->next) { - have_taint = 0; - t = NULL; + t = t->next; } @@ -1764,7 +1572,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { while (t) { #ifdef _DEBUG - fprintf(stderr, "T: pos=%u len=%u\n", t->pos, t->len); + fprintf(stderr, "T: idx=%u len=%u\n", t->pos, t->len); #endif t = t->next; -- cgit 1.4.1 From 0c061186cf873011ca2636a307c9d6dc85ca6880 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 18 Jan 2021 12:13:36 +0100 Subject: less logging --- instrumentation/afl-compiler-rt.o.c | 12 ++++++------ src/afl-fuzz-redqueen.c | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index bbec52f9..edb635ae 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1212,7 +1212,7 @@ void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2, uint8_t attr) { // fprintf(stderr, "hook1 arg0=%02x arg1=%02x attr=%u\n", // (u8) arg1, (u8) arg2, attr); - if (unlikely(!__afl_cmp_map)) return; + if (unlikely(!__afl_cmp_map || arg1 == arg2)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -1236,7 +1236,7 @@ void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2, uint8_t attr) { void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2, uint8_t attr) { - if (unlikely(!__afl_cmp_map)) return; + if (unlikely(!__afl_cmp_map || arg1 == arg2)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -1260,7 +1260,7 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2, uint8_t attr) { // fprintf(stderr, "hook4 arg0=%x arg1=%x attr=%u\n", arg1, arg2, attr); - if (unlikely(!__afl_cmp_map)) return; + if (unlikely(!__afl_cmp_map || arg1 == arg2)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -1282,9 +1282,9 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2, uint8_t attr) { void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2, uint8_t attr) { - // fprintf(stderr, "hook8 arg0=%lx arg1=%lx attr=%u\n", arg1, arg2, attr); + fprintf(stderr, "hook8 arg0=%lx arg1=%lx attr=%u\n", arg1, arg2, attr); - if (unlikely(!__afl_cmp_map)) return; + if (unlikely(!__afl_cmp_map || arg1 == arg2)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -1312,7 +1312,7 @@ void __cmplog_ins_hookN(uint128_t arg1, uint128_t arg2, uint8_t attr, // (u64)(arg1 >> 64), (u64)arg1, (u64)(arg2 >> 64), (u64)arg2, size + 1, // attr); - if (unlikely(!__afl_cmp_map)) return; + if (unlikely(!__afl_cmp_map || arg1 == arg2)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 94667254..0c12657d 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -380,7 +380,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, if (afl->not_on_tty) { char fn[4096]; - snprintf(fn, sizeof(fn), "%s/introspection_color.txt", afl->out_dir); + snprintf(fn, sizeof(fn), "%s/introspection_cmplog.txt", afl->out_dir); f = fopen(fn, "a"); } @@ -1771,7 +1771,7 @@ exit_its: if (afl->not_on_tty) { char fn[4096]; - snprintf(fn, sizeof(fn), "%s/introspection_color.txt", afl->out_dir); + snprintf(fn, sizeof(fn), "%s/introspection_cmplog.txt", afl->out_dir); f = fopen(fn, "a"); } -- cgit 1.4.1 From 94a15b8ca790a87d88c7513282250257f32a48c0 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 18 Jan 2021 13:20:40 +0100 Subject: arithmetic also for <= >= --- src/afl-fuzz-redqueen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 0c12657d..fe98f031 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -772,7 +772,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, if (lvl < 4) { return 0; } // lesser/greater FP comparison - if (!(attr & 1) && (attr & 6) && (attr >= 8 && attr < 16)) { + if ((attr & 6) && (attr >= 8 && attr < 16)) { u64 repl_new; if (SHAPE_BYTES(h->shape) == 4 && its_len >= 4) { @@ -869,7 +869,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - } else if (!(attr & 1) && (attr & 6) && attr < 8) { + } else if ((attr & 6) && attr < 8) { // lesser/greater integer comparison -- cgit 1.4.1 From 0b545aaeb45141e91273f2358ec457293c341c92 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 18 Jan 2021 20:18:18 +0100 Subject: use enums, support cmplog map collisions --- include/cmplog.h | 8 +- include/config.h | 2 - include/types.h | 47 ++++--- instrumentation/afl-compiler-rt.o.c | 201 ++++++++++++++++++++++------ instrumentation/cmplog-instructions-pass.cc | 4 + instrumentation/llvm-ngram-coverage.h | 2 +- src/afl-fuzz-redqueen.c | 129 +++++++++++------- 7 files changed, 276 insertions(+), 117 deletions(-) (limited to 'src') diff --git a/include/cmplog.h b/include/cmplog.h index 6392c503..878ed60c 100644 --- a/include/cmplog.h +++ b/include/cmplog.h @@ -38,17 +38,17 @@ #define SHAPE_BYTES(x) (x + 1) -#define CMP_TYPE_INS 0 -#define CMP_TYPE_RTN 1 +#define CMP_TYPE_INS 1 +#define CMP_TYPE_RTN 2 struct cmp_header { unsigned hits : 24; unsigned id : 24; unsigned shape : 5; - unsigned type : 1; + unsigned type : 2; unsigned attribute : 4; - unsigned reserved : 6; + unsigned reserved : 5; } __attribute__((packed)); diff --git a/include/config.h b/include/config.h index c0cd0ef1..c9c4a677 100644 --- a/include/config.h +++ b/include/config.h @@ -23,8 +23,6 @@ #ifndef _HAVE_CONFIG_H #define _HAVE_CONFIG_H -#include "types.h" - /* Version string: */ // c = release, d = volatile github dev, e = experimental branch diff --git a/include/types.h b/include/types.h index d5c31597..7b94fb83 100644 --- a/include/types.h +++ b/include/types.h @@ -25,12 +25,15 @@ #include #include +#include "config.h" -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +#ifdef WORD_SIZE_64 typedef unsigned __int128 uint128_t; typedef uint128_t u128; +#endif /* Extended forkserver option values */ @@ -59,12 +62,14 @@ typedef uint128_t u128; typedef unsigned long long u64; -typedef int8_t s8; -typedef int16_t s16; -typedef int32_t s32; -typedef int64_t s64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; +#ifdef WORD_SIZE_64 typedef __int128 int128_t; typedef int128_t s128; +#endif #ifndef MIN #define MIN(a, b) \ @@ -119,19 +124,21 @@ typedef int128_t s128; }) // It is impossible to define 128 bit constants, so ... -#define SWAPN(_x, _l) \ - ({ \ - \ - u128 _res = (_x), _ret; \ - char *d = (char *)&_ret, *s = (char *)&_res; \ - int i; \ - for (i = 0; i < 16; i++) \ - d[15 - i] = s[i]; \ - u32 sr = 128U - ((_l) << 3U); \ - (_ret >>= sr); \ - (u128) _ret; \ - \ - }) +#ifdef WORD_SIZE_64 + #define SWAPN(_x, _l) \ + ({ \ + \ + u128 _res = (_x), _ret; \ + char *d = (char *)&_ret, *s = (char *)&_res; \ + int i; \ + for (i = 0; i < 16; i++) \ + d[15 - i] = s[i]; \ + u32 sr = 128U - ((_l) << 3U); \ + (_ret >>= sr); \ + (u128) _ret; \ + \ + }) +#endif #define SWAPNN(_x, _y, _l) \ ({ \ diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index edb635ae..0ce96673 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1218,15 +1218,22 @@ void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2, uint8_t attr) { k = (k >> 4) ^ (k << 8); k &= CMP_MAP_W - 1; - __afl_cmp_map->headers[k].type = CMP_TYPE_INS; - __afl_cmp_map->headers[k].attribute = attr; + u32 hits; + + if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS) { - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; - // if (!__afl_cmp_map->headers[k].cnt) - // __afl_cmp_map->headers[k].cnt = __afl_cmp_counter++; + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + hits = 0; + __afl_cmp_map->headers[k].hits = 1; + __afl_cmp_map->headers[k].shape = 0; - __afl_cmp_map->headers[k].shape = 0; + } else { + + hits = __afl_cmp_map->headers[k].hits++; + + } + + __afl_cmp_map->headers[k].attribute = attr; hits &= CMP_MAP_H - 1; __afl_cmp_map->log[k][hits].v0 = arg1; @@ -1242,13 +1249,28 @@ void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2, uint8_t attr) { k = (k >> 4) ^ (k << 8); k &= CMP_MAP_W - 1; - __afl_cmp_map->headers[k].type = CMP_TYPE_INS; - __afl_cmp_map->headers[k].attribute = attr; + u32 hits; - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; + if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS) { - __afl_cmp_map->headers[k].shape = 1; + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + hits = 0; + __afl_cmp_map->headers[k].hits = 1; + __afl_cmp_map->headers[k].shape = 1; + + } else { + + hits = __afl_cmp_map->headers[k].hits++; + + if (!__afl_cmp_map->headers[k].shape) { + + __afl_cmp_map->headers[k].shape = 1; + + } + + } + + __afl_cmp_map->headers[k].attribute = attr; hits &= CMP_MAP_H - 1; __afl_cmp_map->log[k][hits].v0 = arg1; @@ -1266,13 +1288,28 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2, uint8_t attr) { k = (k >> 4) ^ (k << 8); k &= CMP_MAP_W - 1; - __afl_cmp_map->headers[k].type = CMP_TYPE_INS; - __afl_cmp_map->headers[k].attribute = attr; + u32 hits; + + if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS) { + + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + hits = 0; + __afl_cmp_map->headers[k].hits = 1; + __afl_cmp_map->headers[k].shape = 3; + + } else { + + hits = __afl_cmp_map->headers[k].hits++; - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; + if (__afl_cmp_map->headers[k].shape < 3) { - __afl_cmp_map->headers[k].shape = 3; + __afl_cmp_map->headers[k].shape = 3; + + } + + } + + __afl_cmp_map->headers[k].attribute = attr; hits &= CMP_MAP_H - 1; __afl_cmp_map->log[k][hits].v0 = arg1; @@ -1282,7 +1319,7 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2, uint8_t attr) { void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2, uint8_t attr) { - fprintf(stderr, "hook8 arg0=%lx arg1=%lx attr=%u\n", arg1, arg2, attr); + // fprintf(stderr, "hook8 arg0=%lx arg1=%lx attr=%u\n", arg1, arg2, attr); if (unlikely(!__afl_cmp_map || arg1 == arg2)) return; @@ -1290,13 +1327,28 @@ void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2, uint8_t attr) { k = (k >> 4) ^ (k << 8); k &= CMP_MAP_W - 1; - __afl_cmp_map->headers[k].type = CMP_TYPE_INS; - __afl_cmp_map->headers[k].attribute = attr; + u32 hits; - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; + if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS) { - __afl_cmp_map->headers[k].shape = 7; + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + hits = 0; + __afl_cmp_map->headers[k].hits = 1; + __afl_cmp_map->headers[k].shape = 7; + + } else { + + hits = __afl_cmp_map->headers[k].hits++; + + if (__afl_cmp_map->headers[k].shape < 7) { + + __afl_cmp_map->headers[k].shape = 7; + + } + + } + + __afl_cmp_map->headers[k].attribute = attr; hits &= CMP_MAP_H - 1; __afl_cmp_map->log[k][hits].v0 = arg1; @@ -1304,6 +1356,7 @@ void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2, uint8_t attr) { } +#ifdef WORD_SIZE_64 // support for u24 to u120 via llvm _ExitInt(). size is in bytes minus 1 void __cmplog_ins_hookN(uint128_t arg1, uint128_t arg2, uint8_t attr, uint8_t size) { @@ -1318,13 +1371,28 @@ void __cmplog_ins_hookN(uint128_t arg1, uint128_t arg2, uint8_t attr, k = (k >> 4) ^ (k << 8); k &= CMP_MAP_W - 1; - __afl_cmp_map->headers[k].type = CMP_TYPE_INS; - __afl_cmp_map->headers[k].attribute = attr; + u32 hits; - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; + if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS) { - __afl_cmp_map->headers[k].shape = size; + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + hits = 0; + __afl_cmp_map->headers[k].hits = 1; + __afl_cmp_map->headers[k].shape = size; + + } else { + + hits = __afl_cmp_map->headers[k].hits++; + + if (__afl_cmp_map->headers[k].shape < size) { + + __afl_cmp_map->headers[k].shape = size; + + } + + } + + __afl_cmp_map->headers[k].attribute = attr; hits &= CMP_MAP_H - 1; __afl_cmp_map->log[k][hits].v0 = (u64)arg1; @@ -1347,13 +1415,28 @@ void __cmplog_ins_hook16(uint128_t arg1, uint128_t arg2, uint8_t attr) { k = (k >> 4) ^ (k << 8); k &= CMP_MAP_W - 1; - __afl_cmp_map->headers[k].type = CMP_TYPE_INS; - __afl_cmp_map->headers[k].attribute = attr; + u32 hits; - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; + if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS) { - __afl_cmp_map->headers[k].shape = 15; + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + hits = 0; + __afl_cmp_map->headers[k].hits = 1; + __afl_cmp_map->headers[k].shape = 15; + + } else { + + hits = __afl_cmp_map->headers[k].hits++; + + if (__afl_cmp_map->headers[k].shape < 15) { + + __afl_cmp_map->headers[k].shape = 15; + + } + + } + + __afl_cmp_map->headers[k].attribute = attr; hits &= CMP_MAP_H - 1; __afl_cmp_map->log[k][hits].v0 = (u64)arg1; @@ -1363,6 +1446,8 @@ void __cmplog_ins_hook16(uint128_t arg1, uint128_t arg2, uint8_t attr) { } +#endif + #if defined(__APPLE__) #pragma weak __sanitizer_cov_trace_const_cmp1 = __cmplog_ins_hook1 #pragma weak __sanitizer_cov_trace_const_cmp2 = __cmplog_ins_hook2 @@ -1384,8 +1469,10 @@ void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2) __attribute__((alias("__cmplog_ins_hook4"))); void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2) __attribute__((alias("__cmplog_ins_hook8"))); + #ifdef WORD_SIZE_64 void __sanitizer_cov_trace_const_cmp16(uint128_t arg1, uint128_t arg2) __attribute__((alias("__cmplog_ins_hook16"))); + #endif void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) __attribute__((alias("__cmplog_ins_hook1"))); @@ -1395,8 +1482,10 @@ void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) __attribute__((alias("__cmplog_ins_hook4"))); void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) __attribute__((alias("__cmplog_ins_hook8"))); + #ifdef WORD_SIZE_64 void __sanitizer_cov_trace_cmp16(uint128_t arg1, uint128_t arg2) __attribute__((alias("__cmplog_ins_hook16"))); + #endif #endif /* defined(__APPLE__) */ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { @@ -1409,12 +1498,28 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { k = (k >> 4) ^ (k << 8); k &= CMP_MAP_W - 1; - __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + u32 hits; - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; + if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS) { - __afl_cmp_map->headers[k].shape = 7; + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + hits = 0; + __afl_cmp_map->headers[k].hits = 1; + __afl_cmp_map->headers[k].shape = 7; + + } else { + + hits = __afl_cmp_map->headers[k].hits++; + + if (__afl_cmp_map->headers[k].shape < 7) { + + __afl_cmp_map->headers[k].shape = 7; + + } + + } + + __afl_cmp_map->headers[k].attribute = 1; hits &= CMP_MAP_H - 1; __afl_cmp_map->log[k][hits].v0 = val; @@ -1448,12 +1553,26 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { k = (k >> 4) ^ (k << 8); k &= CMP_MAP_W - 1; - __afl_cmp_map->headers[k].type = CMP_TYPE_RTN; + u32 hits; + + if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS) { + + __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; + + } else { + + hits = __afl_cmp_map->headers[k].hits++; + + if (__afl_cmp_map->headers[k].shape < 31) { - u32 hits = __afl_cmp_map->headers[k].hits; - __afl_cmp_map->headers[k].hits = hits + 1; + __afl_cmp_map->headers[k].shape = 31; - __afl_cmp_map->headers[k].shape = 31; + } + + } hits &= CMP_MAP_RTN_H - 1; __builtin_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0, diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index a74fb6c8..6ce1832f 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -420,6 +420,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { IRB.CreateCall(cmplogHookIns8, args); break; case 128: +#ifdef WORD_SIZE_64 if (max_size == 128) { IRB.CreateCall(cmplogHookIns16, args); @@ -430,6 +431,9 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } +#endif + break; + default: break; } diff --git a/instrumentation/llvm-ngram-coverage.h b/instrumentation/llvm-ngram-coverage.h index 12b666e9..666839c8 100644 --- a/instrumentation/llvm-ngram-coverage.h +++ b/instrumentation/llvm-ngram-coverage.h @@ -1,7 +1,7 @@ #ifndef AFL_NGRAM_CONFIG_H #define AFL_NGRAM_CONFIG_H -#include "../config.h" +#include "types.h" #if (MAP_SIZE_POW2 <= 16) typedef u16 PREV_LOC_T; diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index fe98f031..56022fa6 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -33,7 +33,26 @@ #define CMPLOG_INTROSPECTION //#define ARITHMETIC_LESSER_GREATER -///// Colorization +// CMP attribute enum +enum { + + IS_EQUAL = 1, + IS_GREATER = 2, + IS_LESSER = 4, + IS_FP = 8, + IS_FP_MOD = 16, + IS_INT_MOD = 32 + +}; + +// CMPLOG LVL +enum { + + LVL1 = 1, + LVL2 = 2, + LVL3 = 4 + +}; struct range { @@ -545,8 +564,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, unsigned long long unum; long long num; - // reverse atoi()/strnu?toll() is expensive, so we only to it in lvl == 3 - if (lvl & 4) { + // reverse atoi()/strnu?toll() is expensive, so we only to it in lvl 3 + if (lvl & LVL3) { if (afl->queue_cur->is_ascii) { @@ -618,7 +637,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // we only allow this for ascii2integer (above) if (unlikely(pattern == o_pattern)) { return 0; } - if ((lvl & 1) || ((lvl & 2) && (attr >= 8 && attr <= 15)) || attr >= 16) { + if ((lvl & LVL1) || ((lvl & LVL2) && (attr >= IS_FP && attr < IS_FP_MOD)) || + attr >= IS_FP_MOD) { if (SHAPE_BYTES(h->shape) >= 8 && *status != 1) { @@ -632,8 +652,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // if this is an fcmp (attr & 8 == 8) then do not compare the patterns - // due to a bug in llvm dynamic float bitcasts do not work :( // the value 16 means this is a +- 1.0 test case - if (its_len >= 8 && - ((*buf_64 == pattern && *o_buf_64 == o_pattern) || attr >= 16)) { + if (its_len >= 8 && ((*buf_64 == pattern && *o_buf_64 == o_pattern) || + attr >= IS_FP_MOD)) { u64 tmp_64 = *buf_64; *buf_64 = repl; @@ -674,7 +694,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, if (its_len >= 4 && ((*buf_32 == (u32)pattern && *o_buf_32 == (u32)o_pattern) || - attr >= 16)) { + attr >= IS_FP_MOD)) { u32 tmp_32 = *buf_32; *buf_32 = (u32)repl; @@ -708,7 +728,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, if (its_len >= 2 && ((*buf_16 == (u16)pattern && *o_buf_16 == (u16)o_pattern) || - attr >= 16)) { + attr >= IS_FP_MOD)) { u16 tmp_16 = *buf_16; *buf_16 = (u16)repl; @@ -738,7 +758,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, if (*status != 1) { // u8 - // if (its_len >= 1 && (attr <= 1 || attr >= 8)) + // if (its_len >= 1) // fprintf(stderr, // "TestU8: %u>=1 (idx=%u attr=%u) %x==%x %x==%x <= %x<-%x\n", // its_len, idx, attr, *buf_8, (u8)pattern, *o_buf_8, @@ -746,7 +766,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, if (its_len >= 1 && ((*buf_8 == (u8)pattern && *o_buf_8 == (u8)o_pattern) || - attr >= 16)) { + attr >= IS_FP_MOD)) { u8 tmp_8 = *buf_8; *buf_8 = (u8)repl; @@ -769,10 +789,11 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // in original buffer) #ifdef ARITHMETIC_LESSER_GREATER - if (lvl < 4) { return 0; } + if (lvl < LVL3) { return 0; } // lesser/greater FP comparison - if ((attr & 6) && (attr >= 8 && attr < 16)) { + if ((attr & (IS_LESSER + IS_GREATER)) && + (attr >= IS_FP && attr < IS_FP_MOD)) { u64 repl_new; if (SHAPE_BYTES(h->shape) == 4 && its_len >= 4) { @@ -869,7 +890,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - } else if ((attr & 6) && attr < 8) { + } else if ((attr & (IS_LESSER + IS_GREATER)) && attr < IS_FP) { // lesser/greater integer comparison @@ -903,6 +924,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } +#ifdef WORD_SIZE_64 + static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, u128 pattern, u128 repl, u128 o_pattern, u128 changed_val, u8 attr, u32 idx, @@ -917,15 +940,15 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, u8 backup[16]; u32 its_len = MIN(len - idx, taint_len); u32 shape = h->shape + 1; -#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) size_t off = 0; -#else + #else size_t off = 16 - shape; -#endif + #endif if (its_len >= shape) { -#ifdef _DEBUG + #ifdef _DEBUG // fprintf(stderr, "TestUN: %u>=%u (len=%u idx=%u attr=%u off=%lu) (%u) ", // its_len, shape, len, idx, attr, off, do_reverse); u32 i; @@ -948,7 +971,7 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, for (i = 0; i < shape; i++) fprintf(stderr, "%02x", o_r[off + i]); fprintf(stderr, "\n"); -#endif + #endif if (!memcmp(ptr, p + off, shape) && !memcmp(o_ptr, o_p + off, shape)) { @@ -957,15 +980,15 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } -#ifdef COMBINE + #ifdef COMBINE if (*status == 1) { memcpy(cbuf + idx, r, shape); } -#endif + #endif memcpy(ptr, backup, shape); -#ifdef _DEBUG + #ifdef _DEBUG fprintf(stderr, "Status=%u\n", *status); -#endif + #endif } @@ -990,6 +1013,8 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, } +#endif + static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { u8 *b = (u8 *)&v; @@ -1040,21 +1065,22 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { } +#ifdef WORD_SIZE_64 static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) { u8 *b = (u8 *)&v; u32 k; u8 cons_ff = 0, cons_0 = 0; -#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) u32 off = 0; for (k = 0; k < size; ++k) { -#else - u32 off = 16 - size; + #else + u32 off = 16 - size; for (k = 16 - size; k < 16; ++k) { -#endif + #endif if (b[k] == 0) { ++cons_0; @@ -1077,6 +1103,8 @@ static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) { } +#endif + static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, u32 lvl, struct tainted *taint) { @@ -1091,11 +1119,13 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, u8 found_one = 0; /* loop cmps are useless, detect and ignore them */ +#ifdef WORD_SIZE_64 u128 s128_v0 = 0, s128_v1 = 0, orig_s128_v0 = 0, orig_s128_v1 = 0; - u64 s_v0, s_v1; - u8 s_v0_fixed = 1, s_v1_fixed = 1; - u8 s_v0_inc = 1, s_v1_inc = 1; - u8 s_v0_dec = 1, s_v1_dec = 1; +#endif + u64 s_v0, s_v1; + u8 s_v0_fixed = 1, s_v1_fixed = 1; + u8 s_v0_inc = 1, s_v1_inc = 1; + u8 s_v0_dec = 1, s_v1_dec = 1; switch (SHAPE_BYTES(h->shape)) { @@ -1110,7 +1140,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } // FP handling only from lvl 2 onwards - if ((h->attribute & 8) && lvl < 2) return 0; + if ((h->attribute & IS_FP) && lvl < LVL2) return 0; for (i = 0; i < loggeds; ++i) { @@ -1162,6 +1192,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } +#ifdef WORD_SIZE_64 if (unlikely(is_n)) { s128_v0 = ((u128)o->v0) + (((u128)o->v0_128) << 64); @@ -1171,6 +1202,8 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } +#endif + for (idx = 0; idx < len; ++idx) { if (have_taint) { @@ -1195,6 +1228,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, status = 0; +#ifdef WORD_SIZE_64 if (is_n) { // _ExtInt special case including u128 if (s128_v0 != orig_s128_v0 && orig_s128_v0 != orig_s128_v1) { @@ -1239,11 +1273,13 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } +#endif + // even for u128 and _ExtInt we do cmp_extend_encoding() because // if we got here their own special trials failed and it might just be // a cast from e.g. u64 to u128 from the input data. - if ((o->v0 != orig_o->v0 || lvl >= 4) && orig_o->v0 != orig_o->v1) { + if ((o->v0 != orig_o->v0 || lvl >= LVL3) && orig_o->v0 != orig_o->v1) { if (unlikely(cmp_extend_encoding( afl, h, o->v0, o->v1, orig_o->v0, orig_o->v1, h->attribute, idx, @@ -1263,7 +1299,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } status = 0; - if ((o->v1 != orig_o->v1 || lvl >= 4) && orig_o->v0 != orig_o->v1) { + if ((o->v1 != orig_o->v1 || lvl >= LVL3) && orig_o->v0 != orig_o->v1) { if (unlikely(cmp_extend_encoding( afl, h, o->v1, o->v0, orig_o->v1, orig_o->v0, h->attribute, idx, @@ -1521,13 +1557,6 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { } - // do it manually, forkserver clear only afl->fsrv.trace_bits - memset(afl->shm.cmp_map->headers, 0, sizeof(afl->shm.cmp_map->headers)); - - if (unlikely(common_fuzz_cmplog_stuff(afl, buf, len))) { return 1; } - - memcpy(afl->orig_cmp_map, afl->shm.cmp_map, sizeof(struct cmp_map)); - struct tainted *taint = NULL; if (!afl->queue_cur->taint || !afl->queue_cur->cmplog_colorinput) { @@ -1562,8 +1591,6 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { buf = afl->queue_cur->cmplog_colorinput; taint = afl->queue_cur->taint; - // reget the cmplog information - if (unlikely(common_fuzz_cmplog_stuff(afl, buf, len))) { return 1; } } @@ -1583,9 +1610,13 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { u32 cmp_locations = 0; #endif - // do it manually, forkserver clear only afl->fsrv.trace_bits - memset(afl->shm.cmp_map->headers, 0, sizeof(afl->shm.cmp_map->headers)); - + // Generate the cmplog data + // manually clear the full cmp_map + memset(afl->shm.cmp_map, 0, sizeof(struct cmp_map)); + if (unlikely(common_fuzz_cmplog_stuff(afl, orig_buf, len))) { return 1; } + memcpy(afl->orig_cmp_map, afl->shm.cmp_map, sizeof(struct cmp_map)); + // manually clear just the headers + memset(afl->shm.cmp_map->headers, 0, sizeof(struct cmp_header)); if (unlikely(common_fuzz_cmplog_stuff(afl, buf, len))) { return 1; } u64 orig_hit_cnt, new_hit_cnt; @@ -1602,7 +1633,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { u32 cmplog_lvl = afl->cmplog_lvl; if (!cmplog_done) { - lvl = 1; + lvl = LVL1; } else { @@ -1610,8 +1641,8 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { } - if (cmplog_lvl >= 2 && cmplog_done < 2) { lvl += 2; } - if (cmplog_lvl >= 3 && cmplog_done < 3) { lvl += 4; } + if (cmplog_lvl >= 2 && cmplog_done < 2) { lvl += LVL2; } + if (cmplog_lvl >= 3 && cmplog_done < 3) { lvl += LVL3; } #ifdef COMBINE u8 *cbuf = afl_realloc((void **)&afl->in_scratch_buf, len + 128); @@ -1668,7 +1699,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { } - } else if (lvl & 1) { + } else if (lvl & LVL1) { if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, cbuf, len, taint))) { -- cgit 1.4.1 From a9ebf72a8408f993e6279ba853c49e31310cf73f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 18 Jan 2021 22:19:04 +0100 Subject: cmp map memset fix --- src/afl-fuzz-redqueen.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 56022fa6..4488d220 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -110,7 +110,7 @@ static struct range *pop_biggest_range(struct range **ranges) { static void dump(char *txt, u8 *buf, u32 len) { u32 i; - fprintf(stderr, "DUMP %s %llx ", txt, hash64(buf, len, HASH_CONST)); + fprintf(stderr, "DUMP %s %016llx ", txt, hash64(buf, len, HASH_CONST)); for (i = 0; i < len; i++) fprintf(stderr, "%02x", buf[i]); fprintf(stderr, "\n"); @@ -255,12 +255,6 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, memcpy(changed, buf, len); type_replace(afl, changed, len); -#ifdef _DEBUG - dump("ORIG", buf, len); - dump("CHAN", changed, len); - fprintf(stderr, "CKSUM %llx (%u)\n", exec_cksum, afl->fsrv.map_size); -#endif - while ((rng = pop_biggest_range(&ranges)) != NULL && afl->stage_cur < afl->stage_max) { @@ -949,8 +943,8 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, if (its_len >= shape) { #ifdef _DEBUG - // fprintf(stderr, "TestUN: %u>=%u (len=%u idx=%u attr=%u off=%lu) (%u) ", - // its_len, shape, len, idx, attr, off, do_reverse); + fprintf(stderr, "TestUN: %u>=%u (len=%u idx=%u attr=%u off=%lu) (%u) ", + its_len, shape, len, idx, attr, off, do_reverse); u32 i; u8 *o_r = (u8 *)&changed_val; for (i = 0; i < shape; i++) @@ -1545,12 +1539,6 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { u8 r = 1; - if (unlikely(!afl->orig_cmp_map)) { - - afl->orig_cmp_map = ck_alloc_nozero(sizeof(struct cmp_map)); - - } - if (unlikely(!afl->pass_stats)) { afl->pass_stats = ck_alloc(sizeof(struct afl_pass_stat) * CMP_MAP_W); @@ -1583,10 +1571,6 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { #endif -#ifdef _DEBUG - dump("NEW ", buf, len); -#endif - } else { buf = afl->queue_cur->cmplog_colorinput; @@ -1611,14 +1595,27 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { #endif // Generate the cmplog data + // manually clear the full cmp_map memset(afl->shm.cmp_map, 0, sizeof(struct cmp_map)); if (unlikely(common_fuzz_cmplog_stuff(afl, orig_buf, len))) { return 1; } + if (unlikely(!afl->orig_cmp_map)) { + + afl->orig_cmp_map = ck_alloc_nozero(sizeof(struct cmp_map)); + + } + memcpy(afl->orig_cmp_map, afl->shm.cmp_map, sizeof(struct cmp_map)); - // manually clear just the headers - memset(afl->shm.cmp_map->headers, 0, sizeof(struct cmp_header)); + memset(afl->shm.cmp_map->headers, 0, sizeof(struct cmp_header) * CMP_MAP_W); if (unlikely(common_fuzz_cmplog_stuff(afl, buf, len))) { return 1; } +#ifdef _DEBUG + dump("ORIG", orig_buf, len); + dump("NEW ", buf, len); +#endif + + // Start insertion loop + u64 orig_hit_cnt, new_hit_cnt; u64 orig_execs = afl->fsrv.total_execs; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; -- cgit 1.4.1 From e91f3b0de65376b001d45892cc6bdd2fcafde949 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 19 Jan 2021 10:41:42 +0100 Subject: codeql fix --- docs/Changelog.md | 1 + src/afl-fuzz-run.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index e0f8e9bf..60f09ca5 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -19,6 +19,7 @@ sending a mail to . for reporting) - if determinstic mode is active (-D, or -M without -d) then we sync after every queue entry as this can take very long time otherwise + - better detection if a target needs a large shared map - switched to a faster RNG - added hghwng's patch for faster trace map analysis - afl-cc diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index b597488b..17c305ed 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -424,7 +424,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, if (unlikely(afl->fixed_seed)) { - diff_us = (afl->fsrv.exec_tmout - 1) * afl->stage_max; + diff_us = (u64)(afl->fsrv.exec_tmout - 1) * (u64)afl->stage_max; } else { -- cgit 1.4.1 From 95ee2cdd574dabf39d662cd2a76542b733d374ac Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 19 Jan 2021 13:05:42 +0100 Subject: cleanup --- src/afl-fuzz-redqueen.c | 83 ++++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 4488d220..1c5b95f6 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -29,7 +29,7 @@ #include "cmplog.h" //#define _DEBUG -//#define COMBINE +#define COMBINE #define CMPLOG_INTROSPECTION //#define ARITHMETIC_LESSER_GREATER @@ -1103,14 +1103,13 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, u32 lvl, struct tainted *taint) { struct cmp_header *h = &afl->shm.cmp_map->headers[key]; - struct tainted * t; - u32 i, j, idx, taint_len; - u32 have_taint = 1, is_n = 0; - u32 loggeds = h->hits; - if (h->hits > CMP_MAP_H) { loggeds = CMP_MAP_H; } + // FP handling only from lvl 2 onwards + if ((h->attribute & IS_FP) && lvl < LVL2) { return 0; } - u8 status = 0; - u8 found_one = 0; + struct tainted *t; + u32 i, j, idx, taint_len, loggeds; + u32 have_taint = 1, is_n = 0; + u8 status = 0, found_one = 0; /* loop cmps are useless, detect and ignore them */ #ifdef WORD_SIZE_64 @@ -1121,6 +1120,16 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, u8 s_v0_inc = 1, s_v1_inc = 1; u8 s_v0_dec = 1, s_v1_dec = 1; + if (h->hits > CMP_MAP_H) { + + loggeds = CMP_MAP_H; + + } else { + + loggeds = h->hits; + + } + switch (SHAPE_BYTES(h->shape)) { case 1: @@ -1133,9 +1142,6 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } - // FP handling only from lvl 2 onwards - if ((h->attribute & IS_FP) && lvl < LVL2) return 0; - for (i = 0; i < loggeds; ++i) { struct cmp_operands *o = &afl->shm.cmp_map->log[key][i]; @@ -1742,51 +1748,56 @@ exit_its: } #ifdef COMBINE - // copy the current virgin bits so we can recover the information - u8 *virgin_save = afl_realloc((void **)&afl->eff_buf, afl->shm.map_size); - memcpy(virgin_save, afl->virgin_bits, afl->shm.map_size); - // reset virgin bits to the backup previous to redqueen - memcpy(afl->virgin_bits, virgin_backup, afl->shm.map_size); + if (afl->queued_paths + afl->unique_crashes > orig_hit_cnt + 1) { - u8 status = 0; - its_fuzz(afl, cbuf, len, &status); + // copy the current virgin bits so we can recover the information + u8 *virgin_save = afl_realloc((void **)&afl->eff_buf, afl->shm.map_size); + memcpy(virgin_save, afl->virgin_bits, afl->shm.map_size); + // reset virgin bits to the backup previous to redqueen + memcpy(afl->virgin_bits, virgin_backup, afl->shm.map_size); + + u8 status = 0; + its_fuzz(afl, cbuf, len, &status); // now combine with the saved virgin bits #ifdef WORD_SIZE_64 - u64 *v = (u64 *)afl->virgin_bits; - u64 *s = (u64 *)virgin_save; - u32 i; - for (i = 0; i < (afl->shm.map_size >> 3); i++) { + u64 *v = (u64 *)afl->virgin_bits; + u64 *s = (u64 *)virgin_save; + u32 i; + for (i = 0; i < (afl->shm.map_size >> 3); i++) { - v[i] &= s[i]; + v[i] &= s[i]; - } + } #else - u32 *v = (u64 *)afl->virgin_bits; - u32 *s = (u64 *)virgin_save; - u32 i; - for (i = 0; i < (afl->shm.map_size >> 2); i++) { + u32 *v = (u64 *)afl->virgin_bits; + u32 *s = (u64 *)virgin_save; + u32 i; + for (i = 0; i < (afl->shm.map_size >> 2); i++) { - v[i] &= s[i]; + v[i] &= s[i]; - } + } #endif #ifdef _DEBUG - dump("COMB", cbuf, len); - if (status == 1) { + dump("COMB", cbuf, len); + if (status == 1) { - fprintf(stderr, "NEW COMBINED\n"); + fprintf(stderr, "NEW COMBINED\n"); - } else { + } else { - fprintf(stderr, "NO new combined\n"); + fprintf(stderr, "NO new combined\n"); - } + } #endif + + } + #endif new_hit_cnt = afl->queued_paths + afl->unique_crashes; -- cgit 1.4.1 From 292f91a55f6af5e06b41b429e55f65b7df4d8d16 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 19 Jan 2021 13:16:39 +0100 Subject: tiny scan-build nags fixed --- qemu_mode/qemuafl | 2 +- src/afl-fuzz.c | 6 +++--- unicorn_mode/unicornafl | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl index 5400ce88..21ff3438 160000 --- a/qemu_mode/qemuafl +++ b/qemu_mode/qemuafl @@ -1 +1 @@ -Subproject commit 5400ce883a751582473665d8fd18f8e8f9d14cde +Subproject commit 21ff34383764a8c6f66509b3b8d5282468c721e1 diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index bb2674f0..e6317f43 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -586,7 +586,7 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->timeout_given) { FATAL("Multiple -t options not supported"); } - if (sscanf(optarg, "%u%c", &afl->fsrv.exec_tmout, &suffix) < 1 || + if (!optarg || sscanf(optarg, "%u%c", &afl->fsrv.exec_tmout, &suffix) < 1 || optarg[0] == '-') { FATAL("Bad syntax used for -t"); @@ -768,7 +768,7 @@ int main(int argc, char **argv_orig, char **envp) { case 'V': { afl->most_time_key = 1; - if (sscanf(optarg, "%llu", &afl->most_time) < 1 || optarg[0] == '-') { + if (!optarg || sscanf(optarg, "%llu", &afl->most_time) < 1 || optarg[0] == '-') { FATAL("Bad syntax used for -V"); @@ -779,7 +779,7 @@ int main(int argc, char **argv_orig, char **envp) { case 'E': { afl->most_execs_key = 1; - if (sscanf(optarg, "%llu", &afl->most_execs) < 1 || optarg[0] == '-') { + if (!optarg || sscanf(optarg, "%llu", &afl->most_execs) < 1 || optarg[0] == '-') { FATAL("Bad syntax used for -E"); diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl index 8cca4801..1c88501b 160000 --- a/unicorn_mode/unicornafl +++ b/unicorn_mode/unicornafl @@ -1 +1 @@ -Subproject commit 8cca4801adb767dce7cf72202d7d25bdb420cf7d +Subproject commit 1c88501b435fa10f95c8df0bbe47ccb5313b0428 -- cgit 1.4.1 From 0367f6c72339ba655956d7e17b0b27c92b22d781 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 19 Jan 2021 14:03:10 +0100 Subject: cleanup and rename _DISCARD->_SKIP --- instrumentation/README.instrument_list.md | 2 +- instrumentation/afl-compiler-rt.o.c | 2 +- src/afl-cc.c | 6 +++--- src/afl-fuzz-bitmap.c | 1 - src/afl-fuzz.c | 1 - src/afl-ld-lto.c | 1 - 6 files changed, 5 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/instrumentation/README.instrument_list.md b/instrumentation/README.instrument_list.md index 83197954..b47b50f6 100644 --- a/instrumentation/README.instrument_list.md +++ b/instrumentation/README.instrument_list.md @@ -41,7 +41,7 @@ in any function where you want: * `__AFL_COVERAGE_ON();` - enable coverage from this point onwards * `__AFL_COVERAGE_OFF();` - disable coverage from this point onwards * `__AFL_COVERAGE_DISCARD();` - reset all coverage gathered until this point - * `__AFL_COVERAGE_ABORT();` - mark this test case as unimportant. Whatever happens, afl-fuzz will ignore it. + * `__AFL_COVERAGE_SKIP();` - mark this test case as unimportant. Whatever happens, afl-fuzz will ignore it. ## 3) Selective instrumenation with AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index b735d8df..e31bff16 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1429,7 +1429,7 @@ void __afl_coverage_discard() { } // discard the testcase -void __afl_coverage_abort() { +void __afl_coverage_skip() { __afl_coverage_discard(); diff --git a/src/afl-cc.c b/src/afl-cc.c index 8fb42718..1379488e 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -835,7 +835,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" "extern \"C\" void __afl_coverage_discard();" - "extern \"C\" void __afl_coverage_abort();" + "extern \"C\" void __afl_coverage_skip();" "extern \"C\" void __afl_coverage_on();" "extern \"C\" void __afl_coverage_off();"; @@ -844,7 +844,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" "void __afl_coverage_discard();" - "void __afl_coverage_abort();" + "void __afl_coverage_skip();" "void __afl_coverage_on();" "void __afl_coverage_off();"; @@ -857,7 +857,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_OFF()=__afl_coverage_off()"; cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()"; - cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_ABORT()=__afl_coverage_abort()"; + cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_SKIP()=__afl_coverage_skip()"; cc_params[cc_par_cnt++] = "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " "__afl_fuzz_alt_ptr)"; diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index ed8c2510..586f3990 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -703,7 +703,6 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (!classified) { classify_counts(&afl->fsrv); - // classified = 1; } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e6317f43..7facf261 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -337,7 +337,6 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_DEBUG")) { debug = afl->debug = 1; } - // map_size = get_map_size(); afl_state_init(afl, map_size); afl->debug = debug; afl_fsrv_init(&afl->fsrv); diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index 7a4d9132..0671d1c4 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -253,7 +253,6 @@ static void edit_params(int argc, char **argv) { int main(int argc, char **argv) { s32 pid, i, status; - // u8 * ptr; char thecwd[PATH_MAX]; if (getenv("AFL_LD_CALLER") != NULL) { -- cgit 1.4.1 From e7b572af3608e2d097aad17408ad4853befdc02c Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 20 Jan 2021 01:49:32 +0100 Subject: bugfix and new transform detection feature --- instrumentation/afl-compiler-rt.o.c | 14 +- src/afl-fuzz-redqueen.c | 430 ++++++++++++++++++++++++++++++++---- src/afl-fuzz.c | 14 ++ 3 files changed, 416 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 0ce96673..a290f110 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1545,6 +1545,18 @@ static int area_is_mapped(void *ptr, size_t len) { void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { + /* + u32 i; + if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; + fprintf(stderr, "rtn arg0="); + for (i = 0; i < 8; i++) + fprintf(stderr, "%02x", ptr1[i]); + fprintf(stderr, " arg1="); + for (i = 0; i < 8; 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; @@ -1555,7 +1567,7 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { u32 hits; - if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS) { + if (__afl_cmp_map->headers[k].type != CMP_TYPE_RTN) { __afl_cmp_map->headers[k].type = CMP_TYPE_RTN; hits = 0; diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 1c5b95f6..bf41863e 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -30,18 +30,21 @@ //#define _DEBUG #define COMBINE -#define CMPLOG_INTROSPECTION +//#define CMPLOG_INTROSPECTION //#define ARITHMETIC_LESSER_GREATER +//#define TRANSFORM // CMP attribute enum enum { - IS_EQUAL = 1, - IS_GREATER = 2, - IS_LESSER = 4, - IS_FP = 8, - IS_FP_MOD = 16, - IS_INT_MOD = 32 + IS_EQUAL = 1, // arithemtic equal comparison + IS_GREATER = 2, // arithmetic greater comparison + IS_LESSER = 4, // arithmetic lesser comparison + IS_FP = 8, // is a floating point, not an integer + /* --- below are internal settings, not from target cmplog */ + IS_FP_MOD = 16, // arithemtic changed floating point + IS_INT_MOD = 32, // arithmetic changed interger + IS_TRANSFORM = 64 // transformed integer }; @@ -466,6 +469,7 @@ static u8 its_fuzz(afl_state_t *afl, u8 *buf, u32 len, u8 *status) { } +#ifdef TRANSFORM static int strntoll(const char *str, size_t sz, char **end, int base, long long *out) { @@ -513,6 +517,8 @@ static int strntoull(const char *str, size_t sz, char **end, int base, } +#endif + static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u64 pattern, u64 repl, u64 o_pattern, u64 changed_val, u8 attr, u32 idx, u32 taint_len, @@ -553,14 +559,15 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u32 its_len = MIN(len - idx, taint_len); - u8 * endptr; - u8 use_num = 0, use_unum = 0; - unsigned long long unum; - long long num; - +#ifdef TRANSFORM // reverse atoi()/strnu?toll() is expensive, so we only to it in lvl 3 if (lvl & LVL3) { + u8 * endptr; + u8 use_num = 0, use_unum = 0; + unsigned long long unum; + long long num; + if (afl->queue_cur->is_ascii) { endptr = buf_8; @@ -575,11 +582,11 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } -#ifdef _DEBUG + #ifdef _DEBUG if (idx == 0) fprintf(stderr, "ASCII is=%u use_num=%u use_unum=%u idx=%u %llx==%llx\n", afl->queue_cur->is_ascii, use_num, use_unum, idx, num, pattern); -#endif + #endif // num is likely not pattern as atoi("AAA") will be zero... if (use_num && ((u64)num == pattern || !num)) { @@ -626,8 +633,201 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } + // Try to identify transform magic + if (pattern != o_pattern && repl == changed_val && attr <= IS_EQUAL) { + + u64 *ptr = (u64 *)&buf[idx]; + u64 *o_ptr = (u64 *)&orig_buf[idx]; + u64 b_val, o_b_val, mask; + + switch (SHAPE_BYTES(h->shape)) { + + case 0: + case 1: + b_val = (u64)(*ptr % 0x100); + o_b_val = (u64)(*o_ptr % 0x100); + mask = 0xff; + break; + case 2: + case 3: + b_val = (u64)(*ptr % 0x10000); + o_b_val = (u64)(*o_ptr % 0x10000); + mask = 0xffff; + break; + case 4: + case 5: + case 6: + case 7: + b_val = (u64)(*ptr % 0x100000000); + o_b_val = (u64)(*o_ptr % 0x100000000); + mask = 0xffffffff; + break; + default: + b_val = *ptr; + o_b_val = *o_ptr; + mask = 0xffffffffffffffff; + + } + + // test for arithmetic, eg. "if ((user_val - 0x1111) == 0x1234) ..." + s64 diff = pattern - b_val; + s64 o_diff = o_pattern - o_b_val; + /* + fprintf(stderr, "DIFF1 idx=%03u shape=%02u %llx-%llx=%lx\n", idx, + h->shape + 1, o_pattern, o_b_val, o_diff); + fprintf(stderr, "DIFF1 %016llx %llx-%llx=%lx\n", repl, pattern, + b_val, diff);*/ + if (diff == o_diff && diff) { + + // this could be an arithmetic transformation + + u64 new_repl = (u64)((s64)repl - diff); + // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); + + if (unlikely(cmp_extend_encoding( + afl, h, pattern, new_repl, o_pattern, repl, IS_TRANSFORM, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + return 1; + + } + + if (*status == 1) { fprintf(stderr, "FOUND!\n"); } + + } + + // test for XOR, eg. "if ((user_val ^ 0xabcd) == 0x1234) ..." + if (*status != 1) { + + diff = pattern ^ b_val; + s64 o_diff = o_pattern ^ o_b_val; + + /* fprintf(stderr, "DIFF2 idx=%03u shape=%02u %llx-%llx=%lx\n", + idx, h->shape + 1, o_pattern, o_b_val, o_diff); fprintf(stderr, + "DIFF2 %016llx %llx-%llx=%lx\n", repl, pattern, b_val, diff);*/ + if (diff == o_diff && diff) { + + // this could be a XOR transformation + + u64 new_repl = (u64)((s64)repl ^ diff); + // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); + + if (unlikely(cmp_extend_encoding( + afl, h, pattern, new_repl, o_pattern, repl, IS_TRANSFORM, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + return 1; + + } + + if (*status == 1) { fprintf(stderr, "FOUND!\n"); } + + } + + } + + // test for to lowercase, eg. "new_val = (user_val | 0x2020) ..." + if (*status != 1) { + + if ((b_val | (0x2020202020202020 & mask)) == (pattern & mask)) { + + diff = 1; + + } else { + + diff = 0; + + } + + if ((o_b_val | (0x2020202020202020 & mask)) == (o_pattern & mask)) { + + o_diff = 1; + + } else { + + diff = 0; + + } + + /* fprintf(stderr, "DIFF3 idx=%03u shape=%02u %llx-%llx=%lx\n", + idx, h->shape + 1, o_pattern, o_b_val, o_diff); fprintf(stderr, + "DIFF3 %016llx %llx-%llx=%lx\n", repl, pattern, b_val, diff);*/ + if (o_diff && diff) { + + // this could be a lower to upper + + u64 new_repl = (repl & (0x5f5f5f5f5f5f5f5f & mask)); + // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); + + if (unlikely(cmp_extend_encoding( + afl, h, pattern, new_repl, o_pattern, repl, IS_TRANSFORM, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + return 1; + + } + + if (*status == 1) { fprintf(stderr, "FOUND!\n"); } + + } + + } + + // test for to uppercase, eg. "new_val = (user_val | 0x5f5f) ..." + if (*status != 1) { + + if ((b_val & (0x5f5f5f5f5f5f5f5f & mask)) == (pattern & mask)) { + + diff = 1; + + } else { + + diff = 0; + + } + + if ((o_b_val & (0x5f5f5f5f5f5f5f5f & mask)) == (o_pattern & mask)) { + + o_diff = 1; + + } else { + + o_diff = 0; + + } + + /* fprintf(stderr, "DIFF4 idx=%03u shape=%02u %llx-%llx=%lx\n", + idx, h->shape + 1, o_pattern, o_b_val, o_diff); fprintf(stderr, + "DIFF4 %016llx %llx-%llx=%lx\n", repl, pattern, b_val, diff);*/ + if (o_diff && diff) { + + // this could be a lower to upper + + u64 new_repl = (repl | (0x2020202020202020 & mask)); + // fprintf(stderr, "SAME DIFF %llx->%llx\n", repl, new_repl); + + if (unlikely(cmp_extend_encoding( + afl, h, pattern, new_repl, o_pattern, repl, IS_TRANSFORM, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + return 1; + + } + + if (*status == 1) { fprintf(stderr, "FOUND!\n"); } + + } + + } + + *status = 0; + + } + } +#endif + // we only allow this for ascii2integer (above) if (unlikely(pattern == o_pattern)) { return 0; } @@ -783,7 +983,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // in original buffer) #ifdef ARITHMETIC_LESSER_GREATER - if (lvl < LVL3) { return 0; } + if (lvl < LVL3 || attr == IS_TRANSFORM) { return 0; } // lesser/greater FP comparison if ((attr & (IS_LESSER + IS_GREATER)) && @@ -1374,56 +1574,185 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, - u8 *o_pattern, u32 idx, u32 taint_len, - u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, - u8 *status) { + u8 *o_pattern, u8 *changed_val, u32 idx, + u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf, + u32 len, u8 lvl, u8 *status) { #ifndef COMBINE (void)(cbuf); #endif - u32 i; + u32 i = 0; u32 its_len = MIN((u32)32, len - idx); its_len = MIN(its_len, taint_len); u8 save[32]; memcpy(save, &buf[idx], its_len); - for (i = 0; i < its_len; ++i) { + if (lvl == LVL1) { - if ((pattern[i] != buf[idx + i] && o_pattern[i] != orig_buf[idx + i]) || - *status == 1) { + for (i = 0; i < its_len; ++i) { - break; + if ((pattern[i] != buf[idx + i] && o_pattern[i] != orig_buf[idx + i]) || + *status == 1) { + + break; + + } + + buf[idx + i] = repl[i]; + + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + +#ifdef COMBINE + if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); } +#endif } - buf[idx + i] = repl[i]; + } + + if (*status == 1) return 0; + + if (lvl & LVL3) { + + u8 toupper = 0, tolower = 0, xor = 0, arith = 0; + u8 xor_val[32], arith_val[32]; + u32 j; + +#ifdef _DEBUG + fprintf(stderr, "RTN TRANSFORM idx=%u ", idx); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", orig_buf[idx + j]); + fprintf(stderr, " -> "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", o_pattern[j]); + fprintf(stderr, " <= "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", repl[j]); + fprintf(stderr, "\n"); + fprintf(stderr, " "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", buf[idx + j]); + fprintf(stderr, " -> "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", pattern[j]); + fprintf(stderr, " <= "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", changed_val[j]); + fprintf(stderr, "\n"); +#endif + for (i = 0; i < its_len; ++i) { + + xor_val[i] = pattern[i] ^ buf[idx + i]; + arith_val[i] = pattern[i] - buf[idx + i]; + + if ((o_pattern[i] ^ orig_buf[idx + i]) == xor_val[i] && xor_val[i]) { + + ++xor; + + } + + if ((o_pattern[i] - orig_buf[idx + i]) == arith_val[i] && arith_val[i]) { + + ++arith; + + } + + if ((buf[idx + i] | 0x20) == pattern[i] && + (orig_buf[idx + i] | 0x20) == o_pattern[i]) { + + ++tolower; - if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + } + + if ((buf[idx + i] & 0x5a) == pattern[i] && + (orig_buf[idx + i] & 0x5a) == o_pattern[i]) { + + ++toupper; + + } + +#ifdef _DEBUG + fprintf(stderr, "RTN loop=%u %u %u %u %u (%u %u)\n", i, xor, arith, + tolower, toupper, arith_val[i], xor_val[i]); +#endif + + if (xor > i) { + + for (j = 0; j <= i; j++) + buf[idx + j] = repl[j] ^ xor_val[j]; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + // fprintf(stderr, "RTN ATTEMPT xor %u result %u\n", xor, *status); + + } + + if (arith > i && *status != 1) { + + for (j = 0; j <= i; j++) + buf[idx + j] = repl[j] - arith_val[j]; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + // fprintf(stderr, "RTN ATTEMPT arith %u result %u\n", arith, *status); + + } + + if (toupper > i && *status != 1) { + + for (j = 0; j <= i; j++) + buf[idx + j] = repl[j] | 0x20; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + // fprintf(stderr, "RTN ATTEMPT toupper %u result %u\n", toupper, + // *status); + + } + + if (tolower > i && *status != 1) { + + for (j = 0; j <= i; j++) + buf[idx + j] = repl[j] & 0x5f; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + // fprintf(stderr, "RTN ATTEMPT tolower %u result %u\n", tolower, + // *status); + + } #ifdef COMBINE - if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); } + if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); } #endif + if ((i >= xor&&i >= arith &&i >= tolower &&i >= toupper) || + repl[i] != changed_val[i] || *status == 1) { + + break; + + } + + } + } memcpy(&buf[idx], save, i); + return 0; } static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, - u32 len, struct tainted *taint) { + u32 len, u8 lvl, struct tainted *taint) { struct tainted * t; struct cmp_header *h = &afl->shm.cmp_map->headers[key]; - u32 i, j, idx, have_taint = 1, taint_len; + u32 i, j, idx, have_taint = 1, taint_len, loggeds; + u8 status = 0, found_one = 0; - u32 loggeds = h->hits; - if (h->hits > CMP_MAP_RTN_H) { loggeds = CMP_MAP_RTN_H; } + if (h->hits > CMP_MAP_RTN_H) { - u8 status = 0; - u8 found_one = 0; + loggeds = CMP_MAP_RTN_H; + + } else { + + loggeds = h->hits; + + } for (i = 0; i < loggeds; ++i) { @@ -1445,6 +1774,20 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } + /* + struct cmp_header *hh = &afl->orig_cmp_map->headers[key]; + fprintf(stderr, "RTN N hits=%u id=%u shape=%u attr=%u v0=", h->hits, h->id, + h->shape, h->attribute); for (j = 0; j < 8; j++) fprintf(stderr, "%02x", + o->v0[j]); fprintf(stderr, " v1="); for (j = 0; j < 8; j++) fprintf(stderr, + "%02x", o->v1[j]); fprintf(stderr, "\nRTN O hits=%u id=%u shape=%u attr=%u + o0=", hh->hits, hh->id, hh->shape, hh->attribute); for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", orig_o->v0[j]); + fprintf(stderr, " o1="); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", orig_o->v1[j]); + fprintf(stderr, "\n"); + */ + t = taint; while (t->next) { @@ -1476,9 +1819,9 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, status = 0; - if (unlikely(rtn_extend_encoding(afl, o->v0, o->v1, orig_o->v0, idx, - taint_len, orig_buf, buf, cbuf, len, - &status))) { + if (unlikely(rtn_extend_encoding(afl, o->v0, o->v1, orig_o->v0, + orig_o->v1, idx, taint_len, orig_buf, + buf, cbuf, len, lvl, &status))) { return 1; @@ -1493,9 +1836,9 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, status = 0; - if (unlikely(rtn_extend_encoding(afl, o->v1, o->v0, orig_o->v1, idx, - taint_len, orig_buf, buf, cbuf, len, - &status))) { + if (unlikely(rtn_extend_encoding(afl, o->v1, o->v0, orig_o->v1, + orig_o->v0, idx, taint_len, orig_buf, + buf, cbuf, len, lvl, &status))) { return 1; @@ -1511,7 +1854,7 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } // If failed, add to dictionary - if (!found_one) { + if (!found_one && (lvl & LVL1)) { if (unlikely(!afl->pass_stats[key].total)) { @@ -1702,9 +2045,14 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { } - } else if (lvl & LVL1) { + } else if ((lvl & LVL1) + +#ifdef TRANSFORM + || (lvl & LVL3) +#endif + ) { - if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, cbuf, len, taint))) { + if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, cbuf, len, lvl, taint))) { goto exit_its; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 88c40ee8..0f76e8a3 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1515,7 +1515,21 @@ int main(int argc, char **argv_orig, char **envp) { } + u8 *save_env = NULL; + if (afl->cmplog_binary) { + + save_env = ck_strdup(getenv(CMPLOG_SHM_ENV_VAR)); + unsetenv(CMPLOG_SHM_ENV_VAR); // normal forkserver should not have this + + } + perform_dry_run(afl); + if (save_env) { + + setenv(CMPLOG_SHM_ENV_VAR, save_env, 1); // needed for at_exit() + ck_free(save_env); + + } /* if (!user_set_cache && afl->q_testcase_max_cache_size) { -- cgit 1.4.1 From 0306261fec4c9ebc2b1361da1d0fbbe9bade8847 Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Wed, 13 Jan 2021 12:32:36 +0800 Subject: Fix build error for Android - LTO not work yet --- Android.bp | 304 +++++++++++++++++++++++++-- Android.mk | 1 - include/afl-fuzz.h | 4 - include/android-ashmem.h | 113 ++++------ src/afl-analyze.c | 3 - src/afl-fuzz-stats.c | 4 + src/afl-gotcpu.c | 3 - src/afl-showmap.c | 3 - src/afl-tmin.c | 4 - utils/afl_network_proxy/afl-network-server.c | 4 - 10 files changed, 332 insertions(+), 111 deletions(-) delete mode 120000 Android.mk (limited to 'src') diff --git a/Android.bp b/Android.bp index 2c2114b2..549577db 100644 --- a/Android.bp +++ b/Android.bp @@ -1,5 +1,13 @@ cc_defaults { name: "afl-defaults", + sanitize: { + never: true, + }, + + local_include_dirs: [ + "include", + "instrumentation", + ], cflags: [ "-funroll-loops", @@ -14,12 +22,17 @@ cc_defaults { "-DBIN_PATH=\"out/host/linux-x86/bin\"", "-DDOC_PATH=\"out/host/linux-x86/shared/doc/afl\"", "-D__USE_GNU", + "-D__aarch64__", + "-DDEBUG_BUILD", + "-U_FORTIFY_SOURCE", + "-g", + "-O0", + "-fno-omit-frame-pointer", ], } cc_binary { name: "afl-fuzz", - static_executable: true, host_supported: true, defaults: [ @@ -27,7 +40,11 @@ cc_binary { ], srcs: [ - "afl-fuzz.c", + "src/afl-fuzz*.c", + "src/afl-common.c", + "src/afl-sharedmem.c", + "src/afl-forkserver.c", + "src/afl-performance.c", ], } @@ -41,7 +58,10 @@ cc_binary { ], srcs: [ - "afl-showmap.c", + "src/afl-showmap.c", + "src/afl-common.c", + "src/afl-sharedmem.c", + "src/afl-forkserver.c", ], } @@ -55,7 +75,11 @@ cc_binary { ], srcs: [ - "afl-tmin.c", + "src/afl-tmin.c", + "src/afl-common.c", + "src/afl-sharedmem.c", + "src/afl-forkserver.c", + "src/afl-performance.c", ], } @@ -69,7 +93,10 @@ cc_binary { ], srcs: [ - "afl-analyze.c", + "src/afl-analyze.c", + "src/afl-common.c", + "src/afl-sharedmem.c", + "src/afl-performance.c", ], } @@ -83,12 +110,13 @@ cc_binary { ], srcs: [ - "afl-gotcpu.c", + "src/afl-gotcpu.c", + "src/afl-common.c", ], } cc_binary_host { - name: "afl-clang-fast", + name: "afl-cc", static_executable: true, defaults: [ @@ -98,44 +126,286 @@ cc_binary_host { cflags: [ "-D__ANDROID__", "-DAFL_PATH=\"out/host/linux-x86/lib64\"", + "-DAFL_CLANG_FLTO=\"-flto=full\"", + "-DUSE_BINDIR=1", + "-DLLVM_BINDIR=\"prebuilts/clang/host/linux-x86/clang-r383902b/bin\"", + "-DLLVM_LIBDIR=\"prebuilts/clang/host/linux-x86/clang-r383902b/lib64\"", + "-DCLANGPP_BIN=\"prebuilts/clang/host/linux-x86/clang-r383902b/bin/clang++\"", + "-DAFL_REAL_LD=\"prebuilts/clang/host/linux-x86/clang-r383902b/bin/ld.lld\"", + "-DLLVM_LTO=1", ], srcs: [ "src/afl-cc.c", + "src/afl-common.c", + ], + + symlinks: [ + "afl-clang-fast", + "afl-clang-fast++", + "afl-clang-lto", + "afl-clang-lto++", ], } -cc_binary_host { - name: "afl-clang-fast++", - static_executable: true, +cc_library_static { + name: "afl-llvm-rt", + compile_multilib: "both", + vendor_available: true, + host_supported: true, + recovery_available: true, + sdk_version: "9", + + apex_available: [ + "com.android.adbd", + "com.android.appsearch", + "com.android.art", + "com.android.bluetooth.updatable", + "com.android.cellbroadcast", + "com.android.conscrypt", + "com.android.extservices", + "com.android.cronet", + "com.android.neuralnetworks", + "com.android.media", + "com.android.media.swcodec", + "com.android.mediaprovider", + "com.android.permission", + "com.android.runtime", + "com.android.resolv", + "com.android.tethering", + "com.android.wifi", + "com.android.sdkext", + "com.android.os.statsd", + "//any", + ], defaults: [ "afl-defaults", ], - cflags: [ - "-D__ANDROID__", - "-DAFL_PATH=\"out/host/linux-x86/lib64\"", + srcs: [ + "instrumentation/afl-compiler-rt.o.c", ], +} - srcs: [ - "src/afl-cc.c", +cc_defaults { + name: "afl-defaults-lto", + + include_dirs: [ + "prebuilts/clang/host/linux-x86/clang-r383902b/include", ], } +/*cc_library_host_shared { + name: "afl-llvm-lto-instrumentlist", + + defaults: [ + "afl-defaults", + "afl-defaults-lto", + ], + + srcs: [ + "instrumentation/afl-llvm-lto-instrumentlist.so.cc", + "instrumentation/afl-llvm-common.cc", + ], +}*/ + +/*cc_library_host_shared { + name: "afl-llvm-dict2file", + + defaults: [ + "afl-defaults", + "afl-defaults-lto", + ], + + srcs: [ + "instrumentation/afl-llvm-dict2file.so.cc", + "instrumentation/afl-llvm-common.cc", + ], + + shared_libs: [ + "libLLVM-11git", + ], +}*/ + +/*cc_library_host_shared { + name: "cmplog-routines-pass", + + defaults: [ + "afl-defaults", + "afl-defaults-lto", + ], + + srcs: [ + "instrumentation/cmplog-routines-pass.cc", + "instrumentation/afl-llvm-common.cc", + ], +}*/ + +/*cc_library_host_shared { + name: "cmplog-instructions-pass", + + defaults: [ + "afl-defaults", + "afl-defaults-lto", + ], + + srcs: [ + "instrumentation/cmplog-instructions-pass.cc", + "instrumentation/afl-llvm-common.cc", + ], +}*/ + +/*cc_library_host_shared { + name: "split-switches-pass", + + defaults: [ + "afl-defaults", + "afl-defaults-lto", + ], + + srcs: [ + "instrumentation/split-switches-pass.so.cc", + "instrumentation/afl-llvm-common.cc", + ], +}*/ + +/*cc_library_host_shared { + name: "compare-transform-pass", + + defaults: [ + "afl-defaults", + "afl-defaults-lto", + ], + + srcs: [ + "instrumentation/compare-transform-pass.so.cc", + "instrumentation/afl-llvm-common.cc", + ], +}*/ + +/*cc_library_host_shared { + name: "split-compares-pass", + + defaults: [ + "afl-defaults", + "afl-defaults-lto", + ], + + srcs: [ + "instrumentation/split-compares-pass.so.cc", + "instrumentation/afl-llvm-common.cc", + ], +}*/ + +/*cc_library_host_shared { + name: "libLLVMInsTrim", + + defaults: [ + "afl-defaults", + "afl-defaults-lto", + ], + + srcs: [ + "instrumentation/LLVMInsTrim.so.cc", + "instrumentation/MarkNodes.cc", + "instrumentation/afl-llvm-common.cc", + ], +}*/ + +/*cc_library_host_shared { + name: "afl-llvm-pass", + + defaults: [ + "afl-defaults", + "afl-defaults-lto", + ], + + srcs: [ + "instrumentation/afl-llvm-pass.so.cc", + "instrumentation/afl-llvm-common.cc", + ], +}*/ + +/*cc_library_host_shared { + name: "SanitizerCoveragePCGUARD", + + defaults: [ + "afl-defaults", + "afl-defaults-lto", + ], + + srcs: [ + "instrumentation/SanitizerCoveragePCGUARD.so.cc", + "instrumentation/afl-llvm-common.cc", + ], +}*/ + +/*cc_library_host_shared { + name: "SanitizerCoverageLTO", + + defaults: [ + "afl-defaults", + "afl-defaults-lto", + ], + + srcs: [ + "instrumentation/SanitizerCoverageLTO.so.cc", + "instrumentation/afl-llvm-common.cc", + ], +}*/ + +/*cc_library_host_shared { + name: "afl-llvm-lto-instrumentation", + + defaults: [ + "afl-defaults", + "afl-defaults-lto", + ], + + srcs: [ + "instrumentation/afl-llvm-lto-instrumentation.so.cc", + "instrumentation/afl-llvm-common.cc", + ], +}*/ + cc_library_static { - name: "afl-llvm-rt", + name: "afl-llvm-rt-lto", compile_multilib: "both", vendor_available: true, host_supported: true, recovery_available: true, sdk_version: "9", + apex_available: [ + "com.android.adbd", + "com.android.appsearch", + "com.android.art", + "com.android.bluetooth.updatable", + "com.android.cellbroadcast", + "com.android.conscrypt", + "com.android.extservices", + "com.android.cronet", + "com.android.neuralnetworks", + "com.android.media", + "com.android.media.swcodec", + "com.android.mediaprovider", + "com.android.permission", + "com.android.runtime", + "com.android.resolv", + "com.android.tethering", + "com.android.wifi", + "com.android.sdkext", + "com.android.os.statsd", + "//any", + ], + defaults: [ "afl-defaults", + "afl-defaults-lto", ], srcs: [ - "instrumentation/afl-llvm-rt.o.c", + "instrumentation/afl-llvm-rt-lto.o.c", ], } diff --git a/Android.mk b/Android.mk deleted file mode 120000 index 33ceb8f0..00000000 --- a/Android.mk +++ /dev/null @@ -1 +0,0 @@ -Makefile \ No newline at end of file diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 988a907d..6342c8b6 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -37,10 +37,6 @@ #define _FILE_OFFSET_BITS 64 #endif -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif - #include "config.h" #include "types.h" #include "debug.h" diff --git a/include/android-ashmem.h b/include/android-ashmem.h index 41d4d2da..6939e06d 100644 --- a/include/android-ashmem.h +++ b/include/android-ashmem.h @@ -1,112 +1,81 @@ -/* - american fuzzy lop++ - android shared memory compatibility layer - ---------------------------------------------------------------- - - Originally written by Michal Zalewski - - Now maintained by Marc Heuse , - Heiko Eißfeldt , - Andrea Fioraldi , - Dominik Maier - - Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This header re-defines the shared memory routines used by AFL++ - using the Andoid API. - - */ - +#ifdef __ANDROID__ #ifndef _ANDROID_ASHMEM_H #define _ANDROID_ASHMEM_H -#ifdef __ANDROID__ - - #include - #include - #include - #include - #include - - #if __ANDROID_API__ >= 26 - #define shmat bionic_shmat - #define shmctl bionic_shmctl - #define shmdt bionic_shmdt - #define shmget bionic_shmget - #endif - - #include - #undef shmat - #undef shmctl - #undef shmdt - #undef shmget - #include +#include +#include +#include +#include - #define ASHMEM_DEVICE "/dev/ashmem" +#if __ANDROID_API__ >= 26 +#define shmat bionic_shmat +#define shmctl bionic_shmctl +#define shmdt bionic_shmdt +#define shmget bionic_shmget +#endif +#include +#undef shmat +#undef shmctl +#undef shmdt +#undef shmget +#include -static inline int shmctl(int __shmid, int __cmd, struct shmid_ds *__buf) { +#define ASHMEM_DEVICE "/dev/ashmem" +int shmctl(int __shmid, int __cmd, struct shmid_ds *__buf) { int ret = 0; if (__cmd == IPC_RMID) { - - int length = ioctl(__shmid, ASHMEM_GET_SIZE, NULL); - struct ashmem_pin pin = {0, (unsigned int)length}; + int length = ioctl(__shmid, ASHMEM_GET_SIZE, NULL); + struct ashmem_pin pin = {0, length}; ret = ioctl(__shmid, ASHMEM_UNPIN, &pin); close(__shmid); - } return ret; - } -static inline int shmget(key_t __key, size_t __size, int __shmflg) { - - (void)__shmflg; - int fd, ret; +int shmget(key_t __key, size_t __size, int __shmflg) { + (void) __shmflg; + int fd, ret; char ourkey[11]; fd = open(ASHMEM_DEVICE, O_RDWR); - if (fd < 0) return fd; + if (fd < 0) + return fd; sprintf(ourkey, "%d", __key); ret = ioctl(fd, ASHMEM_SET_NAME, ourkey); - if (ret < 0) goto error; + if (ret < 0) + goto error; ret = ioctl(fd, ASHMEM_SET_SIZE, __size); - if (ret < 0) goto error; + if (ret < 0) + goto error; return fd; error: close(fd); return ret; - } -static inline void *shmat(int __shmid, const void *__shmaddr, int __shmflg) { - - (void)__shmflg; - int size; +void *shmat(int __shmid, const void *__shmaddr, int __shmflg) { + (void) __shmflg; + int size; void *ptr; size = ioctl(__shmid, ASHMEM_GET_SIZE, NULL); - if (size < 0) { return NULL; } + if (size < 0) { + return NULL; + } ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, __shmid, 0); - if (ptr == MAP_FAILED) { return NULL; } + if (ptr == MAP_FAILED) { + return NULL; + } return ptr; - } -#endif /* __ANDROID__ */ - -#endif - +#endif /* !_ANDROID_ASHMEM_H */ +#endif /* !__ANDROID__ */ diff --git a/src/afl-analyze.c b/src/afl-analyze.c index 8fc4434a..0af489fe 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -26,9 +26,6 @@ #define AFL_MAIN -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif #include "config.h" #include "types.h" #include "debug.h" diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index e86f2aeb..e67bace9 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -58,7 +58,11 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { for (i = 0; i < argc; ++i) { if (i) fprintf(f, " "); +#ifdef __ANDROID__ + if (memchr(argv[i], '\'', sizeof(argv[i]))) { +#else if (index(argv[i], '\'')) { +#endif fprintf(f, "'"); for (j = 0; j < strlen(argv[i]); j++) diff --git a/src/afl-gotcpu.c b/src/afl-gotcpu.c index 1aea3e40..ac002a93 100644 --- a/src/afl-gotcpu.c +++ b/src/afl-gotcpu.c @@ -35,9 +35,6 @@ #define _GNU_SOURCE #endif -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif #include #include #include diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 5c9d38e0..6d95fc1d 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -31,9 +31,6 @@ #define AFL_MAIN -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif #include "config.h" #include "types.h" #include "debug.h" diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 6e2d7708..5fd60cd2 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -29,10 +29,6 @@ #define AFL_MAIN -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif - #include "config.h" #include "types.h" #include "debug.h" diff --git a/utils/afl_network_proxy/afl-network-server.c b/utils/afl_network_proxy/afl-network-server.c index 513dc8f2..fe225416 100644 --- a/utils/afl_network_proxy/afl-network-server.c +++ b/utils/afl_network_proxy/afl-network-server.c @@ -24,10 +24,6 @@ #define AFL_MAIN -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif - #include "config.h" #include "types.h" #include "debug.h" -- cgit 1.4.1 From ac1117ffaeec0bd3c593063a05d8aa000d162d47 Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Tue, 19 Jan 2021 09:44:59 +0800 Subject: android: Fix runtime for mutator --- Android.bp | 4 ++-- src/afl-cc.c | 14 ++++++++++---- src/afl-fuzz-mutators.c | 5 ++++- 3 files changed, 16 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/Android.bp b/Android.bp index bd23d1d1..5d6f0433 100644 --- a/Android.bp +++ b/Android.bp @@ -135,6 +135,8 @@ cc_binary_host { "-DCLANGPP_BIN=\"prebuilts/clang/host/linux-x86/clang-r383902b/bin/clang++\"", "-DAFL_REAL_LD=\"prebuilts/clang/host/linux-x86/clang-r383902b/bin/ld.lld\"", "-DLLVM_LTO=1", + "-DLLVM_MAJOR=11", + "-DLLVM_MINOR=2", ], srcs: [ @@ -145,8 +147,6 @@ cc_binary_host { symlinks: [ "afl-clang-fast", "afl-clang-fast++", - "afl-clang-lto", - "afl-clang-lto++", ], } diff --git a/src/afl-cc.c b/src/afl-cc.c index 1379488e..f3dfd49f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -586,6 +586,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { #if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) +#ifdef __ANDROID__ + cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; +#else if (have_instr_list) { if (!be_quiet) @@ -605,6 +608,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } +#endif #else #if LLVM_MAJOR >= 4 if (!be_quiet) @@ -1034,6 +1038,10 @@ int main(int argc, char **argv, char **envp) { #endif +#ifdef __ANDROID__ + have_llvm = 1; +#endif + if ((ptr = find_object("afl-gcc-pass.so", argv[0])) != NULL) { have_gcc_plugin = 1; @@ -1807,11 +1815,8 @@ int main(int argc, char **argv, char **envp) { if (!be_quiet && cmplog_mode) printf("CmpLog mode by \n"); -#ifdef __ANDROID__ - ptr = find_object("afl-compiler-rt.so", argv[0]); -#else +#ifndef __ANDROID__ ptr = find_object("afl-compiler-rt.o", argv[0]); -#endif if (!ptr) { @@ -1824,6 +1829,7 @@ int main(int argc, char **argv, char **envp) { if (debug) { DEBUGF("rt=%s obj_path=%s\n", ptr, obj_path); } ck_free(ptr); +#endif edit_params(argc, argv, envp); diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index 089707b9..80df6d08 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -141,7 +141,10 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { struct custom_mutator *mutator = ck_alloc(sizeof(struct custom_mutator)); mutator->name = fn; - mutator->name_short = strrchr(fn, '/') + 1; + if (memchr(fn, '/', strlen(fn))) + mutator->name_short = strrchr(fn, '/') + 1; + else + mutator->name_short = strdup(fn); ACTF("Loading custom mutator library from '%s'...", fn); dh = dlopen(fn, RTLD_NOW); -- cgit 1.4.1 From 9dff3495d54c0bd3da59ef43ca25df06c6d9f2c2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 20 Jan 2021 10:01:34 +0100 Subject: better cmp map fsrv fix --- src/afl-forkserver.c | 4 +++- src/afl-fuzz-cmplog.c | 2 ++ src/afl-fuzz.c | 14 -------------- 3 files changed, 5 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 39f044f2..c1b3d02f 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -58,7 +58,9 @@ static list_t fsrv_list = {.element_prealloc_count = 0}; static void fsrv_exec_child(afl_forkserver_t *fsrv, char **argv) { - if (fsrv->qemu_mode) setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); + if (fsrv->qemu_mode) { setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); } + + unsetenv(CMPLOG_SHM_ENV_VAR); // we do not want that in non-cmplog fsrv execv(fsrv->target_path, argv); diff --git a/src/afl-fuzz-cmplog.c b/src/afl-fuzz-cmplog.c index 8ffc6e1b..27c6c413 100644 --- a/src/afl-fuzz-cmplog.c +++ b/src/afl-fuzz-cmplog.c @@ -33,6 +33,8 @@ void cmplog_exec_child(afl_forkserver_t *fsrv, char **argv) { setenv("___AFL_EINS_ZWEI_POLIZEI___", "1", 1); + if (fsrv->qemu_mode) { setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); } + if (!fsrv->qemu_mode && argv[0] != fsrv->cmplog_binary) { argv[0] = fsrv->cmplog_binary; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 0f76e8a3..88c40ee8 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1515,21 +1515,7 @@ int main(int argc, char **argv_orig, char **envp) { } - u8 *save_env = NULL; - if (afl->cmplog_binary) { - - save_env = ck_strdup(getenv(CMPLOG_SHM_ENV_VAR)); - unsetenv(CMPLOG_SHM_ENV_VAR); // normal forkserver should not have this - - } - perform_dry_run(afl); - if (save_env) { - - setenv(CMPLOG_SHM_ENV_VAR, save_env, 1); // needed for at_exit() - ck_free(save_env); - - } /* if (!user_set_cache && afl->q_testcase_max_cache_size) { -- cgit 1.4.1 From f380487bb49a66b1fac513cad344f1be5df10959 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 20 Jan 2021 13:51:57 +0100 Subject: wip --- src/afl-fuzz-redqueen.c | 518 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 452 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index bf41863e..7ac0290a 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -517,6 +517,149 @@ static int strntoull(const char *str, size_t sz, char **end, int base, } +static u8 hex_table_up[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; +static u8 hex_table_low[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; +static u8 hex_table[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, + 0, 0, 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15}; + +// tests 2 bytes at location +static int is_hex(const char *str) { + + u32 i; + + for (i = 0; i < 2; i++) { + + switch (str[i]) { + + case '0' ... '9': + case 'A' ... 'F': + case 'a' ... 'f': + break; + default: + return 0; + + } + + } + + return 1; + +} + +// tests 4 bytes at location +static int is_base64(const char *str) { + + u32 i; + + for (i = 0; i < 4; i++) { + + switch (str[i]) { + + case '0' ... '9': + case 'A' ... 'Z': + case 'a' ... 'z': + case '+': + case '/': + break; + default: + return 0; + + } + + } + + return 1; + +} + +static u8 base64_encode_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static u8 base64_decode_table[] = { + 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; + +static u32 from_base64(u8 *src, u8 *dst, u32 dst_len) { + + u32 i, j, v; + u32 len = ((dst_len / 3) << 2); + u32 ret = 0; + + for (i = 0, j = 0; i < len; i += 4, j += 3) { + + v = base64_decode_table[src[i] - 43]; + v = (v << 6) | base64_decode_table[src[i + 1] - 43]; + v = src[i + 2] == '=' ? v << 6 + : (v << 6) | base64_decode_table[src[i + 2] - 43]; + v = src[i + 3] == '=' ? v << 6 + : (v << 6) | base64_decode_table[src[i + 3] - 43]; + + dst[j] = (v >> 16) & 0xFF; + ++ret; + + if (src[i + 2] != '=') { + + dst[j + 1] = (v >> 8) & 0xFF; + ++ret; + + } + + if (src[i + 3] != '=') { + + dst[j + 2] = v & 0xFF; + ++ret; + + } + + } + + return ret; + +} + +static void to_base64(u8 *src, u8 *dst, u32 dst_len) { + + u32 i, j, v; + u32 len = (dst_len >> 2) * 3; + + for (i = 0, j = 0; i < len; i += 3, j += 4) { + + v = src[i]; + v = i + 1 < len ? v << 8 | src[i + 1] : v << 8; + v = i + 2 < len ? v << 8 | src[i + 2] : v << 8; + + dst[j] = base64_encode_table[(v >> 18) & 0x3F]; + dst[j + 1] = base64_encode_table[(v >> 12) & 0x3F]; + if (i + 1 < len) { + + dst[j + 2] = base64_encode_table[(v >> 6) & 0x3F]; + + } else { + + dst[j + 2] = '='; + + } + + if (i + 2 < len) { + + dst[j + 3] = base64_encode_table[v & 0x3F]; + + } else { + + dst[j + 3] = '='; + + } + + } + +} + #endif static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, @@ -525,29 +668,6 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, u8 do_reverse, u8 lvl, u8 *status) { - // (void)(changed_val); // TODO - // we can use the information in changed_val to see if there is a - // computable i2s transformation. - // if (pattern != o_pattern && repl != changed_val) { - - // u64 in_diff = pattern - o_pattern, out_diff = repl - changed_val; - // if (in_diff != out_diff) { - - // switch(in_diff) { - - // detect uppercase <-> lowercase, base64, hex encoding, etc.: - // repl = reverse_transform(TYPE, pattern); - // } - // } - // } - // not 100% but would have a chance to be detected - - // fprintf(stderr, - // "Encode: %llx->%llx into %llx(<-%llx) at idx=%u " - // "taint_len=%u shape=%u attr=%u\n", - // o_pattern, pattern, repl, changed_val, idx, taint_len, - // h->shape + 1, attr); - u64 *buf_64 = (u64 *)&buf[idx]; u32 *buf_32 = (u32 *)&buf[idx]; u16 *buf_16 = (u16 *)&buf[idx]; @@ -559,6 +679,12 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u32 its_len = MIN(len - idx, taint_len); + // fprintf(stderr, + // "Encode: %llx->%llx into %llx(<-%llx) at idx=%u " + // "taint_len=%u shape=%u attr=%u\n", + // o_pattern, pattern, repl, changed_val, idx, taint_len, + // h->shape + 1, attr); + #ifdef TRANSFORM // reverse atoi()/strnu?toll() is expensive, so we only to it in lvl 3 if (lvl & LVL3) { @@ -1581,71 +1707,215 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, #ifndef COMBINE (void)(cbuf); #endif +#ifndef TRANSFORM + (void)(changed_val); +#endif - u32 i = 0; + u8 save[40]; + u32 saved_idx = idx, pre, from = 0, to = 0, i; u32 its_len = MIN((u32)32, len - idx); its_len = MIN(its_len, taint_len); - u8 save[32]; - memcpy(save, &buf[idx], its_len); + u32 saved_its_len = its_len; - if (lvl == LVL1) { + if (lvl & LVL3) { - for (i = 0; i < its_len; ++i) { + u32 max_to = MIN(4U, idx); + if (!(lvl & LVL1) && max_to) { from = 1; } + to = max_to; - if ((pattern[i] != buf[idx + i] && o_pattern[i] != orig_buf[idx + i]) || - *status == 1) { + } - break; + memcpy(save, &buf[saved_idx - to], its_len + to); - } +#ifdef _DEBUG + fprintf(stderr, "RTN TRANSFORM idx=%u lvl=%02x", idx, lvl); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", orig_buf[idx + j]); + fprintf(stderr, " -> "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", o_pattern[j]); + fprintf(stderr, " <= "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", repl[j]); + fprintf(stderr, "\n"); + fprintf(stderr, " "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", buf[idx + j]); + fprintf(stderr, " -> "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", pattern[j]); + fprintf(stderr, " <= "); + for (j = 0; j < 8; j++) + fprintf(stderr, "%02x", changed_val[j]); + fprintf(stderr, "\n"); +#endif - buf[idx + i] = repl[i]; + // Try to match the replace value up to 4 bytes before the current idx. + // This allows matching of eg.: + // if (memcmp(user_val, "TEST") == 0) + // if (memcmp(user_val, "TEST-VALUE") == 0) ... + // We only do this in lvl 3, otherwise we only do direct matching - if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + for (pre = from; pre <= to; pre++) { + + if (*status != 1 && (!pre || !memcmp(buf + saved_idx - pre, repl, pre))) { + + idx = saved_idx - pre; + its_len = saved_its_len + pre; + + for (i = 0; i < its_len; ++i) { + + if ((pattern[i] != buf[idx + i] && o_pattern[i] != orig_buf[idx + i]) || + *status == 1) { + + break; + + } + + buf[idx + i] = repl[i]; + + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } #ifdef COMBINE - if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); } + if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); } #endif + } + + memcpy(&buf[idx], save + to - pre, i); + } } +#ifdef TRANSFORM + if (*status == 1) return 0; if (lvl & LVL3) { - u8 toupper = 0, tolower = 0, xor = 0, arith = 0; - u8 xor_val[32], arith_val[32]; u32 j; + u32 toupper = 0, tolower = 0, xor = 0, arith = 0, tohex = 0, tob64 = 0; + u32 fromhex = 0, fromb64 = 0; + u32 from_0 = 0, from_x = 0, from_X = 0, from_slash = 0, from_lf = 0, + from_cr = 0, from_up = 0; + u32 to_0 = 0, to_x = 0, to_slash = 0, to_lf = 0, to_cr = 0, to_up = 0; + u8 xor_val[32], arith_val[32], tmp[48]; + + idx = saved_idx; + its_len = saved_its_len; + + memcpy(save, &buf[idx], its_len); -#ifdef _DEBUG - fprintf(stderr, "RTN TRANSFORM idx=%u ", idx); - for (j = 0; j < 8; j++) - fprintf(stderr, "%02x", orig_buf[idx + j]); - fprintf(stderr, " -> "); - for (j = 0; j < 8; j++) - fprintf(stderr, "%02x", o_pattern[j]); - fprintf(stderr, " <= "); - for (j = 0; j < 8; j++) - fprintf(stderr, "%02x", repl[j]); - fprintf(stderr, "\n"); - fprintf(stderr, " "); - for (j = 0; j < 8; j++) - fprintf(stderr, "%02x", buf[idx + j]); - fprintf(stderr, " -> "); - for (j = 0; j < 8; j++) - fprintf(stderr, "%02x", pattern[j]); - fprintf(stderr, " <= "); - for (j = 0; j < 8; j++) - fprintf(stderr, "%02x", changed_val[j]); - fprintf(stderr, "\n"); -#endif for (i = 0; i < its_len; ++i) { xor_val[i] = pattern[i] ^ buf[idx + i]; arith_val[i] = pattern[i] - buf[idx + i]; + if (i == 0) { + + if (orig_buf[idx] == '0') { + + from_0 = 1; + + } else if (orig_buf[idx] == '\\') { + + from_slash = 1; + + } + + if (repl[0] == '0') { + + to_0 = 1; + + } else if (repl[0] == '\\') { + + to_slash = 1; + + } + + } else if (i == 1) { + + if (orig_buf[idx + 1] == 'x') { + + from_x = 1; + + } else if (orig_buf[idx + 1] == 'X') { + + from_X = from_x = 1; + + } + + if (repl[1] == 'x' || repl[1] == 'X') { to_x = 1; } + + } else { + + if (orig_buf[idx + i] == '\n') { ++from_lf; } + if (orig_buf[idx + i] == '\r') { ++from_cr; } + if (repl[i] == '\n') { ++to_lf; } + if (repl[i] == '\r') { ++to_cr; } + + } + + if (i) { + + if (!(i % 2)) { + + if (i < 16 && is_hex(repl + (i << 1))) { + + tohex += 2; + + if (!to_up) { + + if (repl[i] >= 'A' && repl[i] <= 'F') + to_up = 1; + else if (repl[i] >= 'a' && repl[i] <= 'f') + to_up = 2; + if (repl[i - 1] >= 'A' && repl[i - 1] <= 'F') + to_up = 1; + else if (repl[i - 1] >= 'a' && repl[i - 1] <= 'f') + to_up = 2; + + } + + } + + if (len > idx + i && is_hex(orig_buf + idx + i)) { + + fromhex += 2; + + if (!from_up) { + + if (orig_buf[idx + i] >= 'A' && orig_buf[idx + i] <= 'F') + from_up = 1; + else if (orig_buf[idx + i] >= 'a' && orig_buf[idx + i] <= 'f') + from_up = 2; + if (orig_buf[idx + i - 1] >= 'A' && orig_buf[idx + i - 1] <= 'F') + from_up = 1; + else if (orig_buf[idx + i - 1] >= 'a' && + orig_buf[idx + i - 1] <= 'f') + from_up = 2; + + } + + } + + } + + if (!(i % 3) && i + to_lf + to_cr < 24) { + + if (is_base64(repl + i + to_lf + to_cr)) tob64 += 3; + + } + + if (!(i % 4) && i < 24) { + + if (is_base64(orig_buf + idx + i)) fromb64 += 4; + + } + + } + if ((o_pattern[i] ^ orig_buf[idx + i]) == xor_val[i] && xor_val[i]) { ++xor; @@ -1672,10 +1942,124 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } -#ifdef _DEBUG - fprintf(stderr, "RTN loop=%u %u %u %u %u (%u %u)\n", i, xor, arith, - tolower, toupper, arith_val[i], xor_val[i]); -#endif + // #ifdef _DEBUG + fprintf( + stderr, + "RTN loop=%u xor=%u arith=%u tolower=%u toupper=%u tohex=%u tob64=%u " + "fromhex=%u fromb64=%u to_0=%u to_slash=%u to_x=%u to_lf=%u to_cr=%u " + "from_0=%u from_slash=%u from_x=%u from_lf=%u from_cr=%u\n", + i, xor, arith, tolower, toupper, tohex, tob64, fromhex, fromb64, to_0, + to_slash, to_x, to_lf, to_cr, from_0, from_slash, from_x, from_lf, + from_cr); + // #endif + + // input is base64 and converted to binary? convert repl to base64! + if (i && !(i % 4) && i < 24 && fromb64 > i) { + + to_base64(repl, tmp, i); + memcpy(buf + idx, tmp, i); + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + fprintf(stderr, "RTN ATTEMPT fromb64 %u result %u\n", fromb64, *status); + + } + + // input is converted to base64? decode repl with base64! + if (i && !(i % 3) && i < 24 && tob64 > i) { + + u32 olen = from_base64(repl, tmp, i); + memcpy(buf + idx, tmp, olen); + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + fprintf(stderr, "RTN ATTEMPT tob64 %u result %u\n", tob64, *status); + + } + + // input is converted to hex? convert repl to binary! + if (i && !(i % 2) && i < 16 && tohex && tohex > i) { + + u32 off; + if (to_slash + to_x + to_0 == 2) { + + off = 2; + + } else { + + off = 0; + + } + + for (j = 0; j < i / 2; j++) + tmp[j] = (hex_table[repl[off + (j << 1)] - '0'] << 4) + + hex_table[repl[off + (j << 1) + 1] - '0']; + + memcpy(buf + idx, tmp, i / 2); + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + fprintf(stderr, "RTN ATTEMPT tohex %u result %u\n", tohex, *status); + + } + + // input is hex and converted to binary? convert repl to hex! + if (i && !(i % 2) && i < 16 && fromhex && + fromhex + from_slash + from_x + from_0 > i) { + + u8 off = 0; + if (from_slash && from_x) { + + tmp[0] = '\\'; + if (from_X) { + + tmp[1] = 'X'; + + } else { + + tmp[1] = 'x'; + + } + + off = 2; + + } else if (from_0 && from_x) { + + tmp[0] = '0'; + if (from_X) { + + tmp[1] = 'X'; + + } else { + + tmp[1] = 'x'; + + } + + off = 2; + + } + + if (to_up == 1) { + + for (j = 0; j <= i; j++) { + + tmp[off + (j << 1)] = hex_table_up[repl[j] >> 4]; + tmp[off + (j << 1) + 1] = hex_table_up[repl[j] % 16]; + + } + + } else { + + for (j = 0; j <= i; j++) { + + tmp[off + (j << 1)] = hex_table_low[repl[j] >> 4]; + tmp[off + (j << 1) + 1] = hex_table_low[repl[j] % 16]; + + } + + } + + memcpy(buf + idx, tmp, (i << 1) + off); + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + fprintf(stderr, "RTN ATTEMPT fromhex %u result %u\n", fromhex, *status); + memcpy(buf + idx, save, (i << 1) + off); + + } if (xor > i) { @@ -1715,9 +2099,9 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } -#ifdef COMBINE + #ifdef COMBINE if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); } -#endif + #endif if ((i >= xor&&i >= arith &&i >= tolower &&i >= toupper) || repl[i] != changed_val[i] || *status == 1) { @@ -1728,9 +2112,11 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } + memcpy(&buf[idx], save, i); + } - memcpy(&buf[idx], save, i); +#endif return 0; -- cgit 1.4.1 From d20a50a41307a7af346e69c93c8b30a3f369a2d4 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 20 Jan 2021 20:59:17 +0100 Subject: hex en/decode works now --- instrumentation/afl-compiler-rt.o.c | 18 ++++---- src/afl-forkserver.c | 8 +++- src/afl-fuzz-redqueen.c | 85 ++++++++++++++++++++----------------- 3 files changed, 60 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index a290f110..de01cb69 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1546,15 +1546,15 @@ static int area_is_mapped(void *ptr, size_t len) { void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { /* - u32 i; - if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; - fprintf(stderr, "rtn arg0="); - for (i = 0; i < 8; i++) - fprintf(stderr, "%02x", ptr1[i]); - fprintf(stderr, " arg1="); - for (i = 0; i < 8; i++) - fprintf(stderr, "%02x", ptr2[i]); - fprintf(stderr, "\n"); + u32 i; + if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; + fprintf(stderr, "rtn arg0="); + for (i = 0; i < 8; i++) + fprintf(stderr, "%02x", ptr1[i]); + fprintf(stderr, " arg1="); + for (i = 0; i < 8; i++) + fprintf(stderr, "%02x", ptr2[i]); + fprintf(stderr, "\n"); */ if (unlikely(!__afl_cmp_map)) return; diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index c1b3d02f..50e4139b 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -59,8 +59,6 @@ static list_t fsrv_list = {.element_prealloc_count = 0}; static void fsrv_exec_child(afl_forkserver_t *fsrv, char **argv) { if (fsrv->qemu_mode) { setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); } - - unsetenv(CMPLOG_SHM_ENV_VAR); // we do not want that in non-cmplog fsrv execv(fsrv->target_path, argv); @@ -398,6 +396,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, struct rlimit r; + if (!fsrv->cmplog_binary && fsrv->qemu_mode == false) { + + unsetenv(CMPLOG_SHM_ENV_VAR); // we do not want that in non-cmplog fsrv + + } + /* Umpf. On OpenBSD, the default fd limit for root users is set to soft 128. Let's try to fix that... */ if (!getrlimit(RLIMIT_NOFILE, &r) && r.rlim_cur < FORKSRV_FD + 2) { diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 7ac0290a..96a27f66 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -30,9 +30,9 @@ //#define _DEBUG #define COMBINE -//#define CMPLOG_INTROSPECTION +#define CMPLOG_INTROSPECTION //#define ARITHMETIC_LESSER_GREATER -//#define TRANSFORM +#define TRANSFORM // CMP attribute enum enum { @@ -579,6 +579,7 @@ static int is_base64(const char *str) { static u8 base64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static u8 base64_decode_table[] = { + 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, @@ -1712,7 +1713,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, #endif u8 save[40]; - u32 saved_idx = idx, pre, from = 0, to = 0, i; + u32 saved_idx = idx, pre, from = 0, to = 0, i, j; u32 its_len = MIN((u32)32, len - idx); its_len = MIN(its_len, taint_len); u32 saved_its_len = its_len; @@ -1728,7 +1729,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, memcpy(save, &buf[saved_idx - to], its_len + to); #ifdef _DEBUG - fprintf(stderr, "RTN TRANSFORM idx=%u lvl=%02x", idx, lvl); + fprintf(stderr, "RTN T idx=%u lvl=%02x ", idx, lvl); for (j = 0; j < 8; j++) fprintf(stderr, "%02x", orig_buf[idx + j]); fprintf(stderr, " -> "); @@ -1738,7 +1739,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, for (j = 0; j < 8; j++) fprintf(stderr, "%02x", repl[j]); fprintf(stderr, "\n"); - fprintf(stderr, " "); + fprintf(stderr, " "); for (j = 0; j < 8; j++) fprintf(stderr, "%02x", buf[idx + j]); fprintf(stderr, " -> "); @@ -1794,7 +1795,6 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, if (lvl & LVL3) { - u32 j; u32 toupper = 0, tolower = 0, xor = 0, arith = 0, tohex = 0, tob64 = 0; u32 fromhex = 0, fromb64 = 0; u32 from_0 = 0, from_x = 0, from_X = 0, from_slash = 0, from_lf = 0, @@ -1857,28 +1857,28 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - if (i) { + if (i < 16 && is_hex(repl + (i << 1))) { - if (!(i % 2)) { + ++tohex; - if (i < 16 && is_hex(repl + (i << 1))) { + if (!to_up) { - tohex += 2; + if (repl[i << 1] >= 'A' && repl[i << 1] <= 'F') + to_up = 1; + else if (repl[i << 1] >= 'a' && repl[i << 1] <= 'f') + to_up = 2; + if (repl[(i << 1) + 1] >= 'A' && repl[(i << 1) + 1] <= 'F') + to_up = 1; + else if (repl[(i << 1) + 1] >= 'a' && repl[(i << 1) + 1] <= 'f') + to_up = 2; - if (!to_up) { + } - if (repl[i] >= 'A' && repl[i] <= 'F') - to_up = 1; - else if (repl[i] >= 'a' && repl[i] <= 'f') - to_up = 2; - if (repl[i - 1] >= 'A' && repl[i - 1] <= 'F') - to_up = 1; - else if (repl[i - 1] >= 'a' && repl[i - 1] <= 'f') - to_up = 2; + } - } + if (i) { - } + if ((i % 2)) { if (len > idx + i && is_hex(orig_buf + idx + i)) { @@ -1902,13 +1902,13 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - if (!(i % 3) && i + to_lf + to_cr < 24) { + if (i % 3 == 2 && i + to_lf + to_cr < 24) { if (is_base64(repl + i + to_lf + to_cr)) tob64 += 3; } - if (!(i % 4) && i < 24) { + if (i % 4 == 3 && i < 24) { if (is_base64(orig_buf + idx + i)) fromb64 += 4; @@ -1956,25 +1956,26 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, // input is base64 and converted to binary? convert repl to base64! if (i && !(i % 4) && i < 24 && fromb64 > i) { - to_base64(repl, tmp, i); - memcpy(buf + idx, tmp, i); + to_base64(repl, tmp, i + 1); + memcpy(buf + idx, tmp, i + 1); if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - fprintf(stderr, "RTN ATTEMPT fromb64 %u result %u\n", fromb64, *status); + // fprintf(stderr, "RTN ATTEMPT fromb64 %u result %u\n", fromb64, + // *status); } // input is converted to base64? decode repl with base64! if (i && !(i % 3) && i < 24 && tob64 > i) { - u32 olen = from_base64(repl, tmp, i); + u32 olen = from_base64(repl, tmp, i + 1); memcpy(buf + idx, tmp, olen); if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - fprintf(stderr, "RTN ATTEMPT tob64 %u result %u\n", tob64, *status); + // fprintf(stderr, "RTN ATTEMPT tob64 %u result %u\n", tob64, *status); } // input is converted to hex? convert repl to binary! - if (i && !(i % 2) && i < 16 && tohex && tohex > i) { + if (i < 16 && tohex > i) { u32 off; if (to_slash + to_x + to_0 == 2) { @@ -1987,18 +1988,18 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - for (j = 0; j < i / 2; j++) + for (j = 0; j <= i; j++) tmp[j] = (hex_table[repl[off + (j << 1)] - '0'] << 4) + hex_table[repl[off + (j << 1) + 1] - '0']; - memcpy(buf + idx, tmp, i / 2); + memcpy(buf + idx, tmp, i + 1); if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - fprintf(stderr, "RTN ATTEMPT tohex %u result %u\n", tohex, *status); + // fprintf(stderr, "RTN ATTEMPT tohex %u result %u\n", tohex, *status); } // input is hex and converted to binary? convert repl to hex! - if (i && !(i % 2) && i < 16 && fromhex && + if (i && (i % 2) && i < 16 && fromhex && fromhex + from_slash + from_x + from_0 > i) { u8 off = 0; @@ -2036,7 +2037,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, if (to_up == 1) { - for (j = 0; j <= i; j++) { + for (j = 0; j <= (i >> 1); j++) { tmp[off + (j << 1)] = hex_table_up[repl[j] >> 4]; tmp[off + (j << 1) + 1] = hex_table_up[repl[j] % 16]; @@ -2045,7 +2046,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } else { - for (j = 0; j <= i; j++) { + for (j = 0; j <= (i >> 1); j++) { tmp[off + (j << 1)] = hex_table_low[repl[j] >> 4]; tmp[off + (j << 1) + 1] = hex_table_low[repl[j] % 16]; @@ -2054,10 +2055,11 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - memcpy(buf + idx, tmp, (i << 1) + off); + memcpy(buf + idx, tmp, i + 1 + off); if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - fprintf(stderr, "RTN ATTEMPT fromhex %u result %u\n", fromhex, *status); - memcpy(buf + idx, save, (i << 1) + off); + // fprintf(stderr, "RTN ATTEMPT fromhex %u result %u\n", fromhex, + // *status); + memcpy(buf + idx + i, save + i, i + 1 + off); } @@ -2100,10 +2102,13 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } #ifdef COMBINE - if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); } + if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i + 1); } #endif - if ((i >= xor&&i >= arith &&i >= tolower &&i >= toupper) || + if ((i >= 7 && + (i >= xor&&i >= arith &&i >= tolower &&i >= toupper &&i > tohex &&i > + (1 + fromhex + from_0 + from_x + from_slash) && + i > tob64 + 3 && i > fromb64 + 3)) || repl[i] != changed_val[i] || *status == 1) { break; -- cgit 1.4.1 From cf5fee7c526ff104cc44b0029aad4395342fa4f2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 20 Jan 2021 21:03:55 +0100 Subject: remove debug output --- src/afl-fuzz-redqueen.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 96a27f66..f958bb71 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -819,7 +819,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - if (*status == 1) { fprintf(stderr, "FOUND!\n"); } + // if (*status == 1) { fprintf(stderr, "FOUND!\n"); } } @@ -847,7 +847,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - if (*status == 1) { fprintf(stderr, "FOUND!\n"); } + // if (*status == 1) { fprintf(stderr, "FOUND!\n"); } } @@ -894,7 +894,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - if (*status == 1) { fprintf(stderr, "FOUND!\n"); } + // if (*status == 1) { fprintf(stderr, "FOUND!\n"); } } @@ -941,7 +941,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - if (*status == 1) { fprintf(stderr, "FOUND!\n"); } + // if (*status == 1) { fprintf(stderr, "FOUND!\n"); } } @@ -1942,7 +1942,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - // #ifdef _DEBUG + #ifdef _DEBUG fprintf( stderr, "RTN loop=%u xor=%u arith=%u tolower=%u toupper=%u tohex=%u tob64=%u " @@ -1951,7 +1951,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, i, xor, arith, tolower, toupper, tohex, tob64, fromhex, fromb64, to_0, to_slash, to_x, to_lf, to_cr, from_0, from_slash, from_x, from_lf, from_cr); - // #endif + #endif // input is base64 and converted to binary? convert repl to base64! if (i && !(i % 4) && i < 24 && fromb64 > i) { -- cgit 1.4.1 From f7c93d741c09f5049e1da4b9d83acabbde104c46 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 21 Jan 2021 03:52:46 +0100 Subject: base64 solving done --- src/afl-fuzz-redqueen.c | 79 +++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index f958bb71..8ffd39da 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -30,7 +30,7 @@ //#define _DEBUG #define COMBINE -#define CMPLOG_INTROSPECTION +//#define CMPLOG_INTROSPECTION //#define ARITHMETIC_LESSER_GREATER #define TRANSFORM @@ -564,6 +564,7 @@ static int is_base64(const char *str) { case 'a' ... 'z': case '+': case '/': + case '=': break; default: return 0; @@ -1797,9 +1798,8 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, u32 toupper = 0, tolower = 0, xor = 0, arith = 0, tohex = 0, tob64 = 0; u32 fromhex = 0, fromb64 = 0; - u32 from_0 = 0, from_x = 0, from_X = 0, from_slash = 0, from_lf = 0, - from_cr = 0, from_up = 0; - u32 to_0 = 0, to_x = 0, to_slash = 0, to_lf = 0, to_cr = 0, to_up = 0; + u32 from_0 = 0, from_x = 0, from_X = 0, from_slash = 0, from_up = 0; + u32 to_0 = 0, to_x = 0, to_slash = 0, to_up = 0; u8 xor_val[32], arith_val[32], tmp[48]; idx = saved_idx; @@ -1848,13 +1848,6 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, if (repl[1] == 'x' || repl[1] == 'X') { to_x = 1; } - } else { - - if (orig_buf[idx + i] == '\n') { ++from_lf; } - if (orig_buf[idx + i] == '\r') { ++from_cr; } - if (repl[i] == '\n') { ++to_lf; } - if (repl[i] == '\r') { ++to_cr; } - } if (i < 16 && is_hex(repl + (i << 1))) { @@ -1876,43 +1869,39 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - if (i) { - - if ((i % 2)) { - - if (len > idx + i && is_hex(orig_buf + idx + i)) { + if ((i % 2)) { - fromhex += 2; + if (len > idx + i && is_hex(orig_buf + idx + i)) { - if (!from_up) { + fromhex += 2; - if (orig_buf[idx + i] >= 'A' && orig_buf[idx + i] <= 'F') - from_up = 1; - else if (orig_buf[idx + i] >= 'a' && orig_buf[idx + i] <= 'f') - from_up = 2; - if (orig_buf[idx + i - 1] >= 'A' && orig_buf[idx + i - 1] <= 'F') - from_up = 1; - else if (orig_buf[idx + i - 1] >= 'a' && - orig_buf[idx + i - 1] <= 'f') - from_up = 2; + if (!from_up) { - } + if (orig_buf[idx + i] >= 'A' && orig_buf[idx + i] <= 'F') + from_up = 1; + else if (orig_buf[idx + i] >= 'a' && orig_buf[idx + i] <= 'f') + from_up = 2; + if (orig_buf[idx + i - 1] >= 'A' && orig_buf[idx + i - 1] <= 'F') + from_up = 1; + else if (orig_buf[idx + i - 1] >= 'a' && + orig_buf[idx + i - 1] <= 'f') + from_up = 2; } } - if (i % 3 == 2 && i + to_lf + to_cr < 24) { + } - if (is_base64(repl + i + to_lf + to_cr)) tob64 += 3; + if (i % 3 == 2 && i < 24) { - } + if (is_base64(repl + ((i / 3) << 2))) tob64 += 3; - if (i % 4 == 3 && i < 24) { + } - if (is_base64(orig_buf + idx + i)) fromb64 += 4; + if (i % 4 == 3 && i < 24) { - } + if (is_base64(orig_buf + idx + i - 3)) fromb64 += 4; } @@ -1943,18 +1932,17 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } #ifdef _DEBUG - fprintf( - stderr, - "RTN loop=%u xor=%u arith=%u tolower=%u toupper=%u tohex=%u tob64=%u " - "fromhex=%u fromb64=%u to_0=%u to_slash=%u to_x=%u to_lf=%u to_cr=%u " - "from_0=%u from_slash=%u from_x=%u from_lf=%u from_cr=%u\n", - i, xor, arith, tolower, toupper, tohex, tob64, fromhex, fromb64, to_0, - to_slash, to_x, to_lf, to_cr, from_0, from_slash, from_x, from_lf, - from_cr); + fprintf(stderr, + "RTN idx=%u loop=%u xor=%u arith=%u tolower=%u toupper=%u " + "tohex=%u tob64=%u " + "fromhex=%u fromb64=%u to_0=%u to_slash=%u to_x=%u " + "from_0=%u from_slash=%u from_x=%u\n", + idx, i, xor, arith, tolower, toupper, tohex, tob64, fromhex, + fromb64, to_0, to_slash, to_x, from_0, from_slash, from_x); #endif // input is base64 and converted to binary? convert repl to base64! - if (i && !(i % 4) && i < 24 && fromb64 > i) { + if ((i % 4) == 3 && i < 24 && fromb64 > i) { to_base64(repl, tmp, i + 1); memcpy(buf + idx, tmp, i + 1); @@ -1965,12 +1953,13 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } // input is converted to base64? decode repl with base64! - if (i && !(i % 3) && i < 24 && tob64 > i) { + if ((i % 3) == 2 && i < 24 && tob64 > i) { u32 olen = from_base64(repl, tmp, i + 1); memcpy(buf + idx, tmp, olen); if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - // fprintf(stderr, "RTN ATTEMPT tob64 %u result %u\n", tob64, *status); + // fprintf(stderr, "RTN ATTEMPT tob64 %u idx=%u result %u\n", tob64, + // idx, *status); } -- cgit 1.4.1 From ba47bee25207436db8e2b98ef2d02801967980ca Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 21 Jan 2021 11:41:25 +0100 Subject: pre merge changes --- src/afl-fuzz-redqueen.c | 57 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 8ffd39da..d631332a 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -29,10 +29,11 @@ #include "cmplog.h" //#define _DEBUG -#define COMBINE +//#define COMBINE //#define CMPLOG_INTROSPECTION //#define ARITHMETIC_LESSER_GREATER -#define TRANSFORM +//#define TRANSFORM +//#define TRANSFORM_BASE64 // CMP attribute enum enum { @@ -51,9 +52,9 @@ enum { // CMPLOG LVL enum { - LVL1 = 1, - LVL2 = 2, - LVL3 = 4 + LVL1 = 1, // Integer solving + LVL2 = 2, // FP solving + LVL3 = 4 // expensive tranformations }; @@ -238,6 +239,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 start_time = get_cur_time(); #endif + u32 screen_update = 1000000 / afl->queue_cur->exec_us; u64 orig_hit_cnt, new_hit_cnt, exec_cksum; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; @@ -315,7 +317,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, } - ++afl->stage_cur; + if (++afl->stage_cur % screen_update) { show_stats(afl); }; } @@ -550,6 +552,7 @@ static int is_hex(const char *str) { } + #ifdef TRANSFORM_BASE64 // tests 4 bytes at location static int is_base64(const char *str) { @@ -662,6 +665,8 @@ static void to_base64(u8 *src, u8 *dst, u32 dst_len) { } + #endif + #endif static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, @@ -1728,6 +1733,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } memcpy(save, &buf[saved_idx - to], its_len + to); + (void)(j); #ifdef _DEBUG fprintf(stderr, "RTN T idx=%u lvl=%02x ", idx, lvl); @@ -1796,8 +1802,10 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, if (lvl & LVL3) { - u32 toupper = 0, tolower = 0, xor = 0, arith = 0, tohex = 0, tob64 = 0; - u32 fromhex = 0, fromb64 = 0; + u32 toupper = 0, tolower = 0, xor = 0, arith = 0, tohex = 0, fromhex = 0; + #ifdef TRANSFORM_BASE64 + u32 tob64 = 0, fromb64 = 0; + #endif u32 from_0 = 0, from_x = 0, from_X = 0, from_slash = 0, from_up = 0; u32 to_0 = 0, to_x = 0, to_slash = 0, to_up = 0; u8 xor_val[32], arith_val[32], tmp[48]; @@ -1893,6 +1901,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } + #ifdef TRANSFORM_BASE64 if (i % 3 == 2 && i < 24) { if (is_base64(repl + ((i / 3) << 2))) tob64 += 3; @@ -1905,6 +1914,8 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } + #endif + if ((o_pattern[i] ^ orig_buf[idx + i]) == xor_val[i] && xor_val[i]) { ++xor; @@ -1934,13 +1945,17 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, #ifdef _DEBUG fprintf(stderr, "RTN idx=%u loop=%u xor=%u arith=%u tolower=%u toupper=%u " - "tohex=%u tob64=%u " - "fromhex=%u fromb64=%u to_0=%u to_slash=%u to_x=%u " + "tohex=%u fromhex=%u to_0=%u to_slash=%u to_x=%u " "from_0=%u from_slash=%u from_x=%u\n", - idx, i, xor, arith, tolower, toupper, tohex, tob64, fromhex, - fromb64, to_0, to_slash, to_x, from_0, from_slash, from_x); + idx, i, xor, arith, tolower, toupper, tohex, fromhex, to_0, + to_slash, to_x, from_0, from_slash, from_x); + #ifdef TRANSFORM_BASE64 + fprintf(stderr, "RTN idx=%u loop=%u tob64=%u from64=%u\n", tob64, + fromb64); + #endif #endif + #ifdef TRANSFORM_BASE64 // input is base64 and converted to binary? convert repl to base64! if ((i % 4) == 3 && i < 24 && fromb64 > i) { @@ -1963,6 +1978,8 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } + #endif + // input is converted to hex? convert repl to binary! if (i < 16 && tohex > i) { @@ -2096,8 +2113,11 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, if ((i >= 7 && (i >= xor&&i >= arith &&i >= tolower &&i >= toupper &&i > tohex &&i > - (1 + fromhex + from_0 + from_x + from_slash) && - i > tob64 + 3 && i > fromb64 + 3)) || + (fromhex + from_0 + from_x + from_slash + 1) + #ifdef TRANSFORM_BASE64 + && i > tob64 + 3 && i > fromb64 + 4 + #endif + )) || repl[i] != changed_val[i] || *status == 1) { break; @@ -2348,6 +2368,8 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { u64 orig_hit_cnt, new_hit_cnt; u64 orig_execs = afl->fsrv.total_execs; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; + u64 screen_update = 1000000 / afl->queue_cur->exec_us, + execs = afl->fsrv.total_execs; afl->stage_name = "input-to-state"; afl->stage_short = "its"; @@ -2440,6 +2462,13 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { } + if (afl->fsrv.total_execs - execs > screen_update) { + + execs = afl->fsrv.total_execs; + show_stats(afl); + + } + } r = 0; -- cgit 1.4.1 From 0d472adef0cbe68cec128b7b15e508f0bb05455d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 21 Jan 2021 12:02:50 +0100 Subject: temp conflict resolve --- src/afl-cc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index d44f069a..2ce986c7 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -856,8 +856,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_OFF()=__afl_coverage_off()"; cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()"; - cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_ABORT()=__afl_coverage_abort()"; - + cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_SKIP()=__afl_coverage_skip()"; cc_params[cc_par_cnt++] = "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " "__afl_fuzz_alt_ptr)"; -- cgit 1.4.1 From e8c1b43a3de7ae3d173c749c3be4e58a95d3372b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 21 Jan 2021 15:24:34 +0100 Subject: fix docs --- docs/env_variables.md | 2 +- src/afl-fuzz.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/docs/env_variables.md b/docs/env_variables.md index 26128b01..baf69e6a 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -519,7 +519,7 @@ The corpus minimization script offers very little customization: a modest security risk on multi-user systems with rogue users, but should be safe on dedicated fuzzing boxes. -# #6) Settings for afl-tmin +## 7) Settings for afl-tmin Virtually nothing to play with. Well, in QEMU mode (`-Q`), `AFL_PATH` will be searched for afl-qemu-trace. In addition to this, `TMPDIR` may be used if a diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 95bc0694..9a8159bd 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -123,7 +123,7 @@ static void usage(u8 *argv0, int more_help) { "it.\n" " if using QEMU, just use -c 0.\n" " -l cmplog_level - set the complexity/intensivity of CmpLog.\n" - " Values: 1 (default), 2 (intensive) and 3 (heavy)\n\n" + " Values: 1 (integer+string), 2 (+FP) and 3 (+transform)\n\n" "Fuzzing behavior settings:\n" " -Z - sequential queue selection instead of weighted " -- cgit 1.4.1 From b850951c726258053c5635d6597704cf346fe3c4 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 21 Jan 2021 17:15:14 +0100 Subject: code format and not setting sanitizers if debug and settings present --- src/afl-cc.c | 8 ++++---- src/afl-forkserver.c | 53 +++++++++++++++++++++++++++------------------------ src/afl-fuzz-bitmap.c | 6 +----- src/afl-fuzz-stats.c | 2 ++ src/afl-fuzz.c | 12 ++++++++---- src/afl-ld-lto.c | 2 +- 6 files changed, 44 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index b0b11f48..8e7af0f9 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -586,9 +586,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { #if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) -#ifdef __ANDROID__ + #ifdef __ANDROID__ cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; -#else + #else if (have_instr_list) { if (!be_quiet) @@ -608,7 +608,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } -#endif + #endif #else #if LLVM_MAJOR >= 4 if (!be_quiet) @@ -1036,7 +1036,7 @@ int main(int argc, char **argv, char **envp) { #endif #ifdef __ANDROID__ - have_llvm = 1; + have_llvm = 1; #endif if ((ptr = find_object("afl-gcc-pass.so", argv[0])) != NULL) { diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 50e4139b..d4484de7 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -484,38 +484,41 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* Set sane defaults for ASAN if nothing else specified. */ - setenv("ASAN_OPTIONS", - "abort_on_error=1:" - "detect_leaks=0:" - "malloc_context_size=0:" - "symbolize=0:" - "allocator_may_return_null=1:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", - 0); + if (!afl->debug || !getenv("ASAN_OPTIONS")) + setenv("ASAN_OPTIONS", + "abort_on_error=1:" + "detect_leaks=0:" + "malloc_context_size=0:" + "symbolize=0:" + "allocator_may_return_null=1:" + "handle_segv=0:" + "handle_sigbus=0:" + "handle_abort=0:" + "handle_sigfpe=0:" + "handle_sigill=0", + 0); /* Set sane defaults for UBSAN if nothing else specified. */ - setenv("UBSAN_OPTIONS", - "halt_on_error=1:" - "abort_on_error=1:" - "malloc_context_size=0:" - "allocator_may_return_null=1:" - "symbolize=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", - 0); + if (!afl->debug || !getenv("UBSAN_OPTIONS")) + setenv("UBSAN_OPTIONS", + "halt_on_error=1:" + "abort_on_error=1:" + "malloc_context_size=0:" + "allocator_may_return_null=1:" + "symbolize=0:" + "handle_segv=0:" + "handle_sigbus=0:" + "handle_abort=0:" + "handle_sigfpe=0:" + "handle_sigill=0", + 0); /* MSAN is tricky, because it doesn't support abort_on_error=1 at this point. So, we do this in a very hacky way. */ - setenv("MSAN_OPTIONS", + if (!afl->debug || !getenv("MSAN_OPTIONS")) + setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" "symbolize=0:" "abort_on_error=1:" diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 586f3990..0c4a114e 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -700,11 +700,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (likely(!afl->non_instrumented_mode)) { - if (!classified) { - - classify_counts(&afl->fsrv); - - } + if (!classified) { classify_counts(&afl->fsrv); } simplify_trace(afl, afl->fsrv.trace_bits); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index e67bace9..82da8176 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -60,8 +60,10 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { if (i) fprintf(f, " "); #ifdef __ANDROID__ if (memchr(argv[i], '\'', sizeof(argv[i]))) { + #else if (index(argv[i], '\'')) { + #endif fprintf(f, "'"); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 9a8159bd..2a59bbe4 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -123,7 +123,8 @@ static void usage(u8 *argv0, int more_help) { "it.\n" " if using QEMU, just use -c 0.\n" " -l cmplog_level - set the complexity/intensivity of CmpLog.\n" - " Values: 1 (integer+string), 2 (+FP) and 3 (+transform)\n\n" + " Values: 1 (integer+string), 2 (+FP) and 3 " + "(+transform)\n\n" "Fuzzing behavior settings:\n" " -Z - sequential queue selection instead of weighted " @@ -584,7 +585,8 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->timeout_given) { FATAL("Multiple -t options not supported"); } - if (!optarg || sscanf(optarg, "%u%c", &afl->fsrv.exec_tmout, &suffix) < 1 || + if (!optarg || + sscanf(optarg, "%u%c", &afl->fsrv.exec_tmout, &suffix) < 1 || optarg[0] == '-') { FATAL("Bad syntax used for -t"); @@ -766,7 +768,8 @@ int main(int argc, char **argv_orig, char **envp) { case 'V': { afl->most_time_key = 1; - if (!optarg || sscanf(optarg, "%llu", &afl->most_time) < 1 || optarg[0] == '-') { + if (!optarg || sscanf(optarg, "%llu", &afl->most_time) < 1 || + optarg[0] == '-') { FATAL("Bad syntax used for -V"); @@ -777,7 +780,8 @@ int main(int argc, char **argv_orig, char **envp) { case 'E': { afl->most_execs_key = 1; - if (!optarg || sscanf(optarg, "%llu", &afl->most_execs) < 1 || optarg[0] == '-') { + if (!optarg || sscanf(optarg, "%llu", &afl->most_execs) < 1 || + optarg[0] == '-') { FATAL("Bad syntax used for -E"); diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index 0671d1c4..49c04e4a 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -252,7 +252,7 @@ static void edit_params(int argc, char **argv) { int main(int argc, char **argv) { - s32 pid, i, status; + s32 pid, i, status; char thecwd[PATH_MAX]; if (getenv("AFL_LD_CALLER") != NULL) { -- cgit 1.4.1 From 040bf5a61db5fa939c6e2a884207f18b62bf1522 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 21 Jan 2021 18:26:08 +0100 Subject: fix silly mistake --- src/afl-forkserver.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index d4484de7..4ee88216 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -484,7 +484,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* Set sane defaults for ASAN if nothing else specified. */ - if (!afl->debug || !getenv("ASAN_OPTIONS")) + if (!getenv("ASAN_OPTIONS")) setenv("ASAN_OPTIONS", "abort_on_error=1:" "detect_leaks=0:" @@ -500,7 +500,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* Set sane defaults for UBSAN if nothing else specified. */ - if (!afl->debug || !getenv("UBSAN_OPTIONS")) + if (!getenv("UBSAN_OPTIONS")) setenv("UBSAN_OPTIONS", "halt_on_error=1:" "abort_on_error=1:" @@ -517,7 +517,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* MSAN is tricky, because it doesn't support abort_on_error=1 at this point. So, we do this in a very hacky way. */ - if (!afl->debug || !getenv("MSAN_OPTIONS")) + if (!getenv("MSAN_OPTIONS")) setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" "symbolize=0:" -- cgit 1.4.1 From 60764ebdf15be0affdd3040135fc6eb36e10d677 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 21 Jan 2021 18:43:06 +0100 Subject: forkserver debug flag support --- include/forkserver.h | 2 ++ src/afl-forkserver.c | 9 +++++---- src/afl-fuzz.c | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/include/forkserver.h b/include/forkserver.h index 3019e289..d2fcaa20 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -83,6 +83,8 @@ typedef struct afl_forkserver { bool uses_asan; /* Target uses ASAN? */ + bool debug; /* debug mode? */ + bool uses_crash_exitcode; /* Custom crash exitcode specified? */ u8 crash_exitcode; /* The crash exitcode specified */ diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 4ee88216..1f5685b0 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -91,7 +91,7 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->map_size = get_map_size(); fsrv->use_fauxsrv = false; fsrv->last_run_timed_out = false; - + fsrv->debug = false; fsrv->uses_crash_exitcode = false; fsrv->uses_asan = false; @@ -117,6 +117,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->uses_crash_exitcode = from->uses_crash_exitcode; fsrv_to->crash_exitcode = from->crash_exitcode; fsrv_to->kill_signal = from->kill_signal; + fsrv_to->debug = from->debug; // These are forkserver specific. fsrv_to->out_dir_fd = -1; @@ -484,7 +485,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* Set sane defaults for ASAN if nothing else specified. */ - if (!getenv("ASAN_OPTIONS")) + if (fsrv->debug == true && !getenv("ASAN_OPTIONS")) setenv("ASAN_OPTIONS", "abort_on_error=1:" "detect_leaks=0:" @@ -500,7 +501,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* Set sane defaults for UBSAN if nothing else specified. */ - if (!getenv("UBSAN_OPTIONS")) + if (fsrv->debug == true && !getenv("UBSAN_OPTIONS")) setenv("UBSAN_OPTIONS", "halt_on_error=1:" "abort_on_error=1:" @@ -517,7 +518,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* MSAN is tricky, because it doesn't support abort_on_error=1 at this point. So, we do this in a very hacky way. */ - if (!getenv("MSAN_OPTIONS")) + if (fsrv->debug == true && !getenv("MSAN_OPTIONS")) setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" "symbolize=0:" diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 2a59bbe4..9b62e961 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -339,6 +339,7 @@ int main(int argc, char **argv_orig, char **envp) { afl_state_init(afl, map_size); afl->debug = debug; afl_fsrv_init(&afl->fsrv); + if (debug) { afl->fsrv.debug = true ; } read_afl_environment(afl, envp); if (afl->shm.map_size) { afl->fsrv.map_size = afl->shm.map_size; } -- cgit 1.4.1 From 30148bc1a9938cc29047d198eab72818cd037e3c Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 21 Jan 2021 19:58:25 +0100 Subject: fix afl-showmap and gcc plugin test --- src/afl-showmap.c | 9 ++++++--- test/test-gcc-plugin.sh | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/afl-showmap.c b/src/afl-showmap.c index ee6d6de9..ab47c602 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -85,6 +85,7 @@ static u8 quiet_mode, /* Hide non-essential messages? */ keep_cores, /* Allow coredumps? */ remove_shm = 1, /* remove shmem? */ collect_coverage, /* collect coverage */ + have_coverage, /* have coverage? */ no_classify; /* do not classify counts */ static volatile u8 stop_soon, /* Ctrl-C pressed? */ @@ -316,7 +317,8 @@ static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem, } - if (fsrv->trace_bits[0] == 1) { fsrv->trace_bits[0] = 0; } + if (fsrv->trace_bits[0] == 1) { fsrv->trace_bits[0] = 0; have_coverage = 1; } + else { have_coverage = 0; } if (!no_classify) { classify_counts(fsrv); } @@ -491,7 +493,8 @@ static void showmap_run_target(afl_forkserver_t *fsrv, char **argv) { } - if (fsrv->trace_bits[0] == 1) { fsrv->trace_bits[0] = 0; } + if (fsrv->trace_bits[0] == 1) { fsrv->trace_bits[0] = 0; have_coverage = 1; } + else { have_coverage = 0; } if (!no_classify) { classify_counts(fsrv); } @@ -1232,7 +1235,7 @@ int main(int argc, char **argv_orig, char **envp) { if (!quiet_mode || collect_coverage) { - if (!tcnt) { FATAL("No instrumentation detected" cRST); } + if (!tcnt && !have_coverage) { FATAL("No instrumentation detected" cRST); } OKF("Captured %u tuples (highest value %u, total values %llu) in " "'%s'." cRST, tcnt, highest, total, out_file); diff --git a/test/test-gcc-plugin.sh b/test/test-gcc-plugin.sh index 58000e92..cce6336b 100755 --- a/test/test-gcc-plugin.sh +++ b/test/test-gcc-plugin.sh @@ -86,15 +86,15 @@ test -e ../afl-gcc-fast -a -e ../afl-compiler-rt.o && { # now for the special gcc_plugin things echo foobar.c > instrumentlist.txt AFL_GCC_INSTRUMENT_FILE=instrumentlist.txt ../afl-gcc-fast -o test-compcov test-compcov.c > /dev/null 2>&1 - test -e test-compcov && test_compcov_binary_functionality ./test-compcov && { - echo 1 | ../afl-showmap -m ${MEM_LIMIT} -o - -r -- ./test-compcov 2>&1 | grep -q "Captured 1 tuples" && { + test -x test-compcov && test_compcov_binary_functionality ./test-compcov && { + echo 1 | ../afl-showmap -m ${MEM_LIMIT} -o - -r -- ./test-compcov 2>&1 | grep -q "Captured 0 tuples" && { $ECHO "$GREEN[+] gcc_plugin instrumentlist feature works correctly" } || { $ECHO "$RED[!] gcc_plugin instrumentlist feature failed" CODE=1 } } || { - $ECHO "$RED[!] gcc_plugin instrumentlist feature compilation failed" + $ECHO "$RED[!] gcc_plugin instrumentlist feature compilation failed." CODE=1 } rm -f test-compcov test.out instrumentlist.txt -- cgit 1.4.1 From 258ae1632af49d9ee68b1ce1a6c74d9e92245bcf Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 22 Jan 2021 21:10:23 +0100 Subject: stack 2 heap --- src/afl-fuzz-init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index cbff6d7e..b1a24f2f 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -810,7 +810,7 @@ void perform_dry_run(afl_state_t *afl) { while (q) { - u8 use_mem[MAX_FILE]; + u8 *use_mem = afl_realloc(AFL_BUF_PARAM(in), MAX_FILE); u8 res; s32 fd; -- cgit 1.4.1 From 0a3a708f9bf7b9f192d236c792a13cec2aa54a16 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 23 Jan 2021 10:01:09 +0100 Subject: less stack mem req --- src/afl-fuzz-init.c | 3 ++- src/afl-fuzz-queue.c | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index b1a24f2f..fed58eb6 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -807,10 +807,10 @@ void perform_dry_run(afl_state_t *afl) { struct queue_entry *q = afl->queue; u32 cal_failures = 0; u8 * skip_crashes = afl->afl_env.afl_skip_crashes; + u8 * use_mem; while (q) { - u8 *use_mem = afl_realloc(AFL_BUF_PARAM(in), MAX_FILE); u8 res; s32 fd; @@ -829,6 +829,7 @@ void perform_dry_run(afl_state_t *afl) { if (fd < 0) { PFATAL("Unable to open '%s'", q->fname); } u32 read_len = MIN(q->len, (u32)MAX_FILE); + use_mem = afl_realloc(AFL_BUF_PARAM(in), read_len); if (read(fd, use_mem, read_len) != (ssize_t)read_len) { FATAL("Short read from '%s'", q->fname); diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index aec57a6e..90f969d9 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -313,17 +313,18 @@ void mark_as_redundant(afl_state_t *afl, struct queue_entry *q, u8 state) { /* check if ascii or UTF-8 */ -static u8 check_if_text(struct queue_entry *q) { +static u8 check_if_text(afl_state_t *afl, struct queue_entry *q) { if (q->len < AFL_TXT_MIN_LEN) return 0; - u8 buf[MAX_FILE]; + u8 *buf; int fd; u32 len = q->len, offset = 0, ascii = 0, utf8 = 0; ssize_t comp; if (len >= MAX_FILE) len = MAX_FILE - 1; if ((fd = open(q->fname, O_RDONLY)) < 0) return 0; + buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len); comp = read(fd, buf, len); close(fd); if (comp != (ssize_t)len) return 0; @@ -487,7 +488,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { } /* only redqueen currently uses is_ascii */ - if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(q); + if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(afl, q); } -- cgit 1.4.1 From cd8668ad3ace7d5e369d492197a7da787183c056 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 25 Jan 2021 13:55:09 +0100 Subject: mopt fix --- src/afl-fuzz-one.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 4ce22c08..a7262eec 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -4781,8 +4781,7 @@ pacemaker_fuzzing: } - afl->stage_cycles_puppet_v2[afl->swarm_now] - [STAGE_OverWriteExtra]++; + MOpt_globals.cycles_v2[STAGE_OverWriteExtra]++; break; @@ -4837,8 +4836,7 @@ pacemaker_fuzzing: memcpy(out_buf + insert_at, ptr, extra_len); temp_len += extra_len; - afl->stage_cycles_puppet_v2[afl->swarm_now][STAGE_InsertExtra] += - 1; + MOpt_globals.cycles_v2[STAGE_InsertExtra]++; break; } @@ -4920,7 +4918,7 @@ pacemaker_fuzzing: } - afl->stage_cycles_puppet_v2[afl->swarm_now][STAGE_Splice]++; + MOpt_globals.cycles_v2[STAGE_Splice]++; break; } // end of default: -- cgit 1.4.1 From 7c381a782e3bb05335df745ea6130c0a668463da Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 25 Jan 2021 20:18:42 +0100 Subject: enable cmplog combine --- src/afl-fuzz-redqueen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index d631332a..6721b8ef 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -29,7 +29,7 @@ #include "cmplog.h" //#define _DEBUG -//#define COMBINE +#define COMBINE //#define CMPLOG_INTROSPECTION //#define ARITHMETIC_LESSER_GREATER //#define TRANSFORM -- cgit 1.4.1 From e0663c91b9cbf1bdc46593dec4ba11224e6847d7 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 26 Jan 2021 12:15:13 +0100 Subject: wip fix --- src/afl-fuzz-init.c | 23 ++++++++++++++++++----- src/afl-fuzz-one.c | 13 +++++++++---- 2 files changed, 27 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index fed58eb6..2cb152a9 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -1026,6 +1026,14 @@ void perform_dry_run(afl_state_t *afl) { /* Remove from fuzzing queue but keep for splicing */ struct queue_entry *p = afl->queue; + + if (!p->disabled && !p->was_fuzzed) { + + --afl->pending_not_fuzzed; + --afl->active_paths; + + } + p->disabled = 1; p->perf_score = 0; while (p && p->next != q) @@ -1036,9 +1044,6 @@ void perform_dry_run(afl_state_t *afl) { else afl->queue = q->next; - --afl->pending_not_fuzzed; - --afl->active_paths; - afl->max_depth = 0; p = afl->queue; while (p) { @@ -1123,8 +1128,16 @@ restart_outer_cull_loop: if (!p->cal_failed && p->exec_cksum == q->exec_cksum) { duplicates = 1; - --afl->pending_not_fuzzed; - afl->active_paths--; + if (!p->disabled && !q->disabled && !p->was_fuzzed && !q->was_fuzzed) { + + --afl->pending_not_fuzzed; + afl->active_paths--; + + } else { + + FATAL("disabled entry? this should not happen, please report!"); + + } // We do not remove any of the memory allocated because for // splicing the data might still be interesting. diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index a7262eec..af768183 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -2782,11 +2782,16 @@ abandon_entry: cycle and have not seen this entry before. */ if (!afl->stop_soon && !afl->queue_cur->cal_failed && - (afl->queue_cur->was_fuzzed == 0 || afl->queue_cur->fuzz_level == 0)) { + (afl->queue_cur->was_fuzzed == 0 || afl->queue_cur->fuzz_level == 0) && + !afl->queue_cur->disabled) { - --afl->pending_not_fuzzed; - afl->queue_cur->was_fuzzed = 1; - if (afl->queue_cur->favored) { --afl->pending_favored; } + if (!afl->queue_cur->was_fuzzed) { + + --afl->pending_not_fuzzed; + afl->queue_cur->was_fuzzed = 1; + if (afl->queue_cur->favored) { --afl->pending_favored; } + + } } -- cgit 1.4.1 From 9c393adbb953fe5bf6809e5b0feca7be2f52b7f8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 26 Jan 2021 17:12:11 +0100 Subject: real fix plus code format --- include/android-ashmem.h | 84 +++++++++++++++++++++++---------------------- src/afl-fuzz-init.c | 28 +++++++++------ src/afl-fuzz-queue.c | 2 +- src/afl-fuzz.c | 2 +- src/afl-showmap.c | 24 ++++++++++--- utils/afl_frida/afl-frida.c | 78 ++++++++++++++++++++++------------------- 6 files changed, 125 insertions(+), 93 deletions(-) (limited to 'src') diff --git a/include/android-ashmem.h b/include/android-ashmem.h index 6939e06d..91699b27 100644 --- a/include/android-ashmem.h +++ b/include/android-ashmem.h @@ -1,81 +1,83 @@ #ifdef __ANDROID__ -#ifndef _ANDROID_ASHMEM_H -#define _ANDROID_ASHMEM_H - -#include -#include -#include -#include - -#if __ANDROID_API__ >= 26 -#define shmat bionic_shmat -#define shmctl bionic_shmctl -#define shmdt bionic_shmdt -#define shmget bionic_shmget -#endif -#include -#undef shmat -#undef shmctl -#undef shmdt -#undef shmget -#include - -#define ASHMEM_DEVICE "/dev/ashmem" + #ifndef _ANDROID_ASHMEM_H + #define _ANDROID_ASHMEM_H + + #include + #include + #include + #include + + #if __ANDROID_API__ >= 26 + #define shmat bionic_shmat + #define shmctl bionic_shmctl + #define shmdt bionic_shmdt + #define shmget bionic_shmget + #endif + #include + #undef shmat + #undef shmctl + #undef shmdt + #undef shmget + #include + + #define ASHMEM_DEVICE "/dev/ashmem" int shmctl(int __shmid, int __cmd, struct shmid_ds *__buf) { + int ret = 0; if (__cmd == IPC_RMID) { - int length = ioctl(__shmid, ASHMEM_GET_SIZE, NULL); + + int length = ioctl(__shmid, ASHMEM_GET_SIZE, NULL); struct ashmem_pin pin = {0, length}; ret = ioctl(__shmid, ASHMEM_UNPIN, &pin); close(__shmid); + } return ret; + } int shmget(key_t __key, size_t __size, int __shmflg) { - (void) __shmflg; - int fd, ret; + + (void)__shmflg; + int fd, ret; char ourkey[11]; fd = open(ASHMEM_DEVICE, O_RDWR); - if (fd < 0) - return fd; + if (fd < 0) return fd; sprintf(ourkey, "%d", __key); ret = ioctl(fd, ASHMEM_SET_NAME, ourkey); - if (ret < 0) - goto error; + if (ret < 0) goto error; ret = ioctl(fd, ASHMEM_SET_SIZE, __size); - if (ret < 0) - goto error; + if (ret < 0) goto error; return fd; error: close(fd); return ret; + } void *shmat(int __shmid, const void *__shmaddr, int __shmflg) { - (void) __shmflg; - int size; + + (void)__shmflg; + int size; void *ptr; size = ioctl(__shmid, ASHMEM_GET_SIZE, NULL); - if (size < 0) { - return NULL; - } + if (size < 0) { return NULL; } ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, __shmid, 0); - if (ptr == MAP_FAILED) { - return NULL; - } + if (ptr == MAP_FAILED) { return NULL; } return ptr; + } -#endif /* !_ANDROID_ASHMEM_H */ -#endif /* !__ANDROID__ */ + #endif /* !_ANDROID_ASHMEM_H */ +#endif /* !__ANDROID__ */ + diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 2cb152a9..ed2010cd 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -1027,7 +1027,7 @@ void perform_dry_run(afl_state_t *afl) { struct queue_entry *p = afl->queue; - if (!p->disabled && !p->was_fuzzed) { + if (!p->was_fuzzed) { --afl->pending_not_fuzzed; --afl->active_paths; @@ -1128,16 +1128,6 @@ restart_outer_cull_loop: if (!p->cal_failed && p->exec_cksum == q->exec_cksum) { duplicates = 1; - if (!p->disabled && !q->disabled && !p->was_fuzzed && !q->was_fuzzed) { - - --afl->pending_not_fuzzed; - afl->active_paths--; - - } else { - - FATAL("disabled entry? this should not happen, please report!"); - - } // We do not remove any of the memory allocated because for // splicing the data might still be interesting. @@ -1147,6 +1137,14 @@ restart_outer_cull_loop: // we keep the shorter file if (p->len >= q->len) { + if (!p->was_fuzzed) { + + p->was_fuzzed = 1; + --afl->pending_not_fuzzed; + afl->active_paths--; + + } + p->disabled = 1; p->perf_score = 0; q->next = p->next; @@ -1154,6 +1152,14 @@ restart_outer_cull_loop: } else { + if (!q->was_fuzzed) { + + q->was_fuzzed = 1; + --afl->pending_not_fuzzed; + afl->active_paths--; + + } + q->disabled = 1; q->perf_score = 0; if (prev) diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 90f969d9..4442b400 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -317,7 +317,7 @@ static u8 check_if_text(afl_state_t *afl, struct queue_entry *q) { if (q->len < AFL_TXT_MIN_LEN) return 0; - u8 *buf; + u8 * buf; int fd; u32 len = q->len, offset = 0, ascii = 0, utf8 = 0; ssize_t comp; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 9b62e961..ecf69728 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -339,7 +339,7 @@ int main(int argc, char **argv_orig, char **envp) { afl_state_init(afl, map_size); afl->debug = debug; afl_fsrv_init(&afl->fsrv); - if (debug) { afl->fsrv.debug = true ; } + if (debug) { afl->fsrv.debug = true; } read_afl_environment(afl, envp); if (afl->shm.map_size) { afl->fsrv.map_size = afl->shm.map_size; } diff --git a/src/afl-showmap.c b/src/afl-showmap.c index ab47c602..5a0b6ecf 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -317,8 +317,16 @@ static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem, } - if (fsrv->trace_bits[0] == 1) { fsrv->trace_bits[0] = 0; have_coverage = 1; } - else { have_coverage = 0; } + if (fsrv->trace_bits[0] == 1) { + + fsrv->trace_bits[0] = 0; + have_coverage = 1; + + } else { + + have_coverage = 0; + + } if (!no_classify) { classify_counts(fsrv); } @@ -493,8 +501,16 @@ static void showmap_run_target(afl_forkserver_t *fsrv, char **argv) { } - if (fsrv->trace_bits[0] == 1) { fsrv->trace_bits[0] = 0; have_coverage = 1; } - else { have_coverage = 0; } + if (fsrv->trace_bits[0] == 1) { + + fsrv->trace_bits[0] = 0; + have_coverage = 1; + + } else { + + have_coverage = 0; + + } if (!no_classify) { classify_counts(fsrv); } diff --git a/utils/afl_frida/afl-frida.c b/utils/afl_frida/afl-frida.c index 087f18e8..bf39be1c 100644 --- a/utils/afl_frida/afl-frida.c +++ b/utils/afl_frida/afl-frida.c @@ -153,7 +153,7 @@ static int enumerate_ranges(const GumRangeDetails *details, } -int main(int argc, char** argv) { +int main(int argc, char **argv) { #ifndef __APPLE__ (void)personality(ADDR_NO_RANDOMIZE); // disable ASLR @@ -166,10 +166,15 @@ int main(int argc, char** argv) { void *dl = NULL; if (argc > 2) { + dl = dlopen(argv[1], RTLD_LAZY); + } else { + dl = dlopen(TARGET_LIBRARY, RTLD_LAZY); + } + if (!dl) { if (argc > 2) @@ -197,17 +202,18 @@ int main(int argc, char** argv) { // END STEP 2 if (!getenv("AFL_FRIDA_TEST_INPUT")) { + gum_init_embedded(); if (!gum_stalker_is_supported()) { - + gum_deinit_embedded(); return 1; - + } - + GumStalker *stalker = gum_stalker_new(); - - GumAddress base_address; + + GumAddress base_address; if (argc > 2) base_address = gum_module_find_base_address(argv[1]); else @@ -215,87 +221,89 @@ int main(int argc, char** argv) { GumMemoryRange code_range; if (argc > 2) gum_module_enumerate_ranges(argv[1], GUM_PAGE_RX, enumerate_ranges, - &code_range); + &code_range); else gum_module_enumerate_ranges(TARGET_LIBRARY, GUM_PAGE_RX, enumerate_ranges, - &code_range); - + &code_range); + guint64 code_start = code_range.base_address; guint64 code_end = code_range.base_address + code_range.size; range_t instr_range = {0, code_start, code_end}; - + printf("Frida instrumentation: base=0x%lx instrumenting=0x%lx-%lx\n", base_address, code_start, code_end); if (!code_start || !code_end) { - + if (argc > 2) fprintf(stderr, "Error: no valid memory address found for %s\n", - argv[1]); + argv[1]); else fprintf(stderr, "Error: no valid memory address found for %s\n", - TARGET_LIBRARY); + TARGET_LIBRARY); exit(-1); - + } - + GumStalkerTransformer *transformer = gum_stalker_transformer_make_from_callback(instr_basic_block, &instr_range, NULL); - + // to ensure that the signatures are not optimized out memcpy(__afl_area_ptr, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT) + 1); memcpy(__afl_area_ptr + 32, (void *)AFL_DEFER_FORKSVR, sizeof(AFL_DEFER_FORKSVR) + 1); __afl_manual_init(); - + // // any expensive target library initialization that has to be done just once // - put that here // - + gum_stalker_follow_me(stalker, transformer, NULL); - + while (__afl_persistent_loop(UINT32_MAX) != 0) { - + previous_pc = 0; // Required! - - #ifdef _DEBUG + +#ifdef _DEBUG fprintf(stderr, "CLIENT crc: %016llx len: %u\n", hash64(__afl_fuzz_ptr, *__afl_fuzz_len), *__afl_fuzz_len); fprintf(stderr, "RECV:"); for (int i = 0; i < *__afl_fuzz_len; i++) fprintf(stderr, "%02x", __afl_fuzz_ptr[i]); fprintf(stderr, "\n"); - #endif - +#endif + // STEP 3: ensure the minimum length is present and setup the target // function to fuzz. - + if (*__afl_fuzz_len > 0) { - + __afl_fuzz_ptr[*__afl_fuzz_len] = 0; // if you need to null terminate (*o_function)(__afl_fuzz_ptr, *__afl_fuzz_len); - + } - + // END STEP 3 - + } - + gum_stalker_unfollow_me(stalker); - + while (gum_stalker_garbage_collect(stalker)) g_usleep(10000); - + g_object_unref(stalker); g_object_unref(transformer); gum_deinit_embedded(); } else { - char buf[8*1024] = {0}; - int count = read(0, buf, sizeof(buf)); - buf[8*1024-1] = '\0'; + + char buf[8 * 1024] = {0}; + int count = read(0, buf, sizeof(buf)); + buf[8 * 1024 - 1] = '\0'; (*o_function)(buf, count); + } return 0; -- cgit 1.4.1 From 36b5336152cd886d911f4299c3154b7817c94838 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 26 Jan 2021 22:45:59 +0100 Subject: better foreign sync name --- src/afl-fuzz-init.c | 15 ++++++++++++++- src/afl-fuzz-redqueen.c | 4 ++-- src/afl-fuzz.c | 10 ++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index ed2010cd..4f59a42f 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -460,6 +460,7 @@ void read_foreign_testcases(afl_state_t *afl, int first) { u32 i, iter; u8 val_buf[2][STRINGIFY_VAL_SIZE_MAX]; + u8 foreign_name[16]; for (iter = 0; iter < afl->foreign_sync_cnt; iter++) { @@ -468,6 +469,18 @@ void read_foreign_testcases(afl_state_t *afl, int first) { if (first) ACTF("Scanning '%s'...", afl->foreign_syncs[iter].dir); time_t ctime_max = 0; + u8 * name = rindex(afl->foreign_syncs[iter].dir, '/'); + if (!name) { name = afl->foreign_syncs[iter].dir; } + if (!strcmp(name, "queue") || !strcmp(name, "out") || + !strcmp(name, "default")) { + + snprintf(foreign_name, sizeof(foreign_name), "foreign_%u", iter); + + } else { + + snprintf(foreign_name, sizeof(foreign_name), "%s_%u", name, iter); + + } /* We use scandir() + alphasort() rather than readdir() because otherwise, the ordering of test cases would vary somewhat randomly and would be @@ -581,7 +594,7 @@ void read_foreign_testcases(afl_state_t *afl, int first) { write_to_testcase(afl, mem, st.st_size); fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); - afl->syncing_party = "foreign"; + afl->syncing_party = foreign_name; afl->queued_imported += save_if_interesting(afl, mem, st.st_size, fault); afl->syncing_party = 0; diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 6721b8ef..34db7231 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -249,7 +249,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, afl->stage_cur = 0; // in colorization we do not classify counts, hence we have to calculate - // the original checksum! + // the original checksum. if (unlikely(get_exec_checksum(afl, buf, len, &exec_cksum))) { goto checksum_fail; @@ -2368,7 +2368,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { u64 orig_hit_cnt, new_hit_cnt; u64 orig_execs = afl->fsrv.total_execs; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; - u64 screen_update = 1000000 / afl->queue_cur->exec_us, + u64 screen_update = 100000 / afl->queue_cur->exec_us, execs = afl->fsrv.total_execs; afl->stage_name = "input-to-state"; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index ecf69728..b92aa2a7 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -559,6 +559,16 @@ int main(int argc, char **argv_orig, char **envp) { FATAL("Maximum %u entried of -F option can be specified", FOREIGN_SYNCS_MAX); afl->foreign_syncs[afl->foreign_sync_cnt].dir = optarg; + while (afl->foreign_syncs[afl->foreign_sync_cnt] + .dir[strlen(afl->foreign_syncs[afl->foreign_sync_cnt].dir) - + 1] == '/') { + + afl->foreign_syncs[afl->foreign_sync_cnt] + .dir[strlen(afl->foreign_syncs[afl->foreign_sync_cnt].dir) - 1] = + 0; + + } + afl->foreign_sync_cnt++; break; -- cgit 1.4.1 From a754694ac4fa7b4016fb9de3253b0f3fe691fdf1 Mon Sep 17 00:00:00 2001 From: fuzzah <60884276+fuzzah@users.noreply.github.com> Date: Wed, 27 Jan 2021 05:48:59 +0300 Subject: include limits.h to fix build on BSD systems --- src/afl-ld-lto.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index 49c04e4a..1fb01600 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -45,6 +45,11 @@ #include +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || \ + defined(__DragonFly__) + #include +#endif + #ifdef __APPLE__ #include #endif -- cgit 1.4.1 From d046b28f2fb5981ce4a28ddcfac6ec3405624450 Mon Sep 17 00:00:00 2001 From: Adrian Panasiuk <4141848+ampanasiuk@users.noreply.github.com> Date: Wed, 27 Jan 2021 01:12:13 +0000 Subject: Fix "src" attribute in sync stage filenames (#703) --- src/afl-fuzz-run.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 17c305ed..97cb7415 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -627,9 +627,8 @@ void sync_fuzzers(afl_state_t *afl) { } if (m >= n) { goto close_sync; } // nothing new - o = n - 1; - while (o >= m) { + for (o = m; o < n; o++) { s32 fd; struct stat st; @@ -637,7 +636,6 @@ void sync_fuzzers(afl_state_t *afl) { snprintf(path, sizeof(path), "%s/%s", qd_path, namelist[o]->d_name); afl->syncing_case = next_min_accept; next_min_accept++; - o--; /* Allow this to fail in case the other fuzzer is resuming or so... */ -- cgit 1.4.1 From 2044c7e2b548e2747fde5deff65c78dd05e2ec8d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 27 Jan 2021 08:41:45 +0100 Subject: fix include --- include/envs.h | 2 +- src/afl-fuzz-init.c | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/include/envs.h b/include/envs.h index 756cd737..931cff15 100644 --- a/include/envs.h +++ b/include/envs.h @@ -48,7 +48,7 @@ static char *afl_environment_variables[] = { "AFL_EXIT_WHEN_DONE", "AFL_FAST_CAL", "AFL_FORCE_UI", - "AFL_FUZZER_ARGS". // oss-fuzz + "AFL_FUZZER_ARGS", // oss-fuzz "AFL_GCC_ALLOWLIST", "AFL_GCC_DENYLIST", "AFL_GCC_BLOCKLIST", diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 4f59a42f..a428923d 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -482,9 +482,8 @@ void read_foreign_testcases(afl_state_t *afl, int first) { } - /* We use scandir() + alphasort() rather than readdir() because otherwise, - the ordering of test cases would vary somewhat randomly and would be - difficult to control. */ + /* We do not use sorting yet and do a more expensive ctime check instead. + a ctimesort() implementation would be better though. */ nl_cnt = scandir(afl->foreign_syncs[iter].dir, &nl, NULL, NULL); -- cgit 1.4.1 From 47f62eb0ca087bf26e79f2f0ce5935eda2599064 Mon Sep 17 00:00:00 2001 From: Joey Jiaojg <77719320+joeyjiaojg@users.noreply.github.com> Date: Thu, 28 Jan 2021 12:51:45 +0800 Subject: Fix dev branch for android (#710) * android: replace rindex with strrchr * android: support 64bit only due to 128bit integer not supported by 32bit system Co-authored-by: joeyjiaojg@qq.com --- Android.bp | 4 +++- src/afl-fuzz-init.c | 2 +- utils/afl_untracer/afl-untracer.c | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/Android.bp b/Android.bp index 5d6f0433..ee076d1e 100644 --- a/Android.bp +++ b/Android.bp @@ -36,6 +36,7 @@ cc_defaults { cc_binary { name: "afl-fuzz", host_supported: true, + compile_multilib: "64", defaults: [ "afl-defaults", @@ -64,6 +65,7 @@ cc_binary { "src/afl-common.c", "src/afl-sharedmem.c", "src/afl-forkserver.c", + "src/afl-performance.c", ], } @@ -152,7 +154,7 @@ cc_binary_host { cc_library_static { name: "afl-llvm-rt", - compile_multilib: "both", + compile_multilib: "64", vendor_available: true, host_supported: true, recovery_available: true, diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index a428923d..5f5e65cd 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -469,7 +469,7 @@ void read_foreign_testcases(afl_state_t *afl, int first) { if (first) ACTF("Scanning '%s'...", afl->foreign_syncs[iter].dir); time_t ctime_max = 0; - u8 * name = rindex(afl->foreign_syncs[iter].dir, '/'); + u8 * name = strrchr(afl->foreign_syncs[iter].dir, '/'); if (!name) { name = afl->foreign_syncs[iter].dir; } if (!strcmp(name, "queue") || !strcmp(name, "out") || !strcmp(name, "default")) { diff --git a/utils/afl_untracer/afl-untracer.c b/utils/afl_untracer/afl-untracer.c index 695f8dd1..f3894a06 100644 --- a/utils/afl_untracer/afl-untracer.c +++ b/utils/afl_untracer/afl-untracer.c @@ -143,7 +143,7 @@ void read_library_information(void) { b = buf; m = index(buf, '-'); e = index(buf, ' '); - if ((n = rindex(buf, '/')) == NULL) n = rindex(buf, ' '); + if ((n = strrchr(buf, '/')) == NULL) n = strrchr(buf, ' '); if (n && ((*n >= '0' && *n <= '9') || *n == '[' || *n == '{' || *n == '(')) n = NULL; -- cgit 1.4.1 From ad63ba49c181dd97786745913c6b2ade5ae69728 Mon Sep 17 00:00:00 2001 From: Yuan Date: Thu, 28 Jan 2021 17:21:54 +0800 Subject: Fix getopt arg string There is no '-P' case here. --- src/afl-fuzz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index b92aa2a7..a1f749b5 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -357,7 +357,7 @@ int main(int argc, char **argv_orig, char **envp) { while ((opt = getopt( argc, argv, - "+b:B:c:CdDe:E:hi:I:f:F:l:L:m:M:nNo:p:P:RQs:S:t:T:UV:Wx:Z")) > + "+b:B:c:CdDe:E:hi:I:f:F:l:L:m:M:nNo:p:RQs:S:t:T:UV:Wx:Z")) > 0) { switch (opt) { -- cgit 1.4.1 From a61a30dee03aced16d117150c4dbfd7079de7e68 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 28 Jan 2021 14:11:33 +0100 Subject: fix another pending_not_fuzzed location --- src/afl-fuzz-extras.c | 2 +- src/afl-fuzz-init.c | 5 +++-- src/afl-fuzz-one.c | 4 ++-- src/afl-fuzz.c | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index a3583651..7ecad233 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -413,7 +413,7 @@ void dedup_extras(afl_state_t *afl) { if (j + 1 < afl->extras_cnt) // not at the end of the list? memmove((char *)&afl->extras[j], (char *)&afl->extras[j + 1], (afl->extras_cnt - j - 1) * sizeof(struct extra_data)); - afl->extras_cnt--; + --afl->extras_cnt; goto restart_dedup; // restart if several duplicates are in a row } diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 5f5e65cd..84f81112 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -1041,6 +1041,7 @@ void perform_dry_run(afl_state_t *afl) { if (!p->was_fuzzed) { + p->was_fuzzed = 1; --afl->pending_not_fuzzed; --afl->active_paths; @@ -1153,7 +1154,7 @@ restart_outer_cull_loop: p->was_fuzzed = 1; --afl->pending_not_fuzzed; - afl->active_paths--; + --afl->active_paths; } @@ -1168,7 +1169,7 @@ restart_outer_cull_loop: q->was_fuzzed = 1; --afl->pending_not_fuzzed; - afl->active_paths--; + --afl->active_paths; } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index af768183..ff766158 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -530,7 +530,7 @@ u8 fuzz_one_original(afl_state_t *afl) { len = afl->queue_cur->len; /* maybe current entry is not ready for splicing anymore */ - if (unlikely(len <= 4 && old_len > 4)) afl->ready_for_splicing_count--; + if (unlikely(len <= 4 && old_len > 4)) --afl->ready_for_splicing_count; } @@ -2958,7 +2958,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { len = afl->queue_cur->len; /* maybe current entry is not ready for splicing anymore */ - if (unlikely(len <= 4 && old_len > 4)) afl->ready_for_splicing_count--; + if (unlikely(len <= 4 && old_len > 4)) --afl->ready_for_splicing_count; } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index a1f749b5..e856730e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1636,7 +1636,7 @@ int main(int argc, char **argv_orig, char **envp) { (afl->old_seed_selection && !afl->queue_cur))) { ++afl->queue_cycle; - runs_in_current_cycle = 0; + runs_in_current_cycle = (u32)-1; afl->cur_skipped_paths = 0; if (unlikely(afl->old_seed_selection)) { -- cgit 1.4.1 From 2a9fcd2a87bf11bc019c8d6be2b6eda77b772ee2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 28 Jan 2021 18:01:27 +0100 Subject: warn on afl-gcc/afl-clang instrumentation --- src/afl-cc.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 8e7af0f9..ff0f3c07 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1788,6 +1788,15 @@ int main(int argc, char **argv, char **envp) { } + if (!be_quiet && (compiler_mode == GCC || compiler_mode == CLANG)) { + + WARNF( + "You are using outdated instrumentation, install LLVM and/or " + "gcc-plugin and use afl-clang-fast/afl-clang-lto/afl-gcc-fast " + "instead!"); + + } + if (debug) { DEBUGF("cd '%s';", getthecwd()); -- cgit 1.4.1 From ce673ccab3f2ca438f879c1d2e63988eb9737a35 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 28 Jan 2021 19:19:57 +0100 Subject: remove snapshot reference --- src/afl-cc.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index ff0f3c07..b5dcb632 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1438,34 +1438,34 @@ int main(int argc, char **argv, char **envp) { " CC=afl-cc CXX=afl-c++ meson\n\n"); SAYF( - " |---------------- FEATURES " - "---------------|\n" - "MODES: NCC PERSIST SNAP DICT LAF " + " |------------- FEATURES " + "-------------|\n" + "MODES: NCC PERSIST DICT LAF " "CMPLOG SELECT\n" " [LTO] llvm LTO: %s%s\n" - " PCGUARD DEFAULT yes yes yes yes yes yes " + " PCGUARD DEFAULT yes yes yes yes yes " " yes\n" - " CLASSIC yes yes yes yes yes yes " + " CLASSIC yes yes yes yes yes " " yes\n" " [LLVM] llvm: %s%s\n" - " PCGUARD %s yes yes yes module yes yes " + " PCGUARD %s yes yes module yes yes " "extern\n" - " CLASSIC %s no yes yes module yes yes " + " CLASSIC %s no yes module yes yes " "yes\n" " - NORMAL\n" " - CTX\n" " - NGRAM-{2-16}\n" - " INSTRIM no yes yes module yes yes " + " INSTRIM no yes module yes yes " " yes\n" " - NORMAL\n" " - CTX\n" " - NGRAM-{2-16}\n" " [GCC_PLUGIN] gcc plugin: %s%s\n" - " CLASSIC DEFAULT no yes yes no no no " - " yes\n" + " CLASSIC DEFAULT no yes no no no " + "yes\n" " [GCC/CLANG] simple gcc/clang: %s%s\n" - " CLASSIC DEFAULT no no no no no no " - " no\n\n", + " CLASSIC DEFAULT no no no no no " + "no\n\n", have_lto ? "AVAILABLE" : "unavailable!", compiler_mode == LTO ? " [SELECTED]" : "", have_llvm ? "AVAILABLE" : "unavailable!", @@ -1520,9 +1520,6 @@ int main(int argc, char **argv, char **envp) { " (instrumentation/README.lto.md)\n" " PERSIST: persistent mode support [code] (huge speed increase!)\n" " (instrumentation/README.persistent_mode.md)\n" - " SNAP: linux lkm snapshot module support [automatic] (speed " - "increase)\n" - " (https://github.com/AFLplusplus/AFL-Snapshot-LKM/)\n" " DICT: dictionary in the target [yes=automatic or llvm module " "pass]\n" " (instrumentation/README.lto.md + " -- cgit 1.4.1 From 6f5746d42878207b5d17af71317220932a42ebd7 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 29 Jan 2021 15:38:49 +0100 Subject: AFL_USE_QASAN --- include/common.h | 1 + src/afl-analyze.c | 25 ++++++++++++++++++++ src/afl-common.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/afl-forkserver.c | 4 ++++ src/afl-fuzz.c | 30 +++++++++++++++++++++--- src/afl-showmap.c | 25 ++++++++++++++++++++ src/afl-tmin.c | 25 ++++++++++++++++++++ 7 files changed, 171 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/include/common.h b/include/common.h index 9490ec5f..bdaa1735 100644 --- a/include/common.h +++ b/include/common.h @@ -47,6 +47,7 @@ void argv_cpy_free(char **argv); char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv); char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv); char * get_afl_env(char *env); +u8 *get_libqasan_path(u8 *own_loc); extern u8 be_quiet; extern u8 *doc_path; /* path to documentation dir */ diff --git a/src/afl-analyze.c b/src/afl-analyze.c index 0af489fe..28598ba0 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -1078,6 +1078,31 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !in_file) { usage(argv[0]); } + if (qemu_mode && getenv("AFL_USE_QASAN")) { + + u8* preload = getenv("AFL_PRELOAD"); + u8* libqasan = get_libqasan_path(argv_orig[0]); + + if (!preload) { + + setenv("AFL_PRELOAD", libqasan, 0); + + } else { + + u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); + strcpy(result, libqasan); + strcat(result, " "); + strcat(result, preload); + + setenv("AFL_PRELOAD", result, 1); + ck_free(result); + + } + + ck_free(libqasan); + + } + map_size = get_map_size(); use_hex_offsets = !!get_afl_env("AFL_ANALYZE_HEX"); diff --git a/src/afl-common.c b/src/afl-common.c index cf996548..a69f2e97 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -334,6 +334,70 @@ char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { } +/* Get libqasan path. */ + +u8 *get_libqasan_path(u8 *own_loc) { + + if (!unlikely(own_loc)) { FATAL("BUG: param own_loc is NULL"); } + + u8 *tmp, *cp = NULL, *rsl, *own_copy; + + tmp = getenv("AFL_PATH"); + + if (tmp) { + + cp = alloc_printf("%s/libqasan.so", tmp); + + if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); } + + return cp; + + } + + own_copy = ck_strdup(own_loc); + rsl = strrchr(own_copy, '/'); + + if (rsl) { + + *rsl = 0; + + cp = alloc_printf("%s/libqasan.so", own_copy); + ck_free(own_copy); + + if (!access(cp, X_OK)) { + + return cp; + + } + + } else { + + ck_free(own_copy); + + } + + if (!access(BIN_PATH "/libqasan.so", X_OK)) { + + if (cp) { ck_free(cp); } + + return ck_strdup(BIN_PATH "/libqasan.so"); + + } + + SAYF("\n" cLRD "[-] " cRST + "Oops, unable to find the 'libqasan.so' binary. The binary must be " + "built\n" + " separately by following the instructions in " + "qemu_mode/libqasan/README.md. " + "If you\n" + " already have the binary installed, you may need to specify " + "AFL_PATH in the\n" + " environment.\n"); + + FATAL("Failed to locate 'libqasan.so'."); + +} + /* Find binary, used by analyze, showmap, tmin @returns the path, allocating the string */ diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 1f5685b0..e59f0d11 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -515,6 +515,10 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "handle_sigill=0", 0); + /* Envs for QASan */ + setenv("QASAN_MAX_CALL_STACK", "0", 0); + setenv("QASAN_SYMBOLIZE", "0", 0); + /* MSAN is tricky, because it doesn't support abort_on_error=1 at this point. So, we do this in a very hacky way. */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e856730e..54850173 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -326,8 +326,32 @@ int main(int argc, char **argv_orig, char **envp) { "compile time)"); } - #endif + + if (getenv("AFL_USE_QASAN")) { + + u8* preload = getenv("AFL_PRELOAD"); + u8* libqasan = get_libqasan_path(argv_orig[0]); + + if (!preload) { + + setenv("AFL_PRELOAD", libqasan, 0); + + } else { + + u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); + strcpy(result, libqasan); + strcat(result, " "); + strcat(result, preload); + + setenv("AFL_PRELOAD", result, 1); + ck_free(result); + + } + + ck_free(libqasan); + + } char **argv = argv_cpy_dup(argc, argv_orig); @@ -1245,7 +1269,7 @@ int main(int argc, char **argv_orig, char **envp) { "instead of using AFL_PRELOAD?"); } - + if (afl->afl_env.afl_preload) { if (afl->fsrv.qemu_mode) { @@ -1297,7 +1321,7 @@ int main(int argc, char **argv_orig, char **envp) { FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD"); } - + save_cmdline(afl, argc, argv); fix_up_banner(afl, argv[optind]); diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 5a0b6ecf..f3cd5a90 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -942,6 +942,31 @@ int main(int argc, char **argv_orig, char **envp) { } if (optind == argc || !out_file) { usage(argv[0]); } + + if (fsrv->qemu_mode && getenv("AFL_USE_QASAN")) { + + u8* preload = getenv("AFL_PRELOAD"); + u8* libqasan = get_libqasan_path(argv_orig[0]); + + if (!preload) { + + setenv("AFL_PRELOAD", libqasan, 0); + + } else { + + u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); + strcpy(result, libqasan); + strcat(result, " "); + strcat(result, preload); + + setenv("AFL_PRELOAD", result, 1); + ck_free(result); + + } + + ck_free(libqasan); + + } if (in_dir) { diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 5fd60cd2..9e9e2d63 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -1074,6 +1074,31 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !in_file || !output_file) { usage(argv[0]); } check_environment_vars(envp); + + if (fsrv->qemu_mode && getenv("AFL_USE_QASAN")) { + + u8* preload = getenv("AFL_PRELOAD"); + u8* libqasan = get_libqasan_path(argv_orig[0]); + + if (!preload) { + + setenv("AFL_PRELOAD", libqasan, 0); + + } else { + + u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); + strcpy(result, libqasan); + strcat(result, " "); + strcat(result, preload); + + setenv("AFL_PRELOAD", result, 1); + ck_free(result); + + } + + ck_free(libqasan); + + } /* initialize cmplog_mode */ shm.cmplog_mode = 0; -- cgit 1.4.1 From 40f609c7354ffa75dea16401d6c22a4eac510910 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 29 Jan 2021 15:57:47 +0100 Subject: better cmplog arithmetic --- src/afl-fuzz-redqueen.c | 194 ++++++++++++++++++++++++++++-------------------- 1 file changed, 114 insertions(+), 80 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 34db7231..5570520a 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1118,128 +1118,159 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, #ifdef ARITHMETIC_LESSER_GREATER if (lvl < LVL3 || attr == IS_TRANSFORM) { return 0; } + if ((attr & (IS_GREATER | IS_LESSER)) SHAPE_BYTES(h->shape) < 4) { return 0; } + + // transform >= to < and <= to > + if ((attr & IS_EQUAL) && (attr & (IS_GREATER | IS_LESSER))) { + + if (attr & 2) { + + attr += 2; + + } else { + + attr -= 2; + + } + + } + // lesser/greater FP comparison - if ((attr & (IS_LESSER + IS_GREATER)) && - (attr >= IS_FP && attr < IS_FP_MOD)) { + if (attr >= IS_FP && attr < IS_FP_MOD) { - u64 repl_new; - if (SHAPE_BYTES(h->shape) == 4 && its_len >= 4) { + u64 repl_new; - float *f = (float *)&repl; - float g = *f; - g += 1.0; - u32 *r = (u32 *)&g; - repl_new = (u32)*r; + if (attr & IS_GREATER) { - } else if (SHAPE_BYTES(h->shape) == 8 && its_len >= 8) { + if (SHAPE_BYTES(h->shape) == 4 && its_len >= 4) { - double *f = (double *)&repl; - double g = *f; - g += 1.0; + float *f = (float *)&repl; + float g = *f; + g += 1.0; + u32 *r = (u32 *)&g; + repl_new = (u32)*r; - u64 *r = (u64 *)&g; - repl_new = *r; + } else if (SHAPE_BYTES(h->shape) == 8 && its_len >= 8) { - } else { + double *f = (double *)&repl; + double g = *f; + g += 1.0; - return 0; + u64 *r = (u64 *)&g; + repl_new = *r; - } + } else { - changed_val = repl_new; + return 0; - if (unlikely(cmp_extend_encoding(afl, h, pattern, repl_new, o_pattern, - changed_val, 16, idx, taint_len, orig_buf, - buf, cbuf, len, 1, lvl, status))) { + } - return 1; + changed_val = repl_new; - } + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { - if (SHAPE_BYTES(h->shape) == 4) { + return 1; - float *f = (float *)&repl; - float g = *f; - g -= 1.0; - u32 *r = (u32 *)&g; - repl_new = (u32)*r; + } - } else if (SHAPE_BYTES(h->shape) == 8) { + } else { - double *f = (double *)&repl; - double g = *f; - g -= 1.0; - u64 *r = (u64 *)&g; - repl_new = *r; + if (SHAPE_BYTES(h->shape) == 4) { - } else { + float *f = (float *)&repl; + float g = *f; + g -= 1.0; + u32 *r = (u32 *)&g; + repl_new = (u32)*r; - return 0; + } else if (SHAPE_BYTES(h->shape) == 8) { - } + double *f = (double *)&repl; + double g = *f; + g -= 1.0; + u64 *r = (u64 *)&g; + repl_new = *r; - changed_val = repl_new; + } else { - if (unlikely(cmp_extend_encoding(afl, h, pattern, repl_new, o_pattern, - changed_val, 16, idx, taint_len, orig_buf, - buf, cbuf, len, 1, lvl, status))) { + return 0; - return 1; + } - } + changed_val = repl_new; + + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + return 1; - // transform double to float, llvm likes to do that internally ... - if (SHAPE_BYTES(h->shape) == 8 && its_len >= 4) { + } - double *f = (double *)&repl; - float g = (float)*f; - repl_new = 0; + } + + // transform double to float, llvm likes to do that internally ... + if (SHAPE_BYTES(h->shape) == 8 && its_len >= 4) { + + double *f = (double *)&repl; + float g = (float)*f; + repl_new = 0; #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) - memcpy((char *)&repl_new, (char *)&g, 4); + memcpy((char *)&repl_new, (char *)&g, 4); #else - memcpy(((char *)&repl_new) + 4, (char *)&g, 4); + memcpy(((char *)&repl_new) + 4, (char *)&g, 4); #endif - changed_val = repl_new; - h->shape = 3; // modify shape + changed_val = repl_new; + h->shape = 3; // modify shape - // fprintf(stderr, "DOUBLE2FLOAT %llx\n", repl_new); + // fprintf(stderr, "DOUBLE2FLOAT %llx\n", repl_new); - if (unlikely(cmp_extend_encoding( - afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, - taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + h->shape = 7; // recover shape + return 1; + + } h->shape = 7; // recover shape - return 1; } - h->shape = 7; // recover shape - } - } else if ((attr & (IS_LESSER + IS_GREATER)) && attr < IS_FP) { + else if (attr < IS_FP) { // lesser/greater integer comparison u64 repl_new; - repl_new = repl + 1; - changed_val = repl_new; - if (unlikely(cmp_extend_encoding(afl, h, pattern, repl_new, o_pattern, - changed_val, 32, idx, taint_len, orig_buf, - buf, cbuf, len, 1, lvl, status))) { + if (attr & IS_GREATER) { + + repl_new = repl + 1; + changed_val = repl_new; + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 32, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + + return 1; - return 1; + } - } + } else { - repl_new = repl - 1; - changed_val = repl_new; - if (unlikely(cmp_extend_encoding(afl, h, pattern, repl_new, o_pattern, - changed_val, 32, idx, taint_len, orig_buf, - buf, cbuf, len, 1, lvl, status))) { + repl_new = repl - 1; + changed_val = repl_new; + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 32, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { - return 1; + return 1; + + } } @@ -1432,6 +1463,8 @@ static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) { #endif +#define SWAPA(_x) ((_x & 0xf8) + ((_x & 7) ^ 0x07)) + static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, u32 lvl, struct tainted *taint) { @@ -1588,8 +1621,8 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, if (unlikely(cmp_extend_encodingN( afl, h, s128_v1, s128_v0, orig_s128_v1, orig_s128_v0, - h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, - lvl, &status))) { + SWAPA(h->attribute), idx, taint_len, orig_buf, buf, cbuf, len, + 1, lvl, &status))) { return 1; @@ -1634,9 +1667,10 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, status = 0; if ((o->v1 != orig_o->v1 || lvl >= LVL3) && orig_o->v0 != orig_o->v1) { - if (unlikely(cmp_extend_encoding( - afl, h, o->v1, o->v0, orig_o->v1, orig_o->v0, h->attribute, idx, - taint_len, orig_buf, buf, cbuf, len, 1, lvl, &status))) { + if (unlikely(cmp_extend_encoding(afl, h, o->v1, o->v0, orig_o->v1, + orig_o->v0, SWAPA(h->attribute), idx, + taint_len, orig_buf, buf, cbuf, len, 1, + lvl, &status))) { return 1; -- cgit 1.4.1 From 66c290f804636de19017ecc3c9ece4a7af9eed28 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 29 Jan 2021 17:23:19 +0100 Subject: fix compile --- src/afl-fuzz-redqueen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 5570520a..1ef84046 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -31,7 +31,7 @@ //#define _DEBUG #define COMBINE //#define CMPLOG_INTROSPECTION -//#define ARITHMETIC_LESSER_GREATER +#define ARITHMETIC_LESSER_GREATER //#define TRANSFORM //#define TRANSFORM_BASE64 @@ -1118,7 +1118,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, #ifdef ARITHMETIC_LESSER_GREATER if (lvl < LVL3 || attr == IS_TRANSFORM) { return 0; } - if ((attr & (IS_GREATER | IS_LESSER)) SHAPE_BYTES(h->shape) < 4) { return 0; } + if (!(attr & (IS_GREATER | IS_LESSER)) || SHAPE_BYTES(h->shape) < 4) { return 0; } // transform >= to < and <= to > if ((attr & IS_EQUAL) && (attr & (IS_GREATER | IS_LESSER))) { -- cgit 1.4.1 From 8a8ecef6f5a11f3b8b7e43ad7232751aeecc6635 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 29 Jan 2021 18:13:45 +0100 Subject: cmplog lower fail --- src/afl-fuzz-redqueen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 1ef84046..67d505fc 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -2440,8 +2440,8 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { if (!afl->shm.cmp_map->headers[k].hits) { continue; } - if (afl->pass_stats[k].faileds == 0xff || - afl->pass_stats[k].total == 0xff) { + if (afl->pass_stats[k].faileds >= 0x69 || + afl->pass_stats[k].total >= 0x69) { #ifdef _DEBUG fprintf(stderr, "DISABLED %u\n", k); -- cgit 1.4.1 From debd832f36b142e1b0b1bab8a6966848a51878f8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 29 Jan 2021 18:25:25 +0100 Subject: 32bit fix --- src/afl-fuzz-redqueen.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 1ef84046..fc620781 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1700,12 +1700,15 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, if (afl->pass_stats[key].total == 0) { +#ifdef WORD_SIZE_64 if (unlikely(is_n)) { try_to_add_to_dictN(afl, s128_v0, SHAPE_BYTES(h->shape)); try_to_add_to_dictN(afl, s128_v1, SHAPE_BYTES(h->shape)); - } else { + } else +#endif + { try_to_add_to_dict(afl, o->v0, SHAPE_BYTES(h->shape)); try_to_add_to_dict(afl, o->v1, SHAPE_BYTES(h->shape)); -- cgit 1.4.1 From 29c1131fe0851d518d06c9ec8c808098dacb12fb Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 29 Jan 2021 18:32:28 +0100 Subject: working AFL_USE_QASAN --- qemu_mode/libqasan/README.md | 5 ++++- src/afl-fuzz.c | 51 ++++++++++++++++++++++---------------------- 2 files changed, 30 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/qemu_mode/libqasan/README.md b/qemu_mode/libqasan/README.md index 1333ed77..399ebeee 100644 --- a/qemu_mode/libqasan/README.md +++ b/qemu_mode/libqasan/README.md @@ -1 +1,4 @@ -TODO +# QEMU AddressSanitizer Runtime + +This library is the injected runtime used by QEMU AddressSanitizer (QASan). + diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 54850173..312d9424 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -328,31 +328,6 @@ int main(int argc, char **argv_orig, char **envp) { } #endif - if (getenv("AFL_USE_QASAN")) { - - u8* preload = getenv("AFL_PRELOAD"); - u8* libqasan = get_libqasan_path(argv_orig[0]); - - if (!preload) { - - setenv("AFL_PRELOAD", libqasan, 0); - - } else { - - u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); - strcpy(result, libqasan); - strcat(result, " "); - strcat(result, preload); - - setenv("AFL_PRELOAD", result, 1); - ck_free(result); - - } - - ck_free(libqasan); - - } - char **argv = argv_cpy_dup(argc, argv_orig); afl_state_t *afl = calloc(1, sizeof(afl_state_t)); @@ -1009,6 +984,32 @@ int main(int argc, char **argv_orig, char **envp) { usage(argv[0], show_help); } + + if (afl->fsrv.qemu_mode && getenv("AFL_USE_QASAN")) { + + u8* preload = getenv("AFL_PRELOAD"); + u8* libqasan = get_libqasan_path(argv_orig[0]); + + if (!preload) { + + setenv("AFL_PRELOAD", libqasan, 0); + + } else { + + u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); + strcpy(result, libqasan); + strcat(result, " "); + strcat(result, preload); + + setenv("AFL_PRELOAD", result, 1); + ck_free(result); + + } + + afl->afl_env.afl_preload = (u8 *)getenv("AFL_PRELOAD"); + ck_free(libqasan); + + } if (afl->fsrv.mem_limit && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260; -- cgit 1.4.1 From 3b3565269d0453c9f4b5b2847f809cd5d315fff2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 30 Jan 2021 14:57:17 +0100 Subject: foreign sync from ctime to mtime (libfuzzer) --- include/afl-fuzz.h | 2 +- src/afl-fuzz-init.c | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index f46d7707..12db9e4d 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -404,7 +404,7 @@ struct afl_pass_stat { struct foreign_sync { u8 * dir; - time_t ctime; + time_t mtime; }; diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 84f81112..1808f0a1 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -468,7 +468,7 @@ void read_foreign_testcases(afl_state_t *afl, int first) { afl->foreign_syncs[iter].dir[0] != 0) { if (first) ACTF("Scanning '%s'...", afl->foreign_syncs[iter].dir); - time_t ctime_max = 0; + time_t mtime_max = 0; u8 * name = strrchr(afl->foreign_syncs[iter].dir, '/'); if (!name) { name = afl->foreign_syncs[iter].dir; } if (!strcmp(name, "queue") || !strcmp(name, "out") || @@ -482,8 +482,8 @@ void read_foreign_testcases(afl_state_t *afl, int first) { } - /* We do not use sorting yet and do a more expensive ctime check instead. - a ctimesort() implementation would be better though. */ + /* We do not use sorting yet and do a more expensive mtime check instead. + a mtimesort() implementation would be better though. */ nl_cnt = scandir(afl->foreign_syncs[iter].dir, &nl, NULL, NULL); @@ -537,8 +537,8 @@ void read_foreign_testcases(afl_state_t *afl, int first) { } - /* we detect new files by their ctime */ - if (likely(st.st_ctime <= afl->foreign_syncs[iter].ctime)) { + /* we detect new files by their mtime */ + if (likely(st.st_mtime <= afl->foreign_syncs[iter].mtime)) { ck_free(fn2); continue; @@ -600,11 +600,11 @@ void read_foreign_testcases(afl_state_t *afl, int first) { munmap(mem, st.st_size); close(fd); - if (st.st_ctime > ctime_max) ctime_max = st.st_ctime; + if (st.st_mtime > mtime_max) mtime_max = st.st_mtime; } - afl->foreign_syncs[iter].ctime = ctime_max; + afl->foreign_syncs[iter].mtime = mtime_max; free(nl); /* not tracked */ } -- cgit 1.4.1 From 9d08f0d098c91e69b5fe41674e4c5d05363af604 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 30 Jan 2021 15:39:47 +0100 Subject: added AFL_CMPLOG_ONLY_NEW feature --- docs/Changelog.md | 2 + docs/env_variables.md | 5 ++ include/afl-fuzz.h | 2 +- include/common.h | 2 +- include/envs.h | 1 + src/afl-analyze.c | 22 ++++---- src/afl-common.c | 6 +-- src/afl-fuzz-init.c | 3 ++ src/afl-fuzz-one.c | 5 +- src/afl-fuzz-redqueen.c | 135 +++++++++++++++++++++++++----------------------- src/afl-fuzz-state.c | 7 +++ src/afl-fuzz.c | 35 +++++++------ src/afl-showmap.c | 24 ++++----- src/afl-tmin.c | 24 ++++----- 14 files changed, 147 insertions(+), 126 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 329b7520..6e59961b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -20,6 +20,8 @@ sending a mail to . transformations (e.g. toupper, tolower, to/from hex, xor, arithmetics, etc.). this is costly hence new command line option -l that sets the intensity (values 1 to 3). recommended is 1 or 2. + - added `AFL_CMPLOG_ONLY_NEW` to not use cmplog on initial testcases from + `-i` or resumes (as these have most likely already been done) - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) - if determinstic mode is active (-D, or -M without -d) then we sync diff --git a/docs/env_variables.md b/docs/env_variables.md index 66d85749..4c3b1cfb 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -287,6 +287,11 @@ checks or alter some of the more exotic semantics of the tool: the target. This must be equal or larger than the size the target was compiled with. + - `AFL_CMPLOG_ONLY_NEW` will only perform the expensive cmplog feature for + newly found testcases and not for testcases that are loaded on startup + (`-i in`). This is an important feature to set when resuming a fuzzing + session. + - `AFL_TESTCACHE_SIZE` allows you to override the size of `#define TESTCASE_CACHE` in config.h. Recommended values are 50-250MB - or more if your fuzzing finds a huge amount of paths for large inputs. diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 12db9e4d..e8a21cb5 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -384,7 +384,7 @@ typedef struct afl_env_vars { afl_dumb_forksrv, afl_import_first, afl_custom_mutator_only, afl_no_ui, afl_force_ui, afl_i_dont_care_about_missing_crashes, afl_bench_just_one, afl_bench_until_crash, afl_debug_child, afl_autoresume, afl_cal_fast, - afl_cycle_schedules, afl_expand_havoc, afl_statsd; + afl_cycle_schedules, afl_expand_havoc, afl_statsd, afl_cmplog_only_new; u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload, diff --git a/include/common.h b/include/common.h index bdaa1735..bb8831f2 100644 --- a/include/common.h +++ b/include/common.h @@ -47,7 +47,7 @@ void argv_cpy_free(char **argv); char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv); char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv); char * get_afl_env(char *env); -u8 *get_libqasan_path(u8 *own_loc); +u8 * get_libqasan_path(u8 *own_loc); extern u8 be_quiet; extern u8 *doc_path; /* path to documentation dir */ diff --git a/include/envs.h b/include/envs.h index 926c9e27..210b34a6 100644 --- a/include/envs.h +++ b/include/envs.h @@ -28,6 +28,7 @@ static char *afl_environment_variables[] = { "AFL_CC", "AFL_CMIN_ALLOW_ANY", "AFL_CMIN_CRASHES_ONLY", + "AFL_CMPLOG_ONLY_NEW", "AFL_CODE_END", "AFL_CODE_START", "AFL_COMPCOV_BINNAME", diff --git a/src/afl-analyze.c b/src/afl-analyze.c index 28598ba0..20aef2da 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -1079,28 +1079,28 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !in_file) { usage(argv[0]); } if (qemu_mode && getenv("AFL_USE_QASAN")) { - - u8* preload = getenv("AFL_PRELOAD"); - u8* libqasan = get_libqasan_path(argv_orig[0]); - + + u8 *preload = getenv("AFL_PRELOAD"); + u8 *libqasan = get_libqasan_path(argv_orig[0]); + if (!preload) { - + setenv("AFL_PRELOAD", libqasan, 0); - + } else { - + u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); strcpy(result, libqasan); strcat(result, " "); strcat(result, preload); - + setenv("AFL_PRELOAD", result, 1); ck_free(result); - + } - + ck_free(libqasan); - + } map_size = get_map_size(); diff --git a/src/afl-common.c b/src/afl-common.c index a69f2e97..235c4c05 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -364,11 +364,7 @@ u8 *get_libqasan_path(u8 *own_loc) { cp = alloc_printf("%s/libqasan.so", own_copy); ck_free(own_copy); - if (!access(cp, X_OK)) { - - return cp; - - } + if (!access(cp, X_OK)) { return cp; } } else { diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 1808f0a1..2a7864f9 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -25,6 +25,7 @@ #include "afl-fuzz.h" #include +#include "cmplog.h" #ifdef HAVE_AFFINITY @@ -833,6 +834,8 @@ void perform_dry_run(afl_state_t *afl) { } + if (afl->afl_env.afl_cmplog_only_new) { q->colorized = CMPLOG_LVL_MAX; } + u8 *fn = strrchr(q->fname, '/') + 1; ACTF("Attempting dry run with '%s'...", fn); diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index ff766158..0cf889a8 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -26,6 +26,7 @@ #include "afl-fuzz.h" #include #include +#include "cmplog.h" /* MOpt */ @@ -553,7 +554,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (unlikely(len < 4)) { - afl->queue_cur->colorized = 0xff; + afl->queue_cur->colorized = CMPLOG_LVL_MAX; } else { @@ -2981,7 +2982,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { if (unlikely(len < 4)) { - afl->queue_cur->colorized = 0xff; + afl->queue_cur->colorized = CMPLOG_LVL_MAX; } else { diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index fc620781..d7657c1d 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1118,7 +1118,11 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, #ifdef ARITHMETIC_LESSER_GREATER if (lvl < LVL3 || attr == IS_TRANSFORM) { return 0; } - if (!(attr & (IS_GREATER | IS_LESSER)) || SHAPE_BYTES(h->shape) < 4) { return 0; } + if (!(attr & (IS_GREATER | IS_LESSER)) || SHAPE_BYTES(h->shape) < 4) { + + return 0; + + } // transform >= to < and <= to > if ((attr & IS_EQUAL) && (attr & (IS_GREATER | IS_LESSER))) { @@ -1138,110 +1142,110 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // lesser/greater FP comparison if (attr >= IS_FP && attr < IS_FP_MOD) { - u64 repl_new; - - if (attr & IS_GREATER) { + u64 repl_new; - if (SHAPE_BYTES(h->shape) == 4 && its_len >= 4) { + if (attr & IS_GREATER) { - float *f = (float *)&repl; - float g = *f; - g += 1.0; - u32 *r = (u32 *)&g; - repl_new = (u32)*r; + if (SHAPE_BYTES(h->shape) == 4 && its_len >= 4) { - } else if (SHAPE_BYTES(h->shape) == 8 && its_len >= 8) { + float *f = (float *)&repl; + float g = *f; + g += 1.0; + u32 *r = (u32 *)&g; + repl_new = (u32)*r; - double *f = (double *)&repl; - double g = *f; - g += 1.0; + } else if (SHAPE_BYTES(h->shape) == 8 && its_len >= 8) { - u64 *r = (u64 *)&g; - repl_new = *r; + double *f = (double *)&repl; + double g = *f; + g += 1.0; - } else { + u64 *r = (u64 *)&g; + repl_new = *r; - return 0; + } else { - } + return 0; - changed_val = repl_new; + } - if (unlikely(cmp_extend_encoding( - afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, - taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + changed_val = repl_new; - return 1; + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { - } + return 1; - } else { + } - if (SHAPE_BYTES(h->shape) == 4) { + } else { - float *f = (float *)&repl; - float g = *f; - g -= 1.0; - u32 *r = (u32 *)&g; - repl_new = (u32)*r; + if (SHAPE_BYTES(h->shape) == 4) { - } else if (SHAPE_BYTES(h->shape) == 8) { + float *f = (float *)&repl; + float g = *f; + g -= 1.0; + u32 *r = (u32 *)&g; + repl_new = (u32)*r; - double *f = (double *)&repl; - double g = *f; - g -= 1.0; - u64 *r = (u64 *)&g; - repl_new = *r; + } else if (SHAPE_BYTES(h->shape) == 8) { - } else { + double *f = (double *)&repl; + double g = *f; + g -= 1.0; + u64 *r = (u64 *)&g; + repl_new = *r; - return 0; + } else { - } + return 0; - changed_val = repl_new; + } - if (unlikely(cmp_extend_encoding( - afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, - taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + changed_val = repl_new; - return 1; + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { - } + return 1; } - // transform double to float, llvm likes to do that internally ... - if (SHAPE_BYTES(h->shape) == 8 && its_len >= 4) { + } - double *f = (double *)&repl; - float g = (float)*f; - repl_new = 0; + // transform double to float, llvm likes to do that internally ... + if (SHAPE_BYTES(h->shape) == 8 && its_len >= 4) { + + double *f = (double *)&repl; + float g = (float)*f; + repl_new = 0; #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) - memcpy((char *)&repl_new, (char *)&g, 4); + memcpy((char *)&repl_new, (char *)&g, 4); #else - memcpy(((char *)&repl_new) + 4, (char *)&g, 4); + memcpy(((char *)&repl_new) + 4, (char *)&g, 4); #endif - changed_val = repl_new; - h->shape = 3; // modify shape - - // fprintf(stderr, "DOUBLE2FLOAT %llx\n", repl_new); + changed_val = repl_new; + h->shape = 3; // modify shape - if (unlikely(cmp_extend_encoding( - afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, - taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + // fprintf(stderr, "DOUBLE2FLOAT %llx\n", repl_new); - h->shape = 7; // recover shape - return 1; - - } + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { h->shape = 7; // recover shape + return 1; } + h->shape = 7; // recover shape + } + } + else if (attr < IS_FP) { // lesser/greater integer comparison @@ -1707,6 +1711,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, try_to_add_to_dictN(afl, s128_v1, SHAPE_BYTES(h->shape)); } else + #endif { diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 8423a3d1..5040e3ef 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -236,6 +236,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_custom_mutator_only = get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_CMPLOG_ONLY_NEW", + + afl_environment_variable_len)) { + + afl->afl_env.afl_cmplog_only_new = + get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_NO_UI", afl_environment_variable_len)) { afl->afl_env.afl_no_ui = diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 312d9424..9d9b0434 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -181,6 +181,7 @@ static void usage(u8 *argv0, int more_help) { "AFL_AUTORESUME: resume fuzzing if directory specified by -o already exists\n" "AFL_BENCH_JUST_ONE: run the target just once\n" "AFL_BENCH_UNTIL_CRASH: exit soon when the first crashing input has been found\n" + "AFL_CMPLOG_ONLY_NEW: do not run cmplog on initial testcases (good for resumes!)\n" "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" "AFL_CUSTOM_MUTATOR_LIBRARY: lib with afl_custom_fuzz() to mutate inputs\n" "AFL_CUSTOM_MUTATOR_ONLY: avoid AFL++'s internal mutators\n" @@ -326,8 +327,9 @@ int main(int argc, char **argv_orig, char **envp) { "compile time)"); } + #endif - + char **argv = argv_cpy_dup(argc, argv_orig); afl_state_t *afl = calloc(1, sizeof(afl_state_t)); @@ -356,8 +358,7 @@ int main(int argc, char **argv_orig, char **envp) { while ((opt = getopt( argc, argv, - "+b:B:c:CdDe:E:hi:I:f:F:l:L:m:M:nNo:p:RQs:S:t:T:UV:Wx:Z")) > - 0) { + "+b:B:c:CdDe:E:hi:I:f:F:l:L:m:M:nNo:p:RQs:S:t:T:UV:Wx:Z")) > 0) { switch (opt) { @@ -984,31 +985,31 @@ int main(int argc, char **argv_orig, char **envp) { usage(argv[0], show_help); } - + if (afl->fsrv.qemu_mode && getenv("AFL_USE_QASAN")) { - - u8* preload = getenv("AFL_PRELOAD"); - u8* libqasan = get_libqasan_path(argv_orig[0]); - + + u8 *preload = getenv("AFL_PRELOAD"); + u8 *libqasan = get_libqasan_path(argv_orig[0]); + if (!preload) { - + setenv("AFL_PRELOAD", libqasan, 0); - + } else { - + u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); strcpy(result, libqasan); strcat(result, " "); strcat(result, preload); - + setenv("AFL_PRELOAD", result, 1); ck_free(result); - + } - + afl->afl_env.afl_preload = (u8 *)getenv("AFL_PRELOAD"); ck_free(libqasan); - + } if (afl->fsrv.mem_limit && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260; @@ -1270,7 +1271,7 @@ int main(int argc, char **argv_orig, char **envp) { "instead of using AFL_PRELOAD?"); } - + if (afl->afl_env.afl_preload) { if (afl->fsrv.qemu_mode) { @@ -1322,7 +1323,7 @@ int main(int argc, char **argv_orig, char **envp) { FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD"); } - + save_cmdline(afl, argc, argv); fix_up_banner(afl, argv[optind]); diff --git a/src/afl-showmap.c b/src/afl-showmap.c index f3cd5a90..62bf1021 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -942,30 +942,30 @@ int main(int argc, char **argv_orig, char **envp) { } if (optind == argc || !out_file) { usage(argv[0]); } - + if (fsrv->qemu_mode && getenv("AFL_USE_QASAN")) { - - u8* preload = getenv("AFL_PRELOAD"); - u8* libqasan = get_libqasan_path(argv_orig[0]); - + + u8 *preload = getenv("AFL_PRELOAD"); + u8 *libqasan = get_libqasan_path(argv_orig[0]); + if (!preload) { - + setenv("AFL_PRELOAD", libqasan, 0); - + } else { - + u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); strcpy(result, libqasan); strcat(result, " "); strcat(result, preload); - + setenv("AFL_PRELOAD", result, 1); ck_free(result); - + } - + ck_free(libqasan); - + } if (in_dir) { diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 9e9e2d63..09b5211d 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -1074,30 +1074,30 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !in_file || !output_file) { usage(argv[0]); } check_environment_vars(envp); - + if (fsrv->qemu_mode && getenv("AFL_USE_QASAN")) { - - u8* preload = getenv("AFL_PRELOAD"); - u8* libqasan = get_libqasan_path(argv_orig[0]); - + + u8 *preload = getenv("AFL_PRELOAD"); + u8 *libqasan = get_libqasan_path(argv_orig[0]); + if (!preload) { - + setenv("AFL_PRELOAD", libqasan, 0); - + } else { - + u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); strcpy(result, libqasan); strcat(result, " "); strcat(result, preload); - + setenv("AFL_PRELOAD", result, 1); ck_free(result); - + } - + ck_free(libqasan); - + } /* initialize cmplog_mode */ -- cgit 1.4.1 From 893cd47d9cdbfa44e43d03e7d40a56a0c2ad7936 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 31 Jan 2021 13:03:00 +0100 Subject: disable trimming for -M --- README.md | 1 + docs/ideas.md | 15 ++------------- src/afl-fuzz.c | 3 ++- 3 files changed, 5 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/README.md b/README.md index 2806b734..8c4aab93 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ behaviours and defaults: * a caching of testcases can now be performed and can be modified by editing config.h for TESTCASE_CACHE or by specifying the env variable `AFL_TESTCACHE_SIZE` (in MB). Good values are between 50-500 (default: 50). + * -M mains do not perform trimming * examples/ got renamed to utils/ * libtokencap/ libdislocator/ and qdbi_mode/ were moved to utils/ * afl-cmin/afl-cmin.bash now search first in PATH and last in AFL_PATH diff --git a/docs/ideas.md b/docs/ideas.md index aaa3eed1..7cbe60a5 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -16,6 +16,8 @@ test cases executed. It should be clickable which value is X and Y axis, zoom factor, log scaling on-off, etc. +Mentor: vanhauser-thc + ## WASM Instrumentation Currently, AFL++ can be used for source code fuzzing and traditional binaries. @@ -36,19 +38,6 @@ Either improve a single mutator thorugh learning of many different bugs Mentor: domenukk -## Collision-free Binary-Only Maps - -AFL++ supports collison-free maps using an LTO (link-time-optimization) pass. -This should be possible to implement for QEMU and Unicorn instrumentations. -As the forkserver parent caches just in time translated translation blocks, -adding a simple counter between jumps should be doable. - -Note: this is already in development for qemu by Andrea, so for people who -want to contribute it might make more sense to port his solution to unicorn. - -Mentor: andreafioraldi or domenukk -Issue/idea tracker: [https://github.com/AFLplusplus/AFLplusplus/issues/237](https://github.com/AFLplusplus/AFLplusplus/issues/237) - ## Your idea! Finally, we are open to proposals! diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 9d9b0434..647a665e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -145,7 +145,7 @@ static void usage(u8 *argv0, int more_help) { "Other stuff:\n" " -M/-S id - distributed mode (see docs/parallel_fuzzing.md)\n" - " -M auto-sets -D and -Z (use -d to disable -D)\n" + " -M auto-sets -D, -Z (use -d to disable -D) and no trimming\n" " -F path - sync to a foreign fuzzer queue directory (requires " "-M, can\n" " be specified up to %u times)\n" @@ -502,6 +502,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->sync_id = ck_strdup(optarg); afl->skip_deterministic = 0; // force deterministic fuzzing afl->old_seed_selection = 1; // force old queue walking seed selection + afl->disable_trim = 1; // disable trimming if ((c = strchr(afl->sync_id, ':'))) { -- cgit 1.4.1 From 7a861498c27997cd7be01a5650d54cff3b87a02e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 31 Jan 2021 15:04:40 +0100 Subject: added support for __afl_coverage_interesting --- docs/Changelog.md | 3 ++ instrumentation/README.instrument_list.md | 7 +++++ instrumentation/SanitizerCoverageLTO.so.cc | 20 ++++++++++++++ instrumentation/SanitizerCoveragePCGUARD.so.cc | 38 +++++++++++++++++++++++--- src/afl-cc.c | 16 ++++++----- src/afl-fuzz.c | 5 ++-- 6 files changed, 76 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 6e59961b..99bc8b47 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -36,6 +36,9 @@ sending a mail to . - cmplog/redqueen now also tracks floating point, _ExtInt() + 128bit - cmplog/redqueen can now process basic libc++ and libstdc++ std::string comparisons (though no position or length type variants) + - added support for __afl_coverage_interesting() for LTO and + and our own PCGUARD (llvm 10.0.1+), read more about this function + and selective coverage in instrumentation/README.instrument_list.md - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang diff --git a/instrumentation/README.instrument_list.md b/instrumentation/README.instrument_list.md index b47b50f6..b7dfb40c 100644 --- a/instrumentation/README.instrument_list.md +++ b/instrumentation/README.instrument_list.md @@ -43,6 +43,13 @@ in any function where you want: * `__AFL_COVERAGE_DISCARD();` - reset all coverage gathered until this point * `__AFL_COVERAGE_SKIP();` - mark this test case as unimportant. Whatever happens, afl-fuzz will ignore it. +A special function is `__afl_coverage_interesting`. +To use this, you must define `void __afl_coverage_interesting(u8 val, u32 id);`. +Then you can use this function globally, where the `val` parameter can be set +by you, the `id` parameter is for afl-fuzz and will be overwritten. +Note that useful parameters are for `val` are: 1, 2, 3, 4, 8, 16, 32, 64, 128. +A value of e.g. 33 will be seen as 32 for coverage purposes. + ## 3) Selective instrumenation with AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST This feature is equivalent to llvm 12 sancov feature and allows to specify diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index 016ac71f..e3490847 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -1237,6 +1237,25 @@ void ModuleSanitizerCoverage::instrumentFunction( for (auto &BB : F) { + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast(&IN))) { + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + StringRef FuncName = Callee->getName(); + if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue; + + Value *val = ConstantInt::get(Int32Ty, ++afl_global_id); + callInst->setOperand(1, val); + + } + + } + if (shouldInstrumentBlock(F, &BB, DT, PDT, Options)) BlocksToInstrument.push_back(&BB); for (auto &Inst : BB) { @@ -1338,6 +1357,7 @@ bool ModuleSanitizerCoverage::InjectCoverage(Function & F, if (AllBlocks.empty()) return false; CreateFunctionLocalArrays(F, AllBlocks); + for (size_t i = 0, N = AllBlocks.size(); i < N; i++) { // afl++ START diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index ecd6bc9b..5b274770 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -311,7 +311,8 @@ class ModuleSanitizerCoverage { Function &F, Type *Ty, const char *Section); GlobalVariable *CreatePCArray(Function &F, ArrayRef AllBlocks); - void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks); + void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks, + uint32_t special); void InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx, bool IsLeafFunc = true); Function *CreateInitCallsForSections(Module &M, const char *CtorName, @@ -970,11 +971,11 @@ GlobalVariable *ModuleSanitizerCoverage::CreatePCArray( } void ModuleSanitizerCoverage::CreateFunctionLocalArrays( - Function &F, ArrayRef AllBlocks) { + Function &F, ArrayRef AllBlocks, uint32_t special) { if (Options.TracePCGuard) FunctionGuardArray = CreateFunctionLocalArrayInSection( - AllBlocks.size(), F, Int32Ty, SanCovGuardsSectionName); + AllBlocks.size() + special, F, Int32Ty, SanCovGuardsSectionName); if (Options.Inline8bitCounters) Function8bitCounterArray = CreateFunctionLocalArrayInSection( @@ -993,9 +994,38 @@ bool ModuleSanitizerCoverage::InjectCoverage(Function & F, bool IsLeafFunc) { if (AllBlocks.empty()) return false; - CreateFunctionLocalArrays(F, AllBlocks); + + uint32_t special = 0; + for (auto &BB : F) { + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast(&IN))) { + + Function *Callee = callInst->getCalledFunction(); + StringRef FuncName = Callee->getName(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue; + + uint32_t id = 1 + instr + (uint32_t)AllBlocks.size() + special++; + Value * val = ConstantInt::get(Int32Ty, id); + callInst->setOperand(1, val); + + } + + } + + } + + CreateFunctionLocalArrays(F, AllBlocks, special); for (size_t i = 0, N = AllBlocks.size(); i < N; i++) InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc); + + instr += special; + return true; } diff --git a/src/afl-cc.c b/src/afl-cc.c index b5dcb632..f513764a 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -572,7 +572,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; - if (instrument_mode == INSTRUMENT_CFG) + if (instrument_mode == INSTRUMENT_CFG || + instrument_mode == INSTRUMENT_PCGUARD) cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path); else @@ -1670,15 +1671,16 @@ int main(int argc, char **argv, char **envp) { if (compiler_mode == LTO) { if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO || - instrument_mode == INSTRUMENT_CFG) { + instrument_mode == INSTRUMENT_CFG || + instrument_mode == INSTRUMENT_PCGUARD) { lto_mode = 1; - if (!instrument_mode) { + // force CFG + // if (!instrument_mode) { - instrument_mode = INSTRUMENT_CFG; - // ptr = instrument_mode_string[instrument_mode]; - - } + instrument_mode = INSTRUMENT_PCGUARD; + // ptr = instrument_mode_string[instrument_mode]; + // } } else if (instrument_mode == INSTRUMENT_LTO || diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 647a665e..82eff61f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -145,7 +145,8 @@ static void usage(u8 *argv0, int more_help) { "Other stuff:\n" " -M/-S id - distributed mode (see docs/parallel_fuzzing.md)\n" - " -M auto-sets -D, -Z (use -d to disable -D) and no trimming\n" + " -M auto-sets -D, -Z (use -d to disable -D) and no " + "trimming\n" " -F path - sync to a foreign fuzzer queue directory (requires " "-M, can\n" " be specified up to %u times)\n" @@ -502,7 +503,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->sync_id = ck_strdup(optarg); afl->skip_deterministic = 0; // force deterministic fuzzing afl->old_seed_selection = 1; // force old queue walking seed selection - afl->disable_trim = 1; // disable trimming + afl->disable_trim = 1; // disable trimming if ((c = strchr(afl->sync_id, ':'))) { -- cgit 1.4.1 From e5116c6d55185177413104cad1232ca64e04b844 Mon Sep 17 00:00:00 2001 From: aflpp Date: Sun, 31 Jan 2021 17:29:37 +0100 Subject: fix -Z, remove q->next --- include/afl-fuzz.h | 4 +- include/xxhash.h | 2 +- instrumentation/compare-transform-pass.so.cc | 2 +- src/afl-fuzz-init.c | 102 +++++++++++---------------- src/afl-fuzz-one.c | 3 +- src/afl-fuzz-queue.c | 39 +++++----- src/afl-fuzz-stats.c | 10 +-- src/afl-fuzz.c | 65 +++++------------ utils/afl_untracer/afl-untracer.c | 2 +- utils/libtokencap/libtokencap.so.c | 10 +-- utils/persistent_mode/persistent_demo_new.c | 2 +- utils/persistent_mode/test-instr.c | 2 +- 12 files changed, 100 insertions(+), 143 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index e8a21cb5..9b27606c 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -154,6 +154,7 @@ struct queue_entry { u8 *fname; /* File name for the test case */ u32 len; /* Input length */ + u32 id; /* entry number in queue_buf */ u8 colorized, /* Do not run redqueen stage again */ cal_failed; /* Calibration failed? */ @@ -191,8 +192,7 @@ struct queue_entry { u8 * cmplog_colorinput; /* the result buf of colorization */ struct tainted *taint; /* Taint information from CmpLog */ - struct queue_entry *mother, /* queue entry this based on */ - *next; /* Next element, if any */ + struct queue_entry *mother; /* queue entry this based on */ }; diff --git a/include/xxhash.h b/include/xxhash.h index 006d3f3d..3bd56d13 100644 --- a/include/xxhash.h +++ b/include/xxhash.h @@ -287,7 +287,7 @@ typedef uint32_t XXH32_hash_t; #else #include #if UINT_MAX == 0xFFFFFFFFUL -typedef unsigned int XXH32_hash_t; +typedef unsigned int XXH32_hash_t; #else #if ULONG_MAX == 0xFFFFFFFFUL typedef unsigned long XXH32_hash_t; diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index da5cf7e9..932540a7 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -68,7 +68,7 @@ class CompareTransform : public ModulePass { const char *getPassName() const override { #else - StringRef getPassName() const override { + StringRef getPassName() const override { #endif return "transforms compare functions"; diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 2a7864f9..56dae48c 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -817,12 +817,15 @@ void read_testcases(afl_state_t *afl, u8 *directory) { void perform_dry_run(afl_state_t *afl) { - struct queue_entry *q = afl->queue; - u32 cal_failures = 0; + struct queue_entry *q; + u32 cal_failures = 0, idx; u8 * skip_crashes = afl->afl_env.afl_skip_crashes; u8 * use_mem; - while (q) { + for (idx = 0; idx < afl->queued_paths; idx++) { + + q = afl->queue_buf[idx]; + if (unlikely(q->disabled)) { continue; } u8 res; s32 fd; @@ -1052,20 +1055,22 @@ void perform_dry_run(afl_state_t *afl) { p->disabled = 1; p->perf_score = 0; - while (p && p->next != q) - p = p->next; - if (p) - p->next = q->next; - else - afl->queue = q->next; + u32 i = 0; + while (unlikely(afl->queue_buf[i]->disabled)) { + + ++i; + + } + + afl->queue = afl->queue_buf[i]; afl->max_depth = 0; - p = afl->queue; - while (p) { + for (i = 0; i < afl->queued_paths; i++) { - if (p->depth > afl->max_depth) afl->max_depth = p->depth; - p = p->next; + if (!afl->queue_buf[i]->disabled && + afl->queue_buf[i]->depth > afl->max_depth) + afl->max_depth = afl->queue_buf[i]->depth; } @@ -1098,8 +1103,6 @@ void perform_dry_run(afl_state_t *afl) { } - q = q->next; - } if (cal_failures) { @@ -1125,31 +1128,23 @@ void perform_dry_run(afl_state_t *afl) { /* Now we remove all entries from the queue that have a duplicate trace map */ - q = afl->queue; - struct queue_entry *p, *prev = NULL; - int duplicates = 0; - -restart_outer_cull_loop: + u32 duplicates = 0, i; - while (q) { + for (idx = 0; idx < afl->queued_paths; idx++) { - if (q->cal_failed || !q->exec_cksum) { goto next_entry; } + q = afl->queue_buf[idx]; + if (q->disabled || q->cal_failed || !q->exec_cksum) { continue; } - restart_inner_cull_loop: + u32 done = 0; + for (i = idx + 1; i < afl->queued_paths && !done; i++) { - p = q->next; + struct queue_entry *p = afl->queue_buf[i]; + if (p->disabled || p->cal_failed || !p->exec_cksum) { continue; } - while (p) { - - if (!p->cal_failed && p->exec_cksum == q->exec_cksum) { + if (p->exec_cksum == q->exec_cksum) { duplicates = 1; - // We do not remove any of the memory allocated because for - // splicing the data might still be interesting. - // We only decouple them from the linked list. - // This will result in some leaks at exit, but who cares. - // we keep the shorter file if (p->len >= q->len) { @@ -1163,8 +1158,6 @@ restart_outer_cull_loop: p->disabled = 1; p->perf_score = 0; - q->next = p->next; - goto restart_inner_cull_loop; } else { @@ -1178,35 +1171,26 @@ restart_outer_cull_loop: q->disabled = 1; q->perf_score = 0; - if (prev) - prev->next = q = p; - else - afl->queue = q = p; - goto restart_outer_cull_loop; + + done = 1; } } - p = p->next; - } - next_entry: - - prev = q; - q = q->next; - } if (duplicates) { afl->max_depth = 0; - q = afl->queue; - while (q) { - if (q->depth > afl->max_depth) afl->max_depth = q->depth; - q = q->next; + for (idx = 0; idx < afl->queued_paths; idx++) { + + if (!afl->queue_buf[idx]->disabled && + afl->queue_buf[idx]->depth > afl->max_depth) + afl->max_depth = afl->queue_buf[idx]->depth; } @@ -1256,11 +1240,15 @@ static void link_or_copy(u8 *old_path, u8 *new_path) { void pivot_inputs(afl_state_t *afl) { struct queue_entry *q = afl->queue; - u32 id = 0; + u32 id = 0, i; ACTF("Creating hard links for all input files..."); - while (q) { + for (i = 0; i < afl->queued_paths; i++) { + + q = afl->queue_buf[i]; + + if (unlikely(q->disabled)) { continue; } u8 *nfn, *rsl = strrchr(q->fname, '/'); u32 orig_id; @@ -1288,19 +1276,14 @@ void pivot_inputs(afl_state_t *afl) { afl->resuming_fuzz = 1; nfn = alloc_printf("%s/queue/%s", afl->out_dir, rsl); - /* Since we're at it, let's also try to find parent and figure out the + /* Since we're at it, let's also get the parent and figure out the appropriate depth for this entry. */ src_str = strchr(rsl + 3, ':'); if (src_str && sscanf(src_str + 1, "%06u", &src_id) == 1) { - struct queue_entry *s = afl->queue; - while (src_id-- && s) { - - s = s->next; - - } + struct queue_entry *s = afl->queue_buf[src_id]; if (s) { q->depth = s->depth + 1; } @@ -1348,7 +1331,6 @@ void pivot_inputs(afl_state_t *afl) { if (q->passed_det) { mark_as_det_done(afl, q); } - q = q->next; ++id; } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 0cf889a8..18291fb7 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -544,7 +544,8 @@ u8 fuzz_one_original(afl_state_t *afl) { if (likely(!afl->old_seed_selection)) orig_perf = perf_score = afl->queue_cur->perf_score; else - orig_perf = perf_score = calculate_score(afl, afl->queue_cur); + afl->queue_cur->perf_score = orig_perf = perf_score = + calculate_score(afl, afl->queue_cur); if (unlikely(perf_score <= 0)) { goto abandon_entry; } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 4442b400..ad3e3b8e 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -143,7 +143,7 @@ void create_alias_table(afl_state_t *afl) { struct queue_entry *q = afl->queue_buf[i]; - if (!q->disabled) { q->perf_score = calculate_score(afl, q); } + if (likely(!q->disabled)) { q->perf_score = calculate_score(afl, q); } sum += q->perf_score; @@ -444,7 +444,6 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { if (afl->queue_top) { - afl->queue_top->next = q; afl->queue_top = q; } else { @@ -465,6 +464,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { AFL_BUF_PARAM(queue), afl->queued_paths * sizeof(struct queue_entry *)); if (unlikely(!queue_buf)) { PFATAL("alloc"); } queue_buf[afl->queued_paths - 1] = q; + q->id = afl->queued_paths - 1; afl->last_path_time = get_cur_time(); @@ -641,10 +641,9 @@ void cull_queue(afl_state_t *afl) { if (likely(!afl->score_changed || afl->non_instrumented_mode)) { return; } - struct queue_entry *q; - u32 len = (afl->fsrv.map_size >> 3); - u32 i; - u8 * temp_v = afl->map_tmp_buf; + u32 len = (afl->fsrv.map_size >> 3); + u32 i; + u8 *temp_v = afl->map_tmp_buf; afl->score_changed = 0; @@ -653,12 +652,9 @@ void cull_queue(afl_state_t *afl) { afl->queued_favored = 0; afl->pending_favored = 0; - q = afl->queue; - - while (q) { + for (i = 0; i < afl->queued_paths; i++) { - q->favored = 0; - q = q->next; + afl->queue_buf[i]->favored = 0; } @@ -697,12 +693,13 @@ void cull_queue(afl_state_t *afl) { } - q = afl->queue; + for (i = 0; i < afl->queued_paths; i++) { + + if (likely(!afl->queue_buf[i]->disabled)) { - while (q) { + mark_as_redundant(afl, afl->queue_buf[i], !afl->queue_buf[i]->favored); - mark_as_redundant(afl, q, !q->favored); - q = q->next; + } } @@ -852,13 +849,15 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { // Don't modify perf_score for unfuzzed seeds if (q->fuzz_level == 0) break; - struct queue_entry *queue_it = afl->queue; - while (queue_it) { + u32 i; + for (i = 0; i < afl->queued_paths; i++) { - fuzz_mu += log2(afl->n_fuzz[q->n_fuzz_entry]); - n_paths++; + if (likely(!afl->queue_buf[i]->disabled)) { - queue_it = queue_it->next; + fuzz_mu += log2(afl->n_fuzz[afl->queue_buf[i]->n_fuzz_entry]); + n_paths++; + + } } diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 82da8176..7e99bf8f 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -1014,8 +1014,8 @@ void show_stats(afl_state_t *afl) { void show_init_stats(afl_state_t *afl) { - struct queue_entry *q = afl->queue; - u32 min_bits = 0, max_bits = 0, max_len = 0, count = 0; + struct queue_entry *q; + u32 min_bits = 0, max_bits = 0, max_len = 0, count = 0, i; u64 min_us = 0, max_us = 0; u64 avg_us = 0; @@ -1028,7 +1028,10 @@ void show_init_stats(afl_state_t *afl) { } - while (q) { + for (i = 0; i < afl->queued_paths; i++) { + + q = afl->queue_buf[i]; + if (unlikely(q->disabled)) { continue; } if (!min_us || q->exec_us < min_us) { min_us = q->exec_us; } if (q->exec_us > max_us) { max_us = q->exec_us; } @@ -1039,7 +1042,6 @@ void show_init_stats(afl_state_t *afl) { if (q->len > max_len) { max_len = q->len; } ++count; - q = q->next; } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 9d9b0434..40d42c11 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1558,45 +1558,6 @@ int main(int argc, char **argv_orig, char **envp) { perform_dry_run(afl); - /* - if (!user_set_cache && afl->q_testcase_max_cache_size) { - - / * The user defined not a fixed number of entries for the cache. - Hence we autodetect a good value. After the dry run inputs are - trimmed and we know the average and max size of the input seeds. - We use this information to set a fitting size to max entries - based on the cache size. * / - - struct queue_entry *q = afl->queue; - u64 size = 0, count = 0, avg = 0, max = 0; - - while (q) { - - ++count; - size += q->len; - if (max < q->len) { max = q->len; } - q = q->next; - - } - - if (count) { - - avg = size / count; - avg = ((avg + max) / 2) + 1; - - } - - if (avg < 10240) { avg = 10240; } - - afl->q_testcase_max_cache_entries = afl->q_testcase_max_cache_size / avg; - - if (afl->q_testcase_max_cache_entries > 32768) - afl->q_testcase_max_cache_entries = 32768; - - } - - */ - if (afl->q_testcase_max_cache_entries) { afl->q_testcase_cache = @@ -1668,7 +1629,10 @@ int main(int argc, char **argv_orig, char **envp) { if (unlikely(afl->old_seed_selection)) { afl->current_entry = 0; - afl->queue_cur = afl->queue; + while (unlikely(afl->queue_buf[afl->current_entry]->disabled)) { + ++afl->current_entry; + } + afl->queue_cur = afl->queue_buf[afl->current_entry]; if (unlikely(seek_to)) { @@ -1800,12 +1764,14 @@ int main(int argc, char **argv_orig, char **envp) { } - struct queue_entry *q = afl->queue; // we must recalculate the scores of all queue entries - while (q) { + for (i = 0; i < (s32)afl->queued_paths; i++) { + + if (likely(!afl->queue_buf[i]->disabled)) { - update_bitmap_score(afl, q); - q = q->next; + update_bitmap_score(afl, afl->queue_buf[i]); + + } } @@ -1847,8 +1813,15 @@ int main(int argc, char **argv_orig, char **envp) { if (unlikely(afl->old_seed_selection)) { - afl->queue_cur = afl->queue_cur->next; - ++afl->current_entry; + while (++afl->current_entry < afl->queued_paths && + afl->queue_buf[afl->current_entry]->disabled) + ; + if (unlikely(afl->current_entry >= afl->queued_paths || + afl->queue_buf[afl->current_entry] == NULL || + afl->queue_buf[afl->current_entry]->disabled)) + afl->queue_cur = NULL; + else + afl->queue_cur = afl->queue_buf[afl->current_entry]; } diff --git a/utils/afl_untracer/afl-untracer.c b/utils/afl_untracer/afl-untracer.c index f3894a06..d2bad0b9 100644 --- a/utils/afl_untracer/afl-untracer.c +++ b/utils/afl_untracer/afl-untracer.c @@ -284,7 +284,7 @@ library_list_t *find_library(char *name) { // this seems to work for clang too. nice :) requires gcc 4.4+ #pragma GCC push_options #pragma GCC optimize("O0") -void breakpoint(void) { +void breakpoint(void) { if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n"); diff --git a/utils/libtokencap/libtokencap.so.c b/utils/libtokencap/libtokencap.so.c index 3629e804..26033b46 100644 --- a/utils/libtokencap/libtokencap.so.c +++ b/utils/libtokencap/libtokencap.so.c @@ -161,8 +161,8 @@ static void __tokencap_load_mappings(void) { #elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ - #if defined __FreeBSD__ - int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, __tokencap_pid}; + #if defined __FreeBSD__ + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, __tokencap_pid}; #elif defined __OpenBSD__ int mib[] = {CTL_KERN, KERN_PROC_VMMAP, __tokencap_pid}; #elif defined __NetBSD__ @@ -177,7 +177,7 @@ static void __tokencap_load_mappings(void) { #if defined __FreeBSD__ || defined __NetBSD__ len = len * 4 / 3; - #elif defined __OpenBSD__ + #elif defined __OpenBSD__ len -= len % sizeof(struct kinfo_vmentry); #endif @@ -202,8 +202,8 @@ static void __tokencap_load_mappings(void) { #if defined __FreeBSD__ || defined __NetBSD__ - #if defined __FreeBSD__ - size_t size = region->kve_structsize; + #if defined __FreeBSD__ + size_t size = region->kve_structsize; if (size == 0) break; #elif defined __NetBSD__ diff --git a/utils/persistent_mode/persistent_demo_new.c b/utils/persistent_mode/persistent_demo_new.c index 7e694696..ca616236 100644 --- a/utils/persistent_mode/persistent_demo_new.c +++ b/utils/persistent_mode/persistent_demo_new.c @@ -51,7 +51,7 @@ __AFL_FUZZ_INIT(); /* To ensure checks are not optimized out it is recommended to disable code optimization for the fuzzer harness main() */ #pragma clang optimize off -#pragma GCC optimize("O0") +#pragma GCC optimize("O0") int main(int argc, char **argv) { diff --git a/utils/persistent_mode/test-instr.c b/utils/persistent_mode/test-instr.c index 6da511de..2c6b6d77 100644 --- a/utils/persistent_mode/test-instr.c +++ b/utils/persistent_mode/test-instr.c @@ -24,7 +24,7 @@ __AFL_FUZZ_INIT(); /* To ensure checks are not optimized out it is recommended to disable code optimization for the fuzzer harness main() */ #pragma clang optimize off -#pragma GCC optimize("O0") +#pragma GCC optimize("O0") int main(int argc, char **argv) { -- cgit 1.4.1 From cc0210426a5a31d56d8a0e850dcc00d90833afcd Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 31 Jan 2021 17:32:24 +0100 Subject: code-format --- docs/Changelog.md | 1 + include/xxhash.h | 2 +- instrumentation/compare-transform-pass.so.cc | 2 +- src/afl-fuzz.c | 3 +++ utils/afl_untracer/afl-untracer.c | 2 +- utils/libtokencap/libtokencap.so.c | 10 +++++----- utils/persistent_mode/persistent_demo_new.c | 2 +- utils/persistent_mode/test-instr.c | 2 +- 8 files changed, 14 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 99bc8b47..ff69c949 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -27,6 +27,7 @@ sending a mail to . - if determinstic mode is active (-D, or -M without -d) then we sync after every queue entry as this can take very long time otherwise - better detection if a target needs a large shared map + - fix for -Z - switched to an even faster RNG - added hghwng's patch for faster trace map analysis - afl-cc diff --git a/include/xxhash.h b/include/xxhash.h index 3bd56d13..006d3f3d 100644 --- a/include/xxhash.h +++ b/include/xxhash.h @@ -287,7 +287,7 @@ typedef uint32_t XXH32_hash_t; #else #include #if UINT_MAX == 0xFFFFFFFFUL -typedef unsigned int XXH32_hash_t; +typedef unsigned int XXH32_hash_t; #else #if ULONG_MAX == 0xFFFFFFFFUL typedef unsigned long XXH32_hash_t; diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index 932540a7..da5cf7e9 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -68,7 +68,7 @@ class CompareTransform : public ModulePass { const char *getPassName() const override { #else - StringRef getPassName() const override { + StringRef getPassName() const override { #endif return "transforms compare functions"; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 276074a4..f1f92717 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1632,8 +1632,11 @@ int main(int argc, char **argv_orig, char **envp) { afl->current_entry = 0; while (unlikely(afl->queue_buf[afl->current_entry]->disabled)) { + ++afl->current_entry; + } + afl->queue_cur = afl->queue_buf[afl->current_entry]; if (unlikely(seek_to)) { diff --git a/utils/afl_untracer/afl-untracer.c b/utils/afl_untracer/afl-untracer.c index d2bad0b9..f3894a06 100644 --- a/utils/afl_untracer/afl-untracer.c +++ b/utils/afl_untracer/afl-untracer.c @@ -284,7 +284,7 @@ library_list_t *find_library(char *name) { // this seems to work for clang too. nice :) requires gcc 4.4+ #pragma GCC push_options #pragma GCC optimize("O0") -void breakpoint(void) { +void breakpoint(void) { if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n"); diff --git a/utils/libtokencap/libtokencap.so.c b/utils/libtokencap/libtokencap.so.c index 26033b46..3629e804 100644 --- a/utils/libtokencap/libtokencap.so.c +++ b/utils/libtokencap/libtokencap.so.c @@ -161,8 +161,8 @@ static void __tokencap_load_mappings(void) { #elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ - #if defined __FreeBSD__ - int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, __tokencap_pid}; + #if defined __FreeBSD__ + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, __tokencap_pid}; #elif defined __OpenBSD__ int mib[] = {CTL_KERN, KERN_PROC_VMMAP, __tokencap_pid}; #elif defined __NetBSD__ @@ -177,7 +177,7 @@ static void __tokencap_load_mappings(void) { #if defined __FreeBSD__ || defined __NetBSD__ len = len * 4 / 3; - #elif defined __OpenBSD__ + #elif defined __OpenBSD__ len -= len % sizeof(struct kinfo_vmentry); #endif @@ -202,8 +202,8 @@ static void __tokencap_load_mappings(void) { #if defined __FreeBSD__ || defined __NetBSD__ - #if defined __FreeBSD__ - size_t size = region->kve_structsize; + #if defined __FreeBSD__ + size_t size = region->kve_structsize; if (size == 0) break; #elif defined __NetBSD__ diff --git a/utils/persistent_mode/persistent_demo_new.c b/utils/persistent_mode/persistent_demo_new.c index ca616236..7e694696 100644 --- a/utils/persistent_mode/persistent_demo_new.c +++ b/utils/persistent_mode/persistent_demo_new.c @@ -51,7 +51,7 @@ __AFL_FUZZ_INIT(); /* To ensure checks are not optimized out it is recommended to disable code optimization for the fuzzer harness main() */ #pragma clang optimize off -#pragma GCC optimize("O0") +#pragma GCC optimize("O0") int main(int argc, char **argv) { diff --git a/utils/persistent_mode/test-instr.c b/utils/persistent_mode/test-instr.c index 2c6b6d77..6da511de 100644 --- a/utils/persistent_mode/test-instr.c +++ b/utils/persistent_mode/test-instr.c @@ -24,7 +24,7 @@ __AFL_FUZZ_INIT(); /* To ensure checks are not optimized out it is recommended to disable code optimization for the fuzzer harness main() */ #pragma clang optimize off -#pragma GCC optimize("O0") +#pragma GCC optimize("O0") int main(int argc, char **argv) { -- cgit 1.4.1 From 981ffb27a8a166b51a06d57fce044ed1eaf1aa62 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 12:01:23 +0100 Subject: making AFL_MAP_SIZE obsolete --- afl-cmin | 4 +-- docs/Changelog.md | 2 ++ include/forkserver.h | 3 ++ include/sharedmem.h | 1 + src/afl-forkserver.c | 28 ++++++++++++++----- src/afl-fuzz-init.c | 14 ++++++---- src/afl-fuzz.c | 73 +++++++++++++++++++++++++++++++++++++++++++------ src/afl-sharedmem.c | 14 ++++++++-- src/afl-showmap.c | 41 +++++++++++++++++++++++++-- src/afl-tmin.c | 37 +++++++++++++++++++++++-- test-instr.c | 5 +++- test/test-basic.sh | 12 ++++---- test/test-gcc-plugin.sh | 10 +++---- test/test-llvm-lto.sh | 8 +++--- test/test-llvm.sh | 10 +++---- 15 files changed, 211 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/afl-cmin b/afl-cmin index ffefaead..31d7ddad 100755 --- a/afl-cmin +++ b/afl-cmin @@ -343,7 +343,7 @@ BEGIN { stat_format = "-f '%z %N'" # *BSD, MacOS } cmdline = "cd "in_dir" && find . \\( ! -name . -a -type d -prune \\) -o -type f -exec stat "stat_format" \\{\\} \\; | sort -k1n -k2r" - cmdline = "ls "in_dir" | (cd "in_dir" && xargs stat "stat_format") | sort -k1n -k2r" + cmdline = "ls "in_dir" | (cd "in_dir" && xargs stat "stat_format" 2>/dev/null) | sort -k1n -k2r" while (cmdline | getline) { sub(/^[0-9]+ (\.\/)?/,"",$0) infilesSmallToBig[i++] = $0 @@ -355,7 +355,7 @@ BEGIN { # Make sure that we're not dealing with a directory. if (0 == system("test -d "in_dir"/"first_file)) { - print "[-] Error: The input directory contains subdirectories - please fix." > "/dev/stderr" + print "[-] Error: The input directory is empty or contains subdirectories - please fix." > "/dev/stderr" exit 1 } diff --git a/docs/Changelog.md b/docs/Changelog.md index ff69c949..e9efdf38 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -16,6 +16,8 @@ sending a mail to . to be placed in the source code. Check out instrumentation/README.instrument_list.md - afl-fuzz + - Making AFL_MAP_SIZE obsolete - afl-fuzz now learns on start the + target map size - upgraded cmplog/redqueen: solving for floating point, solving transformations (e.g. toupper, tolower, to/from hex, xor, arithmetics, etc.). this is costly hence new command line option diff --git a/include/forkserver.h b/include/forkserver.h index d2fcaa20..ac027f81 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -120,11 +120,14 @@ void afl_fsrv_init(afl_forkserver_t *fsrv); void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from); void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, u8 debug_child_output); +u32 afl_fsrv_get_mapsize(afl_forkserver_t *fsrv, char **argv, + volatile u8 *stop_soon_p, u8 debug_child_output); void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len); fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, volatile u8 *stop_soon_p); void afl_fsrv_killall(void); void afl_fsrv_deinit(afl_forkserver_t *fsrv); +void afl_fsrv_kill(afl_forkserver_t *fsrv); #ifdef __APPLE__ #define MSG_FORK_ON_APPLE \ diff --git a/include/sharedmem.h b/include/sharedmem.h index b15d0535..fdc947f9 100644 --- a/include/sharedmem.h +++ b/include/sharedmem.h @@ -51,6 +51,7 @@ typedef struct sharedmem { size_t map_size; /* actual allocated size */ int cmplog_mode; + int shmemfuzz_mode; struct cmp_map *cmp_map; } sharedmem_t; diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index e59f0d11..9ee59822 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -682,11 +682,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if ((status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT) { - if (ignore_autodict) { - - if (!be_quiet) { WARNF("Ignoring offered AUTODICT feature."); } - - } else { + if (!ignore_autodict) { if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) { @@ -969,7 +965,9 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, } -static void afl_fsrv_kill(afl_forkserver_t *fsrv) { +/* Stop the forkserver and child */ + +void afl_fsrv_kill(afl_forkserver_t *fsrv) { if (fsrv->child_pid > 0) { kill(fsrv->child_pid, fsrv->kill_signal); } if (fsrv->fsrv_pid > 0) { @@ -979,13 +977,28 @@ static void afl_fsrv_kill(afl_forkserver_t *fsrv) { } + close(fsrv->fsrv_ctl_fd); + close(fsrv->fsrv_st_fd); + fsrv->fsrv_pid = -1; + fsrv->child_pid = -1; + +} + +/* Get the map size from the target forkserver */ + +u32 afl_fsrv_get_mapsize(afl_forkserver_t *fsrv, char **argv, + volatile u8 *stop_soon_p, u8 debug_child_output) { + + afl_fsrv_start(fsrv, argv, stop_soon_p, debug_child_output); + return fsrv->map_size; + } /* Delete the current testcase and write the buf to the testcase file */ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { - if (fsrv->shmem_fuzz) { + if (likely(fsrv->use_shmem_fuzz && fsrv->shmem_fuzz)) { if (unlikely(len > MAX_FILE)) len = MAX_FILE; @@ -1042,6 +1055,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { } + // fprintf(stderr, "WRITE %d %u\n", fd, len); ck_write(fd, buf, len, fsrv->out_file); if (fsrv->use_stdin) { diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 56dae48c..40ba20c7 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -766,13 +766,16 @@ void read_testcases(afl_state_t *afl, u8 *directory) { } - if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) { + /* + if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) { - u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); - afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE; - afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1; + u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, + HASH_CONST); afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE; + afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1; - } + } + + */ } @@ -2490,6 +2493,7 @@ void setup_testcase_shmem(afl_state_t *afl) { // we need to set the non-instrumented mode to not overwrite the SHM_ENV_VAR u8 *map = afl_shm_init(afl->shm_fuzz, MAX_FILE + sizeof(u32), 1); + afl->shm_fuzz->shmemfuzz_mode = 1; if (!map) { FATAL("BUG: Zero return from afl_shm_init."); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index f1f92717..49733594 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -342,7 +342,6 @@ int main(int argc, char **argv_orig, char **envp) { afl->debug = debug; afl_fsrv_init(&afl->fsrv); if (debug) { afl->fsrv.debug = true; } - read_afl_environment(afl, envp); if (afl->shm.map_size) { afl->fsrv.map_size = afl->shm.map_size; } exit_1 = !!afl->afl_env.afl_bench_just_one; @@ -702,7 +701,6 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->in_bitmap) { FATAL("Multiple -B options not supported"); } afl->in_bitmap = optarg; - read_bitmap(afl->in_bitmap, afl->virgin_bits, afl->fsrv.map_size); break; case 'C': /* crash mode */ @@ -1369,13 +1367,6 @@ int main(int argc, char **argv_orig, char **envp) { set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY); #endif - afl->fsrv.trace_bits = - afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); - - if (!afl->in_bitmap) { memset(afl->virgin_bits, 255, afl->fsrv.map_size); } - memset(afl->virgin_tmout, 255, afl->fsrv.map_size); - memset(afl->virgin_crash, 255, afl->fsrv.map_size); - init_count_class16(); if (afl->is_main_node && check_main_node_exists(afl) == 1) { @@ -1542,6 +1533,70 @@ int main(int argc, char **argv_orig, char **envp) { } afl->argv = use_argv; + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); + + if (!afl->non_instrumented_mode) { + + afl->fsrv.map_size = 4194304; // dummy temporary value + + u32 new_map_size = afl_fsrv_get_mapsize( + &afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); + + if (new_map_size && new_map_size != 4194304) { + + // only reinitialize when it makes sense + if (map_size != new_map_size) { + + // if (map_size < new_map_size || + // (new_map_size > map_size && new_map_size - map_size > + // MAP_SIZE)) { + + OKF("Re-initializing maps to %u bytes", new_map_size); + + afl->virgin_bits = ck_realloc(afl->virgin_bits, map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, map_size); + afl->top_rated = ck_realloc(afl->top_rated, map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, map_size); + afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, map_size); + afl->first_trace = ck_realloc(afl->first_trace, map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, map_size); + + afl_shm_deinit(&afl->shm); + afl_fsrv_kill(&afl->fsrv); + afl->fsrv.map_size = new_map_size; + afl->fsrv.trace_bits = afl_shm_init(&afl->shm, afl->fsrv.map_size, + afl->non_instrumented_mode); + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + } + + map_size = new_map_size; + + } + + afl->fsrv.map_size = map_size; + + } + + // after we have the correct bitmap size we can read the bitmap -B option + // and set the virgin maps + if (!afl->in_bitmap) { + + memset(afl->virgin_bits, 255, afl->fsrv.map_size); + + } else { + + read_bitmap(afl->in_bitmap, afl->virgin_bits, afl->fsrv.map_size); + + } + + memset(afl->virgin_tmout, 255, afl->fsrv.map_size); + memset(afl->virgin_crash, 255, afl->fsrv.map_size); if (afl->cmplog_binary) { diff --git a/src/afl-sharedmem.c b/src/afl-sharedmem.c index fe641d0d..3241a130 100644 --- a/src/afl-sharedmem.c +++ b/src/afl-sharedmem.c @@ -66,9 +66,17 @@ static list_t shm_list = {.element_prealloc_count = 0}; void afl_shm_deinit(sharedmem_t *shm) { - if (shm == NULL) return; - + if (shm == NULL) { return; } list_remove(&shm_list, shm); + if (shm->shmemfuzz_mode) { + + unsetenv(SHM_FUZZ_ENV_VAR); + + } else { + + unsetenv(SHM_ENV_VAR); + + } #ifdef USEMMAP if (shm->map != NULL) { @@ -94,6 +102,8 @@ void afl_shm_deinit(sharedmem_t *shm) { if (shm->cmplog_mode) { + unsetenv(CMPLOG_SHM_ENV_VAR); + if (shm->cmp_map != NULL) { munmap(shm->cmp_map, shm->map_size); diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 62bf1021..56abe4f1 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -86,7 +86,8 @@ static u8 quiet_mode, /* Hide non-essential messages? */ remove_shm = 1, /* remove shmem? */ collect_coverage, /* collect coverage */ have_coverage, /* have coverage? */ - no_classify; /* do not classify counts */ + no_classify, /* do not classify counts */ + debug; /* debug mode */ static volatile u8 stop_soon, /* Ctrl-C pressed? */ child_crashed; /* Child crashed? */ @@ -743,6 +744,7 @@ int main(int argc, char **argv_orig, char **envp) { char **argv = argv_cpy_dup(argc, argv_orig); afl_forkserver_t fsrv_var = {0}; + if (getenv("AFL_DEBUG")) { debug = 1; } fsrv = &fsrv_var; afl_fsrv_init(fsrv); map_size = get_map_size(); @@ -991,14 +993,16 @@ int main(int argc, char **argv_orig, char **envp) { // if (afl->shmem_testcase_mode) { setup_testcase_shmem(afl); } + setenv("AFL_NO_AUTODICT", "1", 1); + /* initialize cmplog_mode */ shm.cmplog_mode = 0; - fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); setup_signal_handlers(); set_up_environment(fsrv); fsrv->target_path = find_binary(argv[optind]); + fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); if (!quiet_mode) { @@ -1051,6 +1055,7 @@ int main(int argc, char **argv_orig, char **envp) { /* initialize cmplog_mode */ shm_fuzz->cmplog_mode = 0; u8 *map = afl_shm_init(shm_fuzz, MAX_FILE + sizeof(u32), 1); + shm_fuzz->shmemfuzz_mode = 1; if (!map) { FATAL("BUG: Zero return from afl_shm_init."); } #ifdef USEMMAP setenv(SHM_FUZZ_ENV_VAR, shm_fuzz->g_shm_file_path, 1); @@ -1063,6 +1068,38 @@ int main(int argc, char **argv_orig, char **envp) { fsrv->shmem_fuzz_len = (u32 *)map; fsrv->shmem_fuzz = map + sizeof(u32); + u32 save_be_quiet = be_quiet; + be_quiet = debug; + fsrv->map_size = 4194304; // dummy temporary value + u32 new_map_size = afl_fsrv_get_mapsize( + fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + be_quiet = save_be_quiet; + + if (new_map_size) { + + // only reinitialize when it makes sense + if (map_size < new_map_size || + (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { + + if (!be_quiet) + ACTF("Aquired new map size for target: %u bytes\n", new_map_size); + + afl_shm_deinit(&shm); + afl_fsrv_kill(fsrv); + fsrv->map_size = new_map_size; + fsrv->trace_bits = afl_shm_init(&shm, new_map_size, 0); + + } + + map_size = new_map_size; + + } + + fsrv->map_size = map_size; + if (in_dir) { DIR * dir_in, *dir_out = NULL; diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 09b5211d..799a4b87 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -79,7 +79,8 @@ static u8 crash_mode, /* Crash-centric mode? */ edges_only, /* Ignore hit counts? */ exact_mode, /* Require path match for crashes? */ remove_out_file, /* remove out_file on exit? */ - remove_shm = 1; /* remove shmem on exit? */ + remove_shm = 1, /* remove shmem on exit? */ + debug; /* debug mode */ static volatile u8 stop_soon; /* Ctrl-C pressed? */ @@ -878,6 +879,7 @@ int main(int argc, char **argv_orig, char **envp) { char **argv = argv_cpy_dup(argc, argv_orig); afl_forkserver_t fsrv_var = {0}; + if (getenv("AFL_DEBUG")) { debug = 1; } fsrv = &fsrv_var; afl_fsrv_init(fsrv); map_size = get_map_size(); @@ -1074,6 +1076,7 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !in_file || !output_file) { usage(argv[0]); } check_environment_vars(envp); + setenv("AFL_NO_AUTODICT", "1", 1); if (fsrv->qemu_mode && getenv("AFL_USE_QASAN")) { @@ -1102,7 +1105,6 @@ int main(int argc, char **argv_orig, char **envp) { /* initialize cmplog_mode */ shm.cmplog_mode = 0; - fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); atexit(at_exit_handler); setup_signal_handlers(); @@ -1110,6 +1112,7 @@ int main(int argc, char **argv_orig, char **envp) { set_up_environment(fsrv); fsrv->target_path = find_binary(argv[optind]); + fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); detect_file_args(argv + optind, out_file, &fsrv->use_stdin); if (fsrv->qemu_mode) { @@ -1181,6 +1184,7 @@ int main(int argc, char **argv_orig, char **envp) { /* initialize cmplog_mode */ shm_fuzz->cmplog_mode = 0; u8 *map = afl_shm_init(shm_fuzz, MAX_FILE + sizeof(u32), 1); + shm_fuzz->shmemfuzz_mode = 1; if (!map) { FATAL("BUG: Zero return from afl_shm_init."); } #ifdef USEMMAP setenv(SHM_FUZZ_ENV_VAR, shm_fuzz->g_shm_file_path, 1); @@ -1195,12 +1199,39 @@ int main(int argc, char **argv_orig, char **envp) { read_initial_file(); - afl_fsrv_start( + fsrv->map_size = 4194304; // dummy temporary value + u32 new_map_size = afl_fsrv_get_mapsize( fsrv, use_argv, &stop_soon, (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) ? 1 : 0); + if (new_map_size) { + + if (map_size < new_map_size || + (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { + + if (!be_quiet) + ACTF("Aquired new map size for target: %u bytes\n", new_map_size); + + afl_shm_deinit(&shm); + afl_fsrv_kill(fsrv); + fsrv->map_size = new_map_size; + fsrv->trace_bits = afl_shm_init(&shm, new_map_size, 0); + afl_fsrv_start(fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || + get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + + } + + map_size = new_map_size; + + } + + fsrv->map_size = map_size; + if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); diff --git a/test-instr.c b/test-instr.c index 84ac0036..00799103 100644 --- a/test-instr.c +++ b/test-instr.c @@ -32,7 +32,8 @@ int main(int argc, char **argv) { } else { - if (argc >= 3 && strcmp(argv[1], "-f") == 0) + if (argc >= 3 && strcmp(argv[1], "-f") == 0) { + if ((fd = open(argv[2], O_RDONLY)) < 0) { fprintf(stderr, "Error: unable to open %s\n", argv[2]); @@ -40,6 +41,8 @@ int main(int argc, char **argv) { } + } + if (read(fd, buf, sizeof(buf)) < 1) { printf("Hum?\n"); diff --git a/test/test-basic.sh b/test/test-basic.sh index fcac8ca3..132610c0 100755 --- a/test/test-basic.sh +++ b/test/test-basic.sh @@ -11,8 +11,8 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 test -e test-instr.plain && { $ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + echo 0 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 test -e test-instr.plain.0 -a -e test-instr.plain.1 && { diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { $ECHO "$RED[!] ${AFL_GCC} instrumentation should be different on different input but is not" @@ -26,7 +26,7 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc } rm -f test-instr.plain.0 test-instr.plain.1 SKIP= - TUPLES=`echo 1|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + TUPLES=`echo 1|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` test "$TUPLES" -gt 1 -a "$TUPLES" -lt 12 && { $ECHO "$GREEN[+] ${AFL_GCC} run reported $TUPLES instrumented locations which is fine" } || { @@ -132,8 +132,8 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 test -e test-instr.plain && { $ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + echo 0 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 test -e test-instr.plain.0 -a -e test-instr.plain.1 && { diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { $ECHO "$RED[!] ${AFL_GCC} instrumentation should be different on different input but is not" @@ -146,7 +146,7 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc CODE=1 } rm -f test-instr.plain.0 test-instr.plain.1 - TUPLES=`echo 1|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + TUPLES=`echo 1|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` test "$TUPLES" -gt 1 -a "$TUPLES" -lt 12 && { $ECHO "$GREEN[+] ${AFL_GCC} run reported $TUPLES instrumented locations which is fine" } || { diff --git a/test/test-gcc-plugin.sh b/test/test-gcc-plugin.sh index cce6336b..4c36b6c9 100755 --- a/test/test-gcc-plugin.sh +++ b/test/test-gcc-plugin.sh @@ -10,15 +10,15 @@ test -e ../afl-gcc-fast -a -e ../afl-compiler-rt.o && { AFL_HARDEN=1 ../afl-gcc-fast -o test-compcov.harden.gccpi test-compcov.c > /dev/null 2>&1 test -e test-instr.plain.gccpi && { $ECHO "$GREEN[+] gcc_plugin compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain.gccpi > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain.gccpi < /dev/null > /dev/null 2>&1 + echo 0 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain.gccpi > /dev/null 2>&1 + AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain.gccpi < /dev/null > /dev/null 2>&1 test -e test-instr.plain.0 -a -e test-instr.plain.1 && { diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { $ECHO "$RED[!] gcc_plugin instrumentation should be different on different input but is not" CODE=1 } || { $ECHO "$GREEN[+] gcc_plugin instrumentation present and working correctly" - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain.gccpi 2>&1 | grep Captur | awk '{print$3}'` + TUPLES=`echo 0|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain.gccpi 2>&1 | grep Captur | awk '{print$3}'` test "$TUPLES" -gt 1 -a "$TUPLES" -lt 9 && { $ECHO "$GREEN[+] gcc_plugin run reported $TUPLES instrumented locations which is fine" } || { @@ -87,7 +87,7 @@ test -e ../afl-gcc-fast -a -e ../afl-compiler-rt.o && { echo foobar.c > instrumentlist.txt AFL_GCC_INSTRUMENT_FILE=instrumentlist.txt ../afl-gcc-fast -o test-compcov test-compcov.c > /dev/null 2>&1 test -x test-compcov && test_compcov_binary_functionality ./test-compcov && { - echo 1 | ../afl-showmap -m ${MEM_LIMIT} -o - -r -- ./test-compcov 2>&1 | grep -q "Captured 0 tuples" && { + echo 1 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o - -r -- ./test-compcov 2>&1 | grep -q "Captured 0 tuples" && { $ECHO "$GREEN[+] gcc_plugin instrumentlist feature works correctly" } || { $ECHO "$RED[!] gcc_plugin instrumentlist feature failed" @@ -100,7 +100,7 @@ test -e ../afl-gcc-fast -a -e ../afl-compiler-rt.o && { rm -f test-compcov test.out instrumentlist.txt ../afl-gcc-fast -o test-persistent ../utils/persistent_mode/persistent_demo.c > /dev/null 2>&1 test -e test-persistent && { - echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { + echo foo | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] gcc_plugin persistent mode feature works correctly" } || { $ECHO "$RED[!] gcc_plugin persistent mode feature failed to work" diff --git a/test/test-llvm-lto.sh b/test/test-llvm-lto.sh index a931afb7..3e762acf 100755 --- a/test/test-llvm-lto.sh +++ b/test/test-llvm-lto.sh @@ -16,15 +16,15 @@ test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && { ../afl-clang-lto -o test-instr.plain ../test-instr.c > /dev/null 2>&1 test -e test-instr.plain && { $ECHO "$GREEN[+] llvm_mode LTO compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + echo 0 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 test -e test-instr.plain.0 -a -e test-instr.plain.1 && { diff -q test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { $ECHO "$RED[!] llvm_mode LTO instrumentation should be different on different input but is not" CODE=1 } || { $ECHO "$GREEN[+] llvm_mode LTO instrumentation present and working correctly" - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + TUPLES=`echo 0|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` test "$TUPLES" -gt 2 -a "$TUPLES" -lt 7 && { $ECHO "$GREEN[+] llvm_mode LTO run reported $TUPLES instrumented locations which is fine" } || { @@ -59,7 +59,7 @@ test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && { rm -f test-compcov test.out instrumentlist.txt ../afl-clang-lto -o test-persistent ../utils/persistent_mode/persistent_demo.c > /dev/null 2>&1 test -e test-persistent && { - echo foo | ../afl-showmap -m none -o /dev/null -q -r ./test-persistent && { + echo foo | AFL_QUIET=1 ../afl-showmap -m none -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] llvm_mode LTO persistent mode feature works correctly" } || { $ECHO "$RED[!] llvm_mode LTO persistent mode feature failed to work" diff --git a/test/test-llvm.sh b/test/test-llvm.sh index c968d5a9..156b8920 100755 --- a/test/test-llvm.sh +++ b/test/test-llvm.sh @@ -16,15 +16,15 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { AFL_HARDEN=1 ../afl-clang-fast -o test-compcov.harden test-compcov.c > /dev/null 2>&1 test -e test-instr.plain && { $ECHO "$GREEN[+] llvm_mode compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + echo 0 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 test -e test-instr.plain.0 -a -e test-instr.plain.1 && { diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { $ECHO "$RED[!] llvm_mode instrumentation should be different on different input but is not" CODE=1 } || { $ECHO "$GREEN[+] llvm_mode instrumentation present and working correctly" - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + TUPLES=`echo 0|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` test "$TUPLES" -gt 2 -a "$TUPLES" -lt 8 && { $ECHO "$GREEN[+] llvm_mode run reported $TUPLES instrumented locations which is fine" } || { @@ -128,7 +128,7 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { test -e ../libLLVMInsTrim.so && { AFL_LLVM_INSTRUMENT=CFG AFL_LLVM_INSTRIM_LOOPHEAD=1 ../afl-clang-fast -o test-instr.instrim ../test-instr.c > /dev/null 2>test.out test -e test-instr.instrim && { - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.instrim 2>&1 | grep Captur | awk '{print$3}'` + TUPLES=`echo 0|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.instrim 2>&1 | grep Captur | awk '{print$3}'` test "$TUPLES" -gt 1 -a "$TUPLES" -lt 5 && { $ECHO "$GREEN[+] llvm_mode InsTrim reported $TUPLES instrumented locations which is fine" } || { @@ -216,7 +216,7 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { rm -rf errors test-cmplog in core.* ../afl-clang-fast -o test-persistent ../utils/persistent_mode/persistent_demo.c > /dev/null 2>&1 test -e test-persistent && { - echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { + echo foo | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] llvm_mode persistent mode feature works correctly" } || { $ECHO "$RED[!] llvm_mode persistent mode feature failed to work" -- cgit 1.4.1 From 965b854803b67f952406d31353d4cb8ed26eeee6 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 12:05:54 +0100 Subject: correct afl-showmap be_quiet state --- src/afl-showmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 56abe4f1..56091357 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1069,7 +1069,7 @@ int main(int argc, char **argv_orig, char **envp) { fsrv->shmem_fuzz = map + sizeof(u32); u32 save_be_quiet = be_quiet; - be_quiet = debug; + be_quiet = !debug; fsrv->map_size = 4194304; // dummy temporary value u32 new_map_size = afl_fsrv_get_mapsize( fsrv, use_argv, &stop_soon, -- cgit 1.4.1 From d808a8401e1acbcde3352d86e9e2da3f7bac97e8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 12:16:55 +0100 Subject: import cmplog opts --- include/afl-fuzz.h | 1 + src/afl-fuzz-one.c | 4 ++-- src/afl-fuzz-redqueen.c | 31 +++++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 9b27606c..c3a8c2ee 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -647,6 +647,7 @@ typedef struct afl_state { u32 cmplog_prev_timed_out; u32 cmplog_max_filesize; u32 cmplog_lvl; + u32 colorize_success; struct afl_pass_stat *pass_stats; struct cmp_map * orig_cmp_map; diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 18291fb7..c73e394a 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -562,7 +562,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (afl->cmplog_lvl == 3 || (afl->cmplog_lvl == 2 && afl->queue_cur->tc_ref) || !(afl->fsrv.total_execs % afl->queued_paths) || - get_cur_time() - afl->last_path_time > 15000) { + get_cur_time() - afl->last_path_time > 300000) { if (input_to_state_stage(afl, in_buf, out_buf, len)) { @@ -2990,7 +2990,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { if (afl->cmplog_lvl == 3 || (afl->cmplog_lvl == 2 && afl->queue_cur->tc_ref) || !(afl->fsrv.total_execs % afl->queued_paths) || - get_cur_time() - afl->last_path_time > 15000) { + get_cur_time() - afl->last_path_time > 300000) { if (input_to_state_stage(afl, in_buf, out_buf, len)) { diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 74c9db38..997b7528 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -379,8 +379,6 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, } - *taints = taint; - /* temporary: clean ranges */ while (ranges) { @@ -423,6 +421,35 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, #endif + if (taint) { + + if (len / positions == 1 && positions > 16384 && + afl->active_paths / afl->colorize_success > 20) { + +#ifdef _DEBUG + fprintf(stderr, "Colorization unsatisfactory\n"); +#endif + + *taints = NULL; + + struct tainted *t; + while (taint) { + + t = taint->next; + ck_free(taint); + taint = t; + + } + + } else { + + *taints = taint; + ++afl->colorize_success; + + } + + } + afl->stage_finds[STAGE_COLORIZATION] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_COLORIZATION] += afl->stage_cur; ck_free(backup); -- cgit 1.4.1 From 88155d2c3b86aa2b042e57481939cf2a7d3b02f4 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 13:04:39 +0100 Subject: make dominik more happy - no auto map size for qemu+unicorn --- instrumentation/afl-compiler-rt.o.c | 4 +-- src/afl-fuzz.c | 3 +- src/afl-showmap.c | 57 +++++++++++++++++-------------- src/afl-tmin.c | 67 ++++++++++++++++++++++--------------- 4 files changed, 75 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 433a1d89..060be044 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1169,8 +1169,8 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { if (getenv("AFL_DEBUG")) { - fprintf(stderr, "Running __sanitizer_cov_trace_pc_guard_init: %p-%p\n", - start, stop); + fprintf(stderr, "Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges)\n", + start, stop, stop - start); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 49733594..edcc14d6 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1536,7 +1536,8 @@ int main(int argc, char **argv_orig, char **envp) { afl->fsrv.trace_bits = afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); - if (!afl->non_instrumented_mode) { + if (!afl->non_instrumented_mode && !afl->fsrv.qemu_mode && + !afl->unicorn_mode) { afl->fsrv.map_size = 4194304; // dummy temporary value diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 56091357..c424cdf3 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1068,38 +1068,43 @@ int main(int argc, char **argv_orig, char **envp) { fsrv->shmem_fuzz_len = (u32 *)map; fsrv->shmem_fuzz = map + sizeof(u32); - u32 save_be_quiet = be_quiet; - be_quiet = !debug; - fsrv->map_size = 4194304; // dummy temporary value - u32 new_map_size = afl_fsrv_get_mapsize( - fsrv, use_argv, &stop_soon, - (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) - ? 1 - : 0); - be_quiet = save_be_quiet; - - if (new_map_size) { - - // only reinitialize when it makes sense - if (map_size < new_map_size || - (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { - - if (!be_quiet) - ACTF("Aquired new map size for target: %u bytes\n", new_map_size); - - afl_shm_deinit(&shm); - afl_fsrv_kill(fsrv); - fsrv->map_size = new_map_size; - fsrv->trace_bits = afl_shm_init(&shm, new_map_size, 0); + if (!fsrv->qemu_mode && !unicorn_mode) { + + u32 save_be_quiet = be_quiet; + be_quiet = !debug; + fsrv->map_size = 4194304; // dummy temporary value + u32 new_map_size = + afl_fsrv_get_mapsize(fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || + get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + be_quiet = save_be_quiet; + + if (new_map_size) { + + // only reinitialize when it makes sense + if (map_size < new_map_size || + (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { + + if (!be_quiet) + ACTF("Aquired new map size for target: %u bytes\n", new_map_size); + + afl_shm_deinit(&shm); + afl_fsrv_kill(fsrv); + fsrv->map_size = new_map_size; + fsrv->trace_bits = afl_shm_init(&shm, new_map_size, 0); + + } + + map_size = new_map_size; } - map_size = new_map_size; + fsrv->map_size = map_size; } - fsrv->map_size = map_size; - if (in_dir) { DIR * dir_in, *dir_out = NULL; diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 799a4b87..15336959 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -1199,38 +1199,51 @@ int main(int argc, char **argv_orig, char **envp) { read_initial_file(); - fsrv->map_size = 4194304; // dummy temporary value - u32 new_map_size = afl_fsrv_get_mapsize( - fsrv, use_argv, &stop_soon, - (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) - ? 1 - : 0); - - if (new_map_size) { - - if (map_size < new_map_size || - (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { - - if (!be_quiet) - ACTF("Aquired new map size for target: %u bytes\n", new_map_size); - - afl_shm_deinit(&shm); - afl_fsrv_kill(fsrv); - fsrv->map_size = new_map_size; - fsrv->trace_bits = afl_shm_init(&shm, new_map_size, 0); - afl_fsrv_start(fsrv, use_argv, &stop_soon, - (get_afl_env("AFL_DEBUG_CHILD") || - get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) - ? 1 - : 0); + if (!fsrv->qemu_mode && !unicorn_mode) { + + fsrv->map_size = 4194304; // dummy temporary value + u32 new_map_size = + afl_fsrv_get_mapsize(fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || + get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + + if (new_map_size) { + + if (map_size < new_map_size || + (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { + + if (!be_quiet) + ACTF("Aquired new map size for target: %u bytes\n", new_map_size); + + afl_shm_deinit(&shm); + afl_fsrv_kill(fsrv); + fsrv->map_size = new_map_size; + fsrv->trace_bits = afl_shm_init(&shm, new_map_size, 0); + afl_fsrv_start(fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || + get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + + } + + map_size = new_map_size; } - map_size = new_map_size; + fsrv->map_size = map_size; - } + } else { - fsrv->map_size = map_size; + afl_fsrv_start(fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || + get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + + } if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); -- cgit 1.4.1 From 374fa8af4788960ef4a0f5462370b68be6e4fc90 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 14:56:22 +0100 Subject: add case when cmplog map neds to be larger --- src/afl-fuzz.c | 85 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index edcc14d6..b3a27fc6 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1547,11 +1547,8 @@ int main(int argc, char **argv_orig, char **envp) { if (new_map_size && new_map_size != 4194304) { // only reinitialize when it makes sense - if (map_size != new_map_size) { - - // if (map_size < new_map_size || - // (new_map_size > map_size && new_map_size - map_size > - // MAP_SIZE)) { + if (map_size < new_map_size || + (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { OKF("Re-initializing maps to %u bytes", new_map_size); @@ -1584,21 +1581,6 @@ int main(int argc, char **argv_orig, char **envp) { } - // after we have the correct bitmap size we can read the bitmap -B option - // and set the virgin maps - if (!afl->in_bitmap) { - - memset(afl->virgin_bits, 255, afl->fsrv.map_size); - - } else { - - read_bitmap(afl->in_bitmap, afl->virgin_bits, afl->fsrv.map_size); - - } - - memset(afl->virgin_tmout, 255, afl->fsrv.map_size); - memset(afl->virgin_crash, 255, afl->fsrv.map_size); - if (afl->cmplog_binary) { ACTF("Spawning cmplog forkserver"); @@ -1608,12 +1590,71 @@ int main(int argc, char **argv_orig, char **envp) { afl->cmplog_fsrv.qemu_mode = afl->fsrv.qemu_mode; afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; afl->cmplog_fsrv.init_child_func = cmplog_exec_child; - afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child); + + afl->cmplog_fsrv.map_size = 4194304; + + u32 new_map_size = + afl_fsrv_get_mapsize(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + if (new_map_size && new_map_size != 4194304) { + + // only reinitialize when it needs to be larger + if (map_size < new_map_size) { + + OKF("Re-initializing maps to %u bytes", new_map_size); + + afl->virgin_bits = ck_realloc(afl->virgin_bits, map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, map_size); + afl->top_rated = ck_realloc(afl->top_rated, map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, map_size); + afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, map_size); + afl->first_trace = ck_realloc(afl->first_trace, map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, map_size); + + afl_shm_deinit(&afl->shm); + afl_fsrv_kill(&afl->fsrv); + afl_fsrv_kill(&afl->cmplog_fsrv); + afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same + + afl->fsrv.trace_bits = afl_shm_init(&afl->shm, new_map_size, + afl->non_instrumented_mode); + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + map_size = new_map_size; + + } + + } + + afl->cmplog_fsrv.map_size = map_size; + OKF("Cmplog forkserver successfully started"); } + // after we have the correct bitmap size we can read the bitmap -B option + // and set the virgin maps + if (afl->in_bitmap) { + + read_bitmap(afl->in_bitmap, afl->virgin_bits, afl->fsrv.map_size); + + } else { + + memset(afl->virgin_bits, 255, map_size); + + } + + memset(afl->virgin_tmout, 255, map_size); + memset(afl->virgin_crash, 255, map_size); + perform_dry_run(afl); if (afl->q_testcase_max_cache_entries) { -- cgit 1.4.1 From 812cf4c9e0f8eff80b8f46907fc8dfcd9458919f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 15:21:39 +0100 Subject: reorder --- src/afl-fuzz.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index b3a27fc6..d8ebe097 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1562,10 +1562,10 @@ int main(int argc, char **argv_orig, char **envp) { afl->first_trace = ck_realloc(afl->first_trace, map_size); afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, map_size); - afl_shm_deinit(&afl->shm); afl_fsrv_kill(&afl->fsrv); + afl_shm_deinit(&afl->shm); afl->fsrv.map_size = new_map_size; - afl->fsrv.trace_bits = afl_shm_init(&afl->shm, afl->fsrv.map_size, + afl->fsrv.trace_bits = afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); setenv("AFL_NO_AUTODICT", "1", 1); // loaded already afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, @@ -1602,7 +1602,7 @@ int main(int argc, char **argv_orig, char **envp) { // only reinitialize when it needs to be larger if (map_size < new_map_size) { - OKF("Re-initializing maps to %u bytes", new_map_size); + OKF("Re-initializing maps to %u bytes due cmplog", new_map_size); afl->virgin_bits = ck_realloc(afl->virgin_bits, map_size); afl->virgin_tmout = ck_realloc(afl->virgin_tmout, map_size); @@ -1614,9 +1614,9 @@ int main(int argc, char **argv_orig, char **envp) { afl->first_trace = ck_realloc(afl->first_trace, map_size); afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, map_size); - afl_shm_deinit(&afl->shm); afl_fsrv_kill(&afl->fsrv); afl_fsrv_kill(&afl->cmplog_fsrv); + afl_shm_deinit(&afl->shm); afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same afl->fsrv.trace_bits = afl_shm_init(&afl->shm, new_map_size, -- cgit 1.4.1 From 32110a04c0101a77a43088b85f1465ba321b2bc4 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 15:51:04 +0100 Subject: fixes --- src/afl-common.c | 2 +- src/afl-fuzz.c | 38 ++++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/afl-common.c b/src/afl-common.c index 235c4c05..1cc7f462 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -981,7 +981,7 @@ u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms) { /* Reads the map size from ENV */ u32 get_map_size(void) { - uint32_t map_size = MAP_SIZE; + uint32_t map_size = (MAP_SIZE << 2); // needed for target ctors :( char * ptr; if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index d8ebe097..008ba7d1 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1540,6 +1540,7 @@ int main(int argc, char **argv_orig, char **envp) { !afl->unicorn_mode) { afl->fsrv.map_size = 4194304; // dummy temporary value + setenv("AFL_MAP_SIZE", "4194304", 1); u32 new_map_size = afl_fsrv_get_mapsize( &afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); @@ -1552,15 +1553,15 @@ int main(int argc, char **argv_orig, char **envp) { OKF("Re-initializing maps to %u bytes", new_map_size); - afl->virgin_bits = ck_realloc(afl->virgin_bits, map_size); - afl->virgin_tmout = ck_realloc(afl->virgin_tmout, map_size); - afl->virgin_crash = ck_realloc(afl->virgin_crash, map_size); - afl->var_bytes = ck_realloc(afl->var_bytes, map_size); - afl->top_rated = ck_realloc(afl->top_rated, map_size * sizeof(void *)); - afl->clean_trace = ck_realloc(afl->clean_trace, map_size); - afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, map_size); - afl->first_trace = ck_realloc(afl->first_trace, map_size); - afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, map_size); + afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); + afl->top_rated = ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); + afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, new_map_size); + afl->first_trace = ck_realloc(afl->first_trace, new_map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); afl_fsrv_kill(&afl->fsrv); afl_shm_deinit(&afl->shm); @@ -1596,6 +1597,7 @@ int main(int argc, char **argv_orig, char **envp) { u32 new_map_size = afl_fsrv_get_mapsize(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); +printf("NEW MAP SIZE2 %u (is %u)\n", new_map_size, map_size); if (new_map_size && new_map_size != 4194304) { @@ -1604,15 +1606,15 @@ int main(int argc, char **argv_orig, char **envp) { OKF("Re-initializing maps to %u bytes due cmplog", new_map_size); - afl->virgin_bits = ck_realloc(afl->virgin_bits, map_size); - afl->virgin_tmout = ck_realloc(afl->virgin_tmout, map_size); - afl->virgin_crash = ck_realloc(afl->virgin_crash, map_size); - afl->var_bytes = ck_realloc(afl->var_bytes, map_size); - afl->top_rated = ck_realloc(afl->top_rated, map_size * sizeof(void *)); - afl->clean_trace = ck_realloc(afl->clean_trace, map_size); - afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, map_size); - afl->first_trace = ck_realloc(afl->first_trace, map_size); - afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, map_size); + afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); + afl->top_rated = ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); + afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, new_map_size); + afl->first_trace = ck_realloc(afl->first_trace, new_map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); afl_fsrv_kill(&afl->fsrv); afl_fsrv_kill(&afl->cmplog_fsrv); -- cgit 1.4.1 From 05472a0fc5767c90811bd55b927d26b1784c403d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 16:04:34 +0100 Subject: move cmplog compile options to config.h --- src/afl-fuzz-redqueen.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 997b7528..14a9b65d 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -29,11 +29,9 @@ #include "cmplog.h" //#define _DEBUG -#define COMBINE //#define CMPLOG_INTROSPECTION +#define COMBINE #define ARITHMETIC_LESSER_GREATER -//#define TRANSFORM -//#define TRANSFORM_BASE64 // CMP attribute enum enum { @@ -423,8 +421,8 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, if (taint) { - if (len / positions == 1 && positions > 16384 && - afl->active_paths / afl->colorize_success > 20) { + if (len / positions == 1 && positions > CMPLOG_POSITIONS_MAX && + afl->active_paths / afl->colorize_success > CMPLOG_CORPUS_PERCENT) { #ifdef _DEBUG fprintf(stderr, "Colorization unsatisfactory\n"); @@ -498,7 +496,7 @@ static u8 its_fuzz(afl_state_t *afl, u8 *buf, u32 len, u8 *status) { } -#ifdef TRANSFORM +#ifdef CMPLOG_TRANSFORM static int strntoll(const char *str, size_t sz, char **end, int base, long long *out) { @@ -579,7 +577,7 @@ static int is_hex(const char *str) { } - #ifdef TRANSFORM_BASE64 + #ifdef CMPLOG_TRANSFORM_BASE64 // tests 4 bytes at location static int is_base64(const char *str) { @@ -719,7 +717,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // o_pattern, pattern, repl, changed_val, idx, taint_len, // h->shape + 1, attr); -#ifdef TRANSFORM +#ifdef CMPLOG_TRANSFORM // reverse atoi()/strnu?toll() is expensive, so we only to it in lvl 3 if (lvl & LVL3) { @@ -1783,7 +1781,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, #ifndef COMBINE (void)(cbuf); #endif -#ifndef TRANSFORM +#ifndef CMPLOG_TRANSFORM (void)(changed_val); #endif @@ -1865,14 +1863,14 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } -#ifdef TRANSFORM +#ifdef CMPLOG_TRANSFORM if (*status == 1) return 0; if (lvl & LVL3) { u32 toupper = 0, tolower = 0, xor = 0, arith = 0, tohex = 0, fromhex = 0; - #ifdef TRANSFORM_BASE64 + #ifdef CMPLOG_TRANSFORM_BASE64 u32 tob64 = 0, fromb64 = 0; #endif u32 from_0 = 0, from_x = 0, from_X = 0, from_slash = 0, from_up = 0; @@ -1970,7 +1968,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - #ifdef TRANSFORM_BASE64 + #ifdef CMPLOG_TRANSFORM_BASE64 if (i % 3 == 2 && i < 24) { if (is_base64(repl + ((i / 3) << 2))) tob64 += 3; @@ -2018,13 +2016,13 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, "from_0=%u from_slash=%u from_x=%u\n", idx, i, xor, arith, tolower, toupper, tohex, fromhex, to_0, to_slash, to_x, from_0, from_slash, from_x); - #ifdef TRANSFORM_BASE64 + #ifdef CMPLOG_TRANSFORM_BASE64 fprintf(stderr, "RTN idx=%u loop=%u tob64=%u from64=%u\n", tob64, fromb64); #endif #endif - #ifdef TRANSFORM_BASE64 + #ifdef CMPLOG_TRANSFORM_BASE64 // input is base64 and converted to binary? convert repl to base64! if ((i % 4) == 3 && i < 24 && fromb64 > i) { @@ -2183,7 +2181,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, if ((i >= 7 && (i >= xor&&i >= arith &&i >= tolower &&i >= toupper &&i > tohex &&i > (fromhex + from_0 + from_x + from_slash + 1) - #ifdef TRANSFORM_BASE64 + #ifdef CMPLOG_TRANSFORM_BASE64 && i > tob64 + 3 && i > fromb64 + 4 #endif )) || @@ -2518,7 +2516,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { } else if ((lvl & LVL1) -#ifdef TRANSFORM +#ifdef CMPLOG_TRANSFORM || (lvl & LVL3) #endif ) { -- cgit 1.4.1 From 4018e7f8e5e45ccef83d740d7bc2514dc4f602f0 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 16:23:40 +0100 Subject: mv cmplog options to config.h --- include/config.h | 28 +++++++++++++++++++++++++++- src/afl-fuzz-redqueen.c | 35 ++++++++++------------------------- src/afl-fuzz.c | 3 +-- 3 files changed, 38 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/include/config.h b/include/config.h index b5137553..60872785 100644 --- a/include/config.h +++ b/include/config.h @@ -34,6 +34,32 @@ * * ******************************************************/ +/* CMPLOG/REDQUEEN TUNING + * + * Here you can tuning and solving options for cmplog. + * Note that these are run-time options for afl-fuzz, no target + * recompilation required. + * + */ + +/* Enable transform following (XOR/ADD/SUB manipulations, hex en/decoding) */ +// #define CMPLOG_TRANSFORM + +/* if TRANSFORM is enabled, this additionally enables base64 en/decoding */ +// #define CMPLOG_TRANSFORM_BASE64 + +/* Minimum % of the corpus to perform cmplog on. Default: 20% */ +#define CMPLOG_CORPUS_PERCENT 20U + +/* Number of potential posititions from which we decide the cmplog becomes + useless, default 16384 */ +#define CMPLOG_POSITIONS_MAX 16384U + +/* Maximum allowed fails per CMP value. Default: 32 * 3 */ +#define CMPLOG_FAIL_MAX 96 + +/* Now non-cmplog configuration options */ + /* console output colors: There are three ways to configure its behavior * 1. default: colored outputs fixed on: defined USE_COLOR && defined * ALWAYS_COLORED The env var. AFL_NO_COLOR will have no effect @@ -67,7 +93,7 @@ /* If you want to have the original afl internal memory corruption checks. Disabled by default for speed. it is better to use "make ASAN_BUILD=1". */ -//#define _WANT_ORIGINAL_AFL_ALLOC +// #define _WANT_ORIGINAL_AFL_ALLOC /* Comment out to disable fancy ANSI boxes and use poor man's 7-bit UI: */ diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 14a9b65d..8979be98 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -51,7 +51,7 @@ enum { enum { LVL1 = 1, // Integer solving - LVL2 = 2, // FP solving + LVL2 = 2, // unused except for setting the queue entry LVL3 = 4 // expensive tranformations }; @@ -986,11 +986,10 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, #endif - // we only allow this for ascii2integer (above) + // we only allow this for ascii2integer (above) so leave if this is the case if (unlikely(pattern == o_pattern)) { return 0; } - if ((lvl & LVL1) || ((lvl & LVL2) && (attr >= IS_FP && attr < IS_FP_MOD)) || - attr >= IS_FP_MOD) { + if ((lvl & LVL1) || attr >= IS_FP_MOD) { if (SHAPE_BYTES(h->shape) >= 8 && *status != 1) { @@ -1498,9 +1497,6 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, u32 lvl, struct tainted *taint) { struct cmp_header *h = &afl->shm.cmp_map->headers[key]; - // FP handling only from lvl 2 onwards - if ((h->attribute & IS_FP) && lvl < LVL2) { return 0; } - struct tainted *t; u32 i, j, idx, taint_len, loggeds; u32 have_taint = 1, is_n = 0; @@ -2443,21 +2439,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { afl->stage_max = 0; afl->stage_cur = 0; - u32 lvl; - u32 cmplog_done = afl->queue_cur->colorized; - u32 cmplog_lvl = afl->cmplog_lvl; - if (!cmplog_done) { - - lvl = LVL1; - - } else { - - lvl = 0; - - } - - if (cmplog_lvl >= 2 && cmplog_done < 2) { lvl += LVL2; } - if (cmplog_lvl >= 3 && cmplog_done < 3) { lvl += LVL3; } + u32 lvl = (afl->queue_cur->colorized ? 0 : LVL1) + (afl->cmplog_lvl == CMPLOG_LVL_MAX ? LVL3 : 0); #ifdef COMBINE u8 *cbuf = afl_realloc((void **)&afl->in_scratch_buf, len + 128); @@ -2473,8 +2455,8 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { if (!afl->shm.cmp_map->headers[k].hits) { continue; } - if (afl->pass_stats[k].faileds >= 0x69 || - afl->pass_stats[k].total >= 0x69) { + if (afl->pass_stats[k].faileds >= CMPLOG_FAIL_MAX || + afl->pass_stats[k].total >= CMPLOG_FAIL_MAX) { #ifdef _DEBUG fprintf(stderr, "DISABLED %u\n", k); @@ -2542,9 +2524,10 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { exit_its: - afl->queue_cur->colorized = afl->cmplog_lvl; if (afl->cmplog_lvl == CMPLOG_LVL_MAX) { + afl->queue_cur->colorized = CMPLOG_LVL_MAX; + ck_free(afl->queue_cur->cmplog_colorinput); t = taint; while (taint) { @@ -2559,6 +2542,8 @@ exit_its: } else { + afl->queue_cur->colorized = LVL2; + if (!afl->queue_cur->taint) { afl->queue_cur->taint = taint; } if (!afl->queue_cur->cmplog_colorinput) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 008ba7d1..62560724 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -123,8 +123,7 @@ static void usage(u8 *argv0, int more_help) { "it.\n" " if using QEMU, just use -c 0.\n" " -l cmplog_level - set the complexity/intensivity of CmpLog.\n" - " Values: 1 (integer+string), 2 (+FP) and 3 " - "(+transform)\n\n" + " Values: 1 (basic), 2 (larger files) and 3 (transform)\n\n" "Fuzzing behavior settings:\n" " -Z - sequential queue selection instead of weighted " -- cgit 1.4.1 From 80fc6166d0aeaf6332a00c369f7bdb872066e1b9 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 16:28:52 +0100 Subject: adjust expand havoc --- src/afl-fuzz.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 62560724..4a23d99d 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1779,6 +1779,7 @@ printf("NEW MAP SIZE2 %u (is %u)\n", new_map_size, map_size); break; case 1: // add MOpt mutator + /* if (afl->limit_time_sig == 0 && !afl->custom_only && !afl->python_only) { @@ -1786,12 +1787,11 @@ printf("NEW MAP SIZE2 %u (is %u)\n", new_map_size, map_size); afl->limit_time_puppet = 0; } - + */ afl->expand_havoc = 2; - if (afl->cmplog_lvl < 2) afl->cmplog_lvl = 2; + if (afl->cmplog_lvl && afl->cmplog_lvl < 2) afl->cmplog_lvl = 2; break; case 2: - // if (!have_p) afl->schedule = EXPLOIT; // increase havoc mutations per fuzz attempt afl->havoc_stack_pow2++; afl->expand_havoc = 3; @@ -1803,11 +1803,11 @@ printf("NEW MAP SIZE2 %u (is %u)\n", new_map_size, map_size); break; case 4: afl->expand_havoc = 5; - if (afl->cmplog_lvl < 3) afl->cmplog_lvl = 3; + if (afl->cmplog_lvl && afl->cmplog_lvl < 3) afl->cmplog_lvl = 3; break; case 5: // if not in sync mode, enable deterministic mode? - if (!afl->sync_id) afl->skip_deterministic = 0; + //if (!afl->sync_id) afl->skip_deterministic = 0; afl->expand_havoc = 6; case 6: // nothing else currently -- cgit 1.4.1 From b9f469e12fde797e301845caa4b0fd44315318bd Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 20:00:29 +0100 Subject: make some really weird targets compile --- src/afl-cc.c | 1 + src/afl-fuzz.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index f513764a..7db3c9a0 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -689,6 +689,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue; if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; if (!strncmp(cur, "-fno-unroll", 11)) continue; + if (strstr(cur, "afl-compiler-rt") || strstr(cur, "afl-llvm-rt")) continue; if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined")) continue; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 4a23d99d..3a7343ae 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1596,7 +1596,6 @@ int main(int argc, char **argv_orig, char **envp) { u32 new_map_size = afl_fsrv_get_mapsize(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); -printf("NEW MAP SIZE2 %u (is %u)\n", new_map_size, map_size); if (new_map_size && new_map_size != 4194304) { -- cgit 1.4.1 From 90fdafa1ad167f43fb42cdec2335fa7416cc633c Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 22:59:41 +0100 Subject: fix warnings and an llvm cmplog+lto panic --- instrumentation/afl-compiler-rt.o.c | 72 +++++++++++++---------------- instrumentation/cmplog-instructions-pass.cc | 8 +++- src/afl-fuzz-redqueen.c | 13 +++--- src/afl-fuzz.c | 26 +++++++---- src/afl-ld-lto.c | 14 +++--- 5 files changed, 70 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 060be044..c24173af 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1169,7 +1169,8 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { if (getenv("AFL_DEBUG")) { - fprintf(stderr, "Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges)\n", + fprintf(stderr, + "Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges)\n", start, stop, stop - start); } @@ -1448,45 +1449,38 @@ void __cmplog_ins_hook16(uint128_t arg1, uint128_t arg2, uint8_t attr) { #endif -#if defined(__APPLE__) - #pragma weak __sanitizer_cov_trace_const_cmp1 = __cmplog_ins_hook1 - #pragma weak __sanitizer_cov_trace_const_cmp2 = __cmplog_ins_hook2 - #pragma weak __sanitizer_cov_trace_const_cmp4 = __cmplog_ins_hook4 - #pragma weak __sanitizer_cov_trace_const_cmp8 = __cmplog_ins_hook8 - #pragma weak __sanitizer_cov_trace_const_cmp16 = __cmplog_ins_hook16 - - #pragma weak __sanitizer_cov_trace_cmp1 = __cmplog_ins_hook1 - #pragma weak __sanitizer_cov_trace_cmp2 = __cmplog_ins_hook2 - #pragma weak __sanitizer_cov_trace_cmp4 = __cmplog_ins_hook4 - #pragma weak __sanitizer_cov_trace_cmp8 = __cmplog_ins_hook8 - #pragma weak __sanitizer_cov_trace_cmp16 = __cmplog_ins_hook16 -#else -void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) - __attribute__((alias("__cmplog_ins_hook1"))); -void __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2) - __attribute__((alias("__cmplog_ins_hook2"))); -void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2) - __attribute__((alias("__cmplog_ins_hook4"))); -void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2) - __attribute__((alias("__cmplog_ins_hook8"))); - #ifdef WORD_SIZE_64 -void __sanitizer_cov_trace_const_cmp16(uint128_t arg1, uint128_t arg2) - __attribute__((alias("__cmplog_ins_hook16"))); - #endif +void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) { -void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) - __attribute__((alias("__cmplog_ins_hook1"))); -void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) - __attribute__((alias("__cmplog_ins_hook2"))); -void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) - __attribute__((alias("__cmplog_ins_hook4"))); -void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) - __attribute__((alias("__cmplog_ins_hook8"))); - #ifdef WORD_SIZE_64 -void __sanitizer_cov_trace_cmp16(uint128_t arg1, uint128_t arg2) - __attribute__((alias("__cmplog_ins_hook16"))); - #endif -#endif /* defined(__APPLE__) */ + __cmplog_ins_hook1(arg1, arg2, 0); + +} + +void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) { + + __cmplog_ins_hook2(arg1, arg2, 0); + +} + +void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) { + + __cmplog_ins_hook4(arg1, arg2, 0); + +} + +void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) { + + __cmplog_ins_hook8(arg1, arg2, 0); + +} + +#ifdef WORD_SIZE_64 +void __sanitizer_cov_trace_cmp16(uint128_t arg1, uint128_t arg2) { + + __cmplog_ins_hook16(arg1, arg2, 0); + +} + +#endif void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index 6ce1832f..d4bc0b38 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -277,8 +277,12 @@ bool CmpLogInstructions::hookInstrs(Module &M) { if (max_size % 8) { - max_size = (((max_size / 8) + 1) * 8); - do_cast = 1; + // bitcast from i6 to i8 panics llvm, so ... + continue; + /* + max_size = (((max_size / 8) + 1) * 8); + do_cast = 1; + */ } diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 8979be98..f619a6d3 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1497,10 +1497,10 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, u32 lvl, struct tainted *taint) { struct cmp_header *h = &afl->shm.cmp_map->headers[key]; - struct tainted *t; - u32 i, j, idx, taint_len, loggeds; - u32 have_taint = 1, is_n = 0; - u8 status = 0, found_one = 0; + struct tainted * t; + u32 i, j, idx, taint_len, loggeds; + u32 have_taint = 1, is_n = 0; + u8 status = 0, found_one = 0; /* loop cmps are useless, detect and ignore them */ #ifdef WORD_SIZE_64 @@ -2439,7 +2439,8 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { afl->stage_max = 0; afl->stage_cur = 0; - u32 lvl = (afl->queue_cur->colorized ? 0 : LVL1) + (afl->cmplog_lvl == CMPLOG_LVL_MAX ? LVL3 : 0); + u32 lvl = (afl->queue_cur->colorized ? 0 : LVL1) + + (afl->cmplog_lvl == CMPLOG_LVL_MAX ? LVL3 : 0); #ifdef COMBINE u8 *cbuf = afl_realloc((void **)&afl->in_scratch_buf, len + 128); @@ -2527,7 +2528,7 @@ exit_its: if (afl->cmplog_lvl == CMPLOG_LVL_MAX) { afl->queue_cur->colorized = CMPLOG_LVL_MAX; - + ck_free(afl->queue_cur->cmplog_colorinput); t = taint; while (taint) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 3a7343ae..a579a8f5 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -123,7 +123,8 @@ static void usage(u8 *argv0, int more_help) { "it.\n" " if using QEMU, just use -c 0.\n" " -l cmplog_level - set the complexity/intensivity of CmpLog.\n" - " Values: 1 (basic), 2 (larger files) and 3 (transform)\n\n" + " Values: 1 (basic), 2 (larger files) and 3 " + "(transform)\n\n" "Fuzzing behavior settings:\n" " -Z - sequential queue selection instead of weighted " @@ -1556,17 +1557,19 @@ int main(int argc, char **argv_orig, char **envp) { afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); - afl->top_rated = ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->top_rated = + ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); - afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, new_map_size); + afl->clean_trace_custom = + ck_realloc(afl->clean_trace_custom, new_map_size); afl->first_trace = ck_realloc(afl->first_trace, new_map_size); afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); afl_fsrv_kill(&afl->fsrv); afl_shm_deinit(&afl->shm); afl->fsrv.map_size = new_map_size; - afl->fsrv.trace_bits = afl_shm_init(&afl->shm, new_map_size, - afl->non_instrumented_mode); + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); setenv("AFL_NO_AUTODICT", "1", 1); // loaded already afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); @@ -1608,9 +1611,11 @@ int main(int argc, char **argv_orig, char **envp) { afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); - afl->top_rated = ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->top_rated = + ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); - afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, new_map_size); + afl->clean_trace_custom = + ck_realloc(afl->clean_trace_custom, new_map_size); afl->first_trace = ck_realloc(afl->first_trace, new_map_size); afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); @@ -1619,8 +1624,8 @@ int main(int argc, char **argv_orig, char **envp) { afl_shm_deinit(&afl->shm); afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same - afl->fsrv.trace_bits = afl_shm_init(&afl->shm, new_map_size, - afl->non_instrumented_mode); + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); setenv("AFL_NO_AUTODICT", "1", 1); // loaded already afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); @@ -1786,6 +1791,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->limit_time_puppet = 0; } + */ afl->expand_havoc = 2; if (afl->cmplog_lvl && afl->cmplog_lvl < 2) afl->cmplog_lvl = 2; @@ -1806,7 +1812,7 @@ int main(int argc, char **argv_orig, char **envp) { break; case 5: // if not in sync mode, enable deterministic mode? - //if (!afl->sync_id) afl->skip_deterministic = 0; + // if (!afl->sync_id) afl->skip_deterministic = 0; afl->expand_havoc = 6; case 6: // nothing else currently diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index 1fb01600..0a978653 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -83,7 +83,7 @@ static void edit_params(int argc, char **argv) { if (!passthrough) { - for (i = 1; i < argc; i++) { + for (i = 1; i < (u32)argc; i++) { if (strstr(argv[i], "/afl-llvm-rt-lto.o") != NULL) rt_lto_present = 1; if (strstr(argv[i], "/afl-llvm-rt.o") != NULL) rt_present = 1; @@ -91,7 +91,7 @@ static void edit_params(int argc, char **argv) { } - for (i = 1; i < argc && !gold_pos; i++) { + for (i = 1; i < (u32)argc && !gold_pos; i++) { if (strcmp(argv[i], "-plugin") == 0) { @@ -100,7 +100,9 @@ static void edit_params(int argc, char **argv) { if (strcasestr(argv[i], "LLVMgold.so") != NULL) gold_present = gold_pos = i + 1; - } else if (i < argc && strcasestr(argv[i + 1], "LLVMgold.so") != NULL) { + } else if (i < (u32)argc && + + strcasestr(argv[i + 1], "LLVMgold.so") != NULL) { gold_present = gold_pos = i + 2; @@ -112,7 +114,7 @@ static void edit_params(int argc, char **argv) { if (!gold_pos) { - for (i = 1; i + 1 < argc && !gold_pos; i++) { + for (i = 1; i + 1 < (u32)argc && !gold_pos; i++) { if (argv[i][0] != '-') { @@ -198,7 +200,7 @@ static void edit_params(int argc, char **argv) { gold_present ? "true" : "false", inst_present ? "true" : "false", rt_present ? "true" : "false", rt_lto_present ? "true" : "false"); - for (i = 1; i < argc; i++) { + for (i = 1; i < (u32)argc; i++) { if (ld_param_cnt >= MAX_PARAM_COUNT) FATAL( @@ -324,7 +326,7 @@ int main(int argc, char **argv) { if (debug) { DEBUGF("cd \"%s\";", thecwd); - for (i = 0; i < ld_param_cnt; i++) + for (i = 0; i < (s32)ld_param_cnt; i++) SAYF(" \"%s\"", ld_params[i]); SAYF("\n"); -- cgit 1.4.1 From 654f389e73c9fd5b7e141b33ea28ab0fdda3178f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 23:36:27 +0100 Subject: try to remove warnings during compilation --- src/afl-cc.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 7db3c9a0..f272f0b5 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -313,7 +313,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { static void edit_params(u32 argc, char **argv, char **envp) { u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, shared_linking = 0, - preprocessor_only = 0, have_unroll = 0, have_o = 0, have_pic = 0; + preprocessor_only = 0, have_unroll = 0, have_o = 0, have_pic = 0, + have_c = 0; u8 *name; cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); @@ -461,7 +462,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { // laf if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { - if (lto_mode) { + if (lto_mode && !have_c) { cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); @@ -481,7 +482,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { - if (lto_mode) { + if (lto_mode && !have_c) { cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/compare-transform-pass.so", obj_path); @@ -501,7 +502,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { - if (lto_mode) { + if (lto_mode && !have_c) { cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/split-compares-pass.so", obj_path); @@ -524,7 +525,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { unsetenv("AFL_LD_CALLER"); if (cmplog_mode) { - if (lto_mode) { + if (lto_mode && !have_c) { cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/cmplog-routines-pass.so", obj_path); @@ -560,7 +561,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (lto_mode) { + if (lto_mode && !have_c) { u8 *ld_path = strdup(AFL_REAL_LD); if (!*ld_path) ld_path = "ld.lld"; @@ -708,6 +709,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (!strcmp(cur, "-x")) x_set = 1; if (!strcmp(cur, "-E")) preprocessor_only = 1; if (!strcmp(cur, "-shared")) shared_linking = 1; + if (!strcmp(cur, "-c")) have_c = 1; if (!strncmp(cur, "-O", 2)) have_o = 1; if (!strncmp(cur, "-funroll-loop", 13)) have_unroll = 1; @@ -800,7 +802,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if defined(USEMMAP) && !defined(__HAIKU__) - cc_params[cc_par_cnt++] = "-lrt"; + if (!have_c) + cc_params[cc_par_cnt++] = "-lrt"; #endif cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; @@ -967,15 +970,19 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if !defined(__APPLE__) && !defined(__sun) - if (!shared_linking) + if (!shared_linking && !have_c) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif #if defined(USEMMAP) && !defined(__HAIKU__) - cc_params[cc_par_cnt++] = "-lrt"; + if (!have_c) + cc_params[cc_par_cnt++] = "-lrt"; #endif + // prevent unnecessary build errors + cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; + } #endif -- cgit 1.4.1 From 1f71b85426f837ebcae8381897d44a3a67c73a4f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 2 Feb 2021 10:05:10 +0100 Subject: automagically fix sanitize fuzzer+coverage --- src/afl-cc.c | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index f272f0b5..cba435bd 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -693,6 +693,38 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (strstr(cur, "afl-compiler-rt") || strstr(cur, "afl-llvm-rt")) continue; if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined")) continue; + if (!strncmp(cur, "-fsanitize=fuzzer-", strlen("-fsanitize=fuzzer-")) || + !strncmp(cur, "-fsanitize-coverage", strlen("-fsanitize-coverage"))) { + + if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); } + continue; + + } + + if (!strcmp(cur, "-fsanitize=fuzzer")) { + + u8 *afllib = find_object("libAFLDriver.a", argv[0]); + + if (!be_quiet) + WARNF( + "Found errornous '-fsanitize=fuzzer', trying to replace with " + "libAFLDriver.a"); + + if (!afllib) { + + WARNF( + "Cannot find 'libAFLDriver.a' to replace a wrong " + "'-fsanitize=fuzzer' in the flags - this will fail!"); + + } else { + + cc_params[cc_par_cnt++] = afllib; + + } + + continue; + + } if (!strcmp(cur, "-m32")) bit_mode = 32; if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32; @@ -802,8 +834,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if defined(USEMMAP) && !defined(__HAIKU__) - if (!have_c) - cc_params[cc_par_cnt++] = "-lrt"; + if (!have_c) cc_params[cc_par_cnt++] = "-lrt"; #endif cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; @@ -976,12 +1007,11 @@ static void edit_params(u32 argc, char **argv, char **envp) { #endif #if defined(USEMMAP) && !defined(__HAIKU__) - if (!have_c) - cc_params[cc_par_cnt++] = "-lrt"; + if (!have_c) cc_params[cc_par_cnt++] = "-lrt"; #endif - // prevent unnecessary build errors - cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; + // prevent unnecessary build errors + cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; } -- cgit 1.4.1 From 58a5372bf0c55ead2a04ed4a4a5b651d68e69292 Mon Sep 17 00:00:00 2001 From: hexcoder Date: Wed, 3 Feb 2021 14:18:35 +0100 Subject: typo --- src/afl-cc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index cba435bd..cf10d9a7 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -707,7 +707,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (!be_quiet) WARNF( - "Found errornous '-fsanitize=fuzzer', trying to replace with " + "Found erroneous '-fsanitize=fuzzer', trying to replace with " "libAFLDriver.a"); if (!afllib) { -- cgit 1.4.1 From 6f163bb0c50a103dc4565ec5f0b8b9b94b5c16f6 Mon Sep 17 00:00:00 2001 From: vj-27 Date: Fri, 5 Feb 2021 00:26:23 +0000 Subject: load existing stats file when in AFL_AUTORESUME or -i - --- include/afl-fuzz.h | 1 + src/afl-fuzz-stats.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/afl-fuzz.c | 1 + 3 files changed, 103 insertions(+) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index c3a8c2ee..9e2913a2 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1067,6 +1067,7 @@ void destroy_extras(afl_state_t *); /* Stats */ +void load_stats_file(afl_state_t *); void write_setup_file(afl_state_t *, u32, char **); void write_stats_file(afl_state_t *, double, double, double); void maybe_update_plot_file(afl_state_t *, double, double); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 7e99bf8f..d75b8405 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -89,6 +89,107 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { } +/* load some of the existing stats file when resuming.*/ +void load_stats_file(afl_state_t *afl) { + + FILE *f; + u8 buf[MAX_LINE]; + u8 * lptr; + u8 fn[PATH_MAX]; + u32 lineno = 0; + + snprintf(fn, PATH_MAX, "%s/fuzzer_stats", afl->out_dir); + f = fopen(fn, "r"); + if (!f) { + + WARNF("Unable to load stats file '%s'", fn); + return; + + } + + while ((lptr = fgets(buf, MAX_LINE, f))) { + + lineno++; + u8 *lstartptr = lptr; + u8 *rptr = lptr + strlen(lptr) - 1; + u8 keystring[MAX_LINE]; + while (*lptr != ':' && lptr < rptr) { + + lptr++; + + } + + if (*lptr == '\n' || !*lptr) { + + WARNF("Unable to read line %d of stats file", lineno); + continue; + + } + + if (*lptr == ':') { + + *lptr = 0; + strcpy(keystring, lstartptr); + lptr++; + char *nptr; + switch (lineno) { + + case 5: + if (!strcmp(keystring, "cycles_done ")) + afl->queue_cycle = + strtoull(lptr, &nptr, 10) ? strtoull(lptr, &nptr, 10) + 1 : 0; + break; + case 7: + if (!strcmp(keystring, "execs_done ")) + afl->fsrv.total_execs = strtoull(lptr, &nptr, 10); + break; + case 10: + if (!strcmp(keystring, "paths_total ")) + afl->queued_paths = strtoul(lptr, &nptr, 10); + break; + case 11: + if (!strcmp(keystring, "paths_favored ")) + afl->queued_favored = strtoul(lptr, &nptr, 10); + break; + case 12: + if (!strcmp(keystring, "paths_found ")) + afl->queued_discovered = strtoul(lptr, &nptr, 10); + break; + case 13: + if (!strcmp(keystring, "paths_imported ")) + afl->queued_imported = strtoul(lptr, &nptr, 10); + break; + case 14: + if (!strcmp(keystring, "max_depth ")) + afl->max_depth = strtoul(lptr, &nptr, 10); + break; + case 16: + if (!strcmp(keystring, "pending_favs ")) + afl->pending_favored = strtoul(lptr, &nptr, 10); + break; + case 17: + if (!strcmp(keystring, "pending_total ")) + afl->pending_not_fuzzed = strtoul(lptr, &nptr, 10); + break; + case 21: + if (!strcmp(keystring, "unique_crashes ")) + afl->unique_crashes = strtoull(lptr, &nptr, 10); + break; + case 22: + if (!strcmp(keystring, "unique_hangs ")) + afl->unique_hangs = strtoull(lptr, &nptr, 10); + break; + default: + break; + + } + + } + + } + +} + /* Update stats file for unattended monitoring. */ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index a579a8f5..6c617b18 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1682,6 +1682,7 @@ int main(int argc, char **argv_orig, char **envp) { if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl); + if (afl->in_place_resume || afl->afl_env.afl_autoresume) load_stats_file(afl); write_stats_file(afl, 0, 0, 0); maybe_update_plot_file(afl, 0, 0); save_auto(afl); -- cgit 1.4.1 From 1a8c242d280066b7bfb36897c91215d4f4b5eb01 Mon Sep 17 00:00:00 2001 From: vj-27 Date: Fri, 5 Feb 2021 19:46:24 +0000 Subject: load run time and donot load pending_* or *_favoured --- include/afl-fuzz.h | 2 +- src/afl-fuzz-stats.c | 29 ++++++++++++++--------------- src/afl-fuzz.c | 8 +++++++- 3 files changed, 22 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 9e2913a2..1b2b9a8e 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1067,7 +1067,7 @@ void destroy_extras(afl_state_t *); /* Stats */ -void load_stats_file(afl_state_t *); +u32 load_stats_file(afl_state_t *); void write_setup_file(afl_state_t *, u32, char **); void write_stats_file(afl_state_t *, double, double, double); void maybe_update_plot_file(afl_state_t *, double, double); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index d75b8405..3edb5bb6 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -90,20 +90,20 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { } /* load some of the existing stats file when resuming.*/ -void load_stats_file(afl_state_t *afl) { +u32 load_stats_file(afl_state_t *afl) { FILE *f; u8 buf[MAX_LINE]; u8 * lptr; u8 fn[PATH_MAX]; u32 lineno = 0; - + u32 prev_run_time = 0; snprintf(fn, PATH_MAX, "%s/fuzzer_stats", afl->out_dir); f = fopen(fn, "r"); if (!f) { WARNF("Unable to load stats file '%s'", fn); - return; + return prev_run_time; } @@ -134,6 +134,15 @@ void load_stats_file(afl_state_t *afl) { char *nptr; switch (lineno) { + case 3: + if (!strcmp(keystring, "run_time ")) { + + prev_run_time = 1000 * strtoull(lptr, &nptr, 10); + afl->start_time -= prev_run_time; + + } + + break; case 5: if (!strcmp(keystring, "cycles_done ")) afl->queue_cycle = @@ -147,10 +156,6 @@ void load_stats_file(afl_state_t *afl) { if (!strcmp(keystring, "paths_total ")) afl->queued_paths = strtoul(lptr, &nptr, 10); break; - case 11: - if (!strcmp(keystring, "paths_favored ")) - afl->queued_favored = strtoul(lptr, &nptr, 10); - break; case 12: if (!strcmp(keystring, "paths_found ")) afl->queued_discovered = strtoul(lptr, &nptr, 10); @@ -163,14 +168,6 @@ void load_stats_file(afl_state_t *afl) { if (!strcmp(keystring, "max_depth ")) afl->max_depth = strtoul(lptr, &nptr, 10); break; - case 16: - if (!strcmp(keystring, "pending_favs ")) - afl->pending_favored = strtoul(lptr, &nptr, 10); - break; - case 17: - if (!strcmp(keystring, "pending_total ")) - afl->pending_not_fuzzed = strtoul(lptr, &nptr, 10); - break; case 21: if (!strcmp(keystring, "unique_crashes ")) afl->unique_crashes = strtoull(lptr, &nptr, 10); @@ -188,6 +185,8 @@ void load_stats_file(afl_state_t *afl) { } + return prev_run_time; + } /* Update stats file for unattended monitoring. */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 6c617b18..b7cd251a 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1682,7 +1682,11 @@ int main(int argc, char **argv_orig, char **envp) { if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl); - if (afl->in_place_resume || afl->afl_env.afl_autoresume) load_stats_file(afl); + u32 prev_run_time = 0; // to not call load_stats_file again after line 1705 + afl->start_time = get_cur_time(); // without this, time taken for + // perform_dry_run gets added to run time. + if (afl->in_place_resume || afl->afl_env.afl_autoresume) + prev_run_time = load_stats_file(afl); write_stats_file(afl, 0, 0, 0); maybe_update_plot_file(afl, 0, 0); save_auto(afl); @@ -1701,6 +1705,8 @@ int main(int argc, char **argv_orig, char **envp) { // (void)nice(-20); // does not improve the speed // real start time, we reset, so this works correctly with -V afl->start_time = get_cur_time(); + if (afl->in_place_resume || afl->afl_env.afl_autoresume) + afl->start_time -= prev_run_time; u32 runs_in_current_cycle = (u32)-1; u32 prev_queued_paths = 0; -- cgit 1.4.1 From f54c4dbfdb17a06798b337a2182d7cf33ec178dd Mon Sep 17 00:00:00 2001 From: vj-27 Date: Sat, 6 Feb 2021 09:41:15 +0000 Subject: set prev_run_time inside afl state --- include/afl-fuzz.h | 3 ++- src/afl-fuzz-stats.c | 12 ++++++------ src/afl-fuzz.c | 6 ++---- 3 files changed, 10 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 1b2b9a8e..4027a88f 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -569,6 +569,7 @@ typedef struct afl_state { blocks_eff_total, /* Blocks subject to effector maps */ blocks_eff_select, /* Blocks selected as fuzzable */ start_time, /* Unix start time (ms) */ + prev_run_time, /* Runtime read from prev stats file*/ last_path_time, /* Time for most recent path (ms) */ last_crash_time, /* Time for most recent crash (ms) */ last_hang_time; /* Time for most recent hang (ms) */ @@ -1067,7 +1068,7 @@ void destroy_extras(afl_state_t *); /* Stats */ -u32 load_stats_file(afl_state_t *); +void load_stats_file(afl_state_t *); void write_setup_file(afl_state_t *, u32, char **); void write_stats_file(afl_state_t *, double, double, double); void maybe_update_plot_file(afl_state_t *, double, double); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 3edb5bb6..880551d3 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -90,20 +90,20 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { } /* load some of the existing stats file when resuming.*/ -u32 load_stats_file(afl_state_t *afl) { +void load_stats_file(afl_state_t *afl) { FILE *f; u8 buf[MAX_LINE]; u8 * lptr; u8 fn[PATH_MAX]; u32 lineno = 0; - u32 prev_run_time = 0; + afl->prev_run_time = 0; snprintf(fn, PATH_MAX, "%s/fuzzer_stats", afl->out_dir); f = fopen(fn, "r"); if (!f) { WARNF("Unable to load stats file '%s'", fn); - return prev_run_time; + return; } @@ -137,8 +137,8 @@ u32 load_stats_file(afl_state_t *afl) { case 3: if (!strcmp(keystring, "run_time ")) { - prev_run_time = 1000 * strtoull(lptr, &nptr, 10); - afl->start_time -= prev_run_time; + afl->prev_run_time = 1000 * strtoull(lptr, &nptr, 10); + afl->start_time -= afl->prev_run_time; } @@ -185,7 +185,7 @@ u32 load_stats_file(afl_state_t *afl) { } - return prev_run_time; + return; } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index b7cd251a..08724959 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1682,11 +1682,9 @@ int main(int argc, char **argv_orig, char **envp) { if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl); - u32 prev_run_time = 0; // to not call load_stats_file again after line 1705 afl->start_time = get_cur_time(); // without this, time taken for // perform_dry_run gets added to run time. - if (afl->in_place_resume || afl->afl_env.afl_autoresume) - prev_run_time = load_stats_file(afl); + if (afl->in_place_resume || afl->afl_env.afl_autoresume) load_stats_file(afl); write_stats_file(afl, 0, 0, 0); maybe_update_plot_file(afl, 0, 0); save_auto(afl); @@ -1706,7 +1704,7 @@ int main(int argc, char **argv_orig, char **envp) { // real start time, we reset, so this works correctly with -V afl->start_time = get_cur_time(); if (afl->in_place_resume || afl->afl_env.afl_autoresume) - afl->start_time -= prev_run_time; + afl->start_time -= afl->prev_run_time; u32 runs_in_current_cycle = (u32)-1; u32 prev_queued_paths = 0; -- cgit 1.4.1 From d920104248a6c5387267561382636de404938675 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 6 Feb 2021 12:26:35 +0100 Subject: remove compiler warnings --- instrumentation/afl-compiler-rt.o.c | 20 ++++++++++---------- src/afl-cc.c | 4 ++++ utils/aflpp_driver/aflpp_driver.c | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 905051c6..ceae15d1 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -368,8 +368,8 @@ static void __afl_map_shm(void) { if (__afl_map_size && __afl_map_size > MAP_SIZE) { - u8 *map_env = getenv("AFL_MAP_SIZE"); - if (!map_env || atoi(map_env) < MAP_SIZE) { + u8 *map_env = (u8 *)getenv("AFL_MAP_SIZE"); + if (!map_env || atoi((char *)map_env) < MAP_SIZE) { send_forkserver_error(FS_ERROR_MAP_SIZE); _exit(1); @@ -378,7 +378,7 @@ static void __afl_map_shm(void) { } - __afl_area_ptr = shmat(shm_id, (void *)__afl_map_addr, 0); + __afl_area_ptr = (u8 *)shmat(shm_id, (void *)__afl_map_addr, 0); /* Whooooops. */ @@ -405,9 +405,9 @@ static void __afl_map_shm(void) { __afl_map_addr) { - __afl_area_ptr = - mmap((void *)__afl_map_addr, __afl_map_size, PROT_READ | PROT_WRITE, - MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); + __afl_area_ptr = (u8 *)mmap( + (void *)__afl_map_addr, __afl_map_size, PROT_READ | PROT_WRITE, + MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (__afl_area_ptr == MAP_FAILED) { @@ -425,7 +425,7 @@ static void __afl_map_shm(void) { if (__afl_final_loc > MAP_INITIAL_SIZE) { - __afl_area_ptr = malloc(__afl_final_loc); + __afl_area_ptr = (u8 *)malloc(__afl_final_loc); } @@ -439,7 +439,7 @@ static void __afl_map_shm(void) { if (__afl_map_size > MAP_INITIAL_SIZE) { - __afl_area_ptr_dummy = malloc(__afl_map_size); + __afl_area_ptr_dummy = (u8 *)malloc(__afl_map_size); if (__afl_area_ptr_dummy) { @@ -505,7 +505,7 @@ static void __afl_map_shm(void) { #else u32 shm_id = atoi(id_str); - __afl_cmp_map = shmat(shm_id, NULL, 0); + __afl_cmp_map = (struct cmp_map *)shmat(shm_id, NULL, 0); #endif __afl_cmp_map_backup = __afl_cmp_map; @@ -1528,7 +1528,7 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { // to avoid to call it on .text addresses static int area_is_mapped(void *ptr, size_t len) { - char *p = ptr; + char *p = (char *)ptr; char *page = (char *)((uintptr_t)p & ~(sysconf(_SC_PAGE_SIZE) - 1)); int r = msync(page, (p - page) + len, MS_ASYNC); diff --git a/src/afl-cc.c b/src/afl-cc.c index cf10d9a7..76f4a437 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1840,6 +1840,8 @@ int main(int argc, char **argv, char **envp) { for (i = 0; i < argc; i++) SAYF(" '%s'", argv[i]); SAYF("\n"); + fflush(stdout); + fflush(stderr); } @@ -1880,6 +1882,8 @@ int main(int argc, char **argv, char **envp) { for (i = 0; i < (s32)cc_par_cnt; i++) SAYF(" '%s'", cc_params[i]); SAYF("\n"); + fflush(stdout); + fflush(stderr); } diff --git a/utils/aflpp_driver/aflpp_driver.c b/utils/aflpp_driver/aflpp_driver.c index 7bb929b2..6af79e14 100644 --- a/utils/aflpp_driver/aflpp_driver.c +++ b/utils/aflpp_driver/aflpp_driver.c @@ -173,7 +173,7 @@ size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { // Execute any files provided as parameters. static int ExecuteFilesOnyByOne(int argc, char **argv) { - unsigned char *buf = malloc(MAX_FILE); + unsigned char *buf = (unsigned char *)malloc(MAX_FILE); for (int i = 1; i < argc; i++) { int fd = open(argv[i], O_RDONLY); -- cgit 1.4.1 From 96cdc97c98ee2e2af7df59252f4f0df1689afb7b Mon Sep 17 00:00:00 2001 From: vj-27 Date: Sun, 7 Feb 2021 03:33:47 +0530 Subject: prev_run_time loaded used only for ui and when writing the stats file --- include/afl-fuzz.h | 4 ++-- src/afl-fuzz-stats.c | 25 ++++++++++--------------- src/afl-fuzz.c | 5 +---- 3 files changed, 13 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 4027a88f..1d5ec1f0 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -425,7 +425,8 @@ typedef struct afl_state { really makes no sense to haul them around as function parameters. */ u64 orig_hit_cnt_puppet, last_limit_time_start, tmp_pilot_time, total_pacemaker_time, total_puppet_find, temp_puppet_find, most_time_key, - most_time, most_execs_key, most_execs, old_hit_count, force_ui_update; + most_time, most_execs_key, most_execs, old_hit_count, force_ui_update, + prev_run_time; MOpt_globals_t mopt_globals_core, mopt_globals_pilot; @@ -569,7 +570,6 @@ typedef struct afl_state { blocks_eff_total, /* Blocks subject to effector maps */ blocks_eff_select, /* Blocks selected as fuzzable */ start_time, /* Unix start time (ms) */ - prev_run_time, /* Runtime read from prev stats file*/ last_path_time, /* Time for most recent path (ms) */ last_crash_time, /* Time for most recent crash (ms) */ last_hang_time; /* Time for most recent hang (ms) */ diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 880551d3..66efeb20 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -97,7 +97,6 @@ void load_stats_file(afl_state_t *afl) { u8 * lptr; u8 fn[PATH_MAX]; u32 lineno = 0; - afl->prev_run_time = 0; snprintf(fn, PATH_MAX, "%s/fuzzer_stats", afl->out_dir); f = fopen(fn, "r"); if (!f) { @@ -135,13 +134,8 @@ void load_stats_file(afl_state_t *afl) { switch (lineno) { case 3: - if (!strcmp(keystring, "run_time ")) { - + if (!strcmp(keystring, "run_time ")) afl->prev_run_time = 1000 * strtoull(lptr, &nptr, 10); - afl->start_time -= afl->prev_run_time; - - } - break; case 5: if (!strcmp(keystring, "cycles_done ")) @@ -279,12 +273,13 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, "\n" "target_mode : %s%s%s%s%s%s%s%s%s\n" "command_line : %s\n", - afl->start_time / 1000, cur_time / 1000, - (cur_time - afl->start_time) / 1000, (u32)getpid(), - afl->queue_cycle ? (afl->queue_cycle - 1) : 0, afl->cycles_wo_finds, - afl->fsrv.total_execs, + (afl->start_time - afl->prev_run_time) / 1000, cur_time / 1000, + (afl->prev_run_time + cur_time - afl->start_time) / 1000, + (u32)getpid(), afl->queue_cycle ? (afl->queue_cycle - 1) : 0, + afl->cycles_wo_finds, afl->fsrv.total_execs, afl->fsrv.total_execs / - ((double)(get_cur_time() - afl->start_time) / 1000), + ((double)(afl->prev_run_time + get_cur_time() - afl->start_time) / + 1000), afl->last_avg_execs_saved, afl->queued_paths, afl->queued_favored, afl->queued_discovered, afl->queued_imported, afl->max_depth, afl->current_entry, afl->pending_favored, afl->pending_not_fuzzed, @@ -479,8 +474,8 @@ void show_stats(afl_state_t *afl) { if (likely(cur_ms != afl->start_time)) { - afl->stats_avg_exec = - ((double)afl->fsrv.total_execs) * 1000 / (cur_ms - afl->start_time); + afl->stats_avg_exec = ((double)afl->fsrv.total_execs) * 1000 / + (afl->prev_run_time + cur_ms - afl->start_time); } @@ -692,7 +687,7 @@ void show_stats(afl_state_t *afl) { } - u_stringify_time_diff(time_tmp, cur_ms, afl->start_time); + u_stringify_time_diff(time_tmp, afl->prev_run_time + cur_ms, afl->start_time); SAYF(bV bSTOP " run time : " cRST "%-33s " bSTG bV bSTOP " cycles done : %s%-5s " bSTG bV "\n", time_tmp, tmp, u_stringify_int(IB(0), afl->queue_cycle - 1)); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 08724959..e4139857 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1682,8 +1682,7 @@ int main(int argc, char **argv_orig, char **envp) { if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl); - afl->start_time = get_cur_time(); // without this, time taken for - // perform_dry_run gets added to run time. + afl->start_time = get_cur_time(); if (afl->in_place_resume || afl->afl_env.afl_autoresume) load_stats_file(afl); write_stats_file(afl, 0, 0, 0); maybe_update_plot_file(afl, 0, 0); @@ -1703,8 +1702,6 @@ int main(int argc, char **argv_orig, char **envp) { // (void)nice(-20); // does not improve the speed // real start time, we reset, so this works correctly with -V afl->start_time = get_cur_time(); - if (afl->in_place_resume || afl->afl_env.afl_autoresume) - afl->start_time -= afl->prev_run_time; u32 runs_in_current_cycle = (u32)-1; u32 prev_queued_paths = 0; -- cgit 1.4.1 From 209c5ba4657b641bf261da7ac9ce7d3f809109c2 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sun, 7 Feb 2021 05:33:02 +0100 Subject: larger map, stats reload fix, code format --- docs/Changelog.md | 2 + instrumentation/afl-compiler-rt.o.c | 2 +- instrumentation/afl-llvm-lto-instrumentation.so.cc | 3 +- qemu_mode/libqasan/dlmalloc.c | 5 ++ src/afl-fuzz-bitmap.c | 3 +- src/afl-fuzz-statsd.c | 63 ++++++++++++---------- utils/afl_untracer/afl-untracer.c | 10 ++-- 7 files changed, 52 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index e9efdf38..f2041917 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -26,6 +26,8 @@ sending a mail to . `-i` or resumes (as these have most likely already been done) - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) + - on restarts (-i)/autoresume (AFL_AUTORESUME) the stats are now + reloaded and used, thanks to Vimal Joseph for this PR! - if determinstic mode is active (-D, or -M without -d) then we sync after every queue entry as this can take very long time otherwise - better detection if a target needs a large shared map diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 65a5d3d2..059691ec 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -70,7 +70,7 @@ run. It will end up as .comm, so it shouldn't be too wasteful. */ #if MAP_SIZE <= 65536 - #define MAP_INITIAL_SIZE 1048576 + #define MAP_INITIAL_SIZE 2097152 #else #define MAP_INITIAL_SIZE MAP_SIZE #endif diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc index fa494f44..841d52e5 100644 --- a/instrumentation/afl-llvm-lto-instrumentation.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc @@ -69,7 +69,8 @@ class AFLLTOPass : public ModulePass { if (getenv("AFL_DEBUG")) debug = 1; if ((ptr = getenv("AFL_LLVM_LTO_STARTID")) != NULL) - if ((afl_global_id = (uint32_t)atoi(ptr)) < 0 || afl_global_id >= MAP_SIZE) + if ((afl_global_id = (uint32_t)atoi(ptr)) < 0 || + afl_global_id >= MAP_SIZE) FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is not between 0 and %u\n", ptr, MAP_SIZE - 1); diff --git a/qemu_mode/libqasan/dlmalloc.c b/qemu_mode/libqasan/dlmalloc.c index 39ca4301..ce94451d 100644 --- a/qemu_mode/libqasan/dlmalloc.c +++ b/qemu_mode/libqasan/dlmalloc.c @@ -3907,6 +3907,7 @@ static void internal_malloc_stats(mstate m) { clear_smallmap(M, I); \ \ } else if (RTCHECK(B == smallbin_at(M, I) || \ + \ (ok_address(M, B) && B->fd == P))) { \ \ F->bk = B; \ @@ -4117,6 +4118,7 @@ static void internal_malloc_stats(mstate m) { XP->child[1] = R; \ \ } else \ + \ CORRUPTION_ERROR_ACTION(M); \ if (R != 0) { \ \ @@ -4132,6 +4134,7 @@ static void internal_malloc_stats(mstate m) { C0->parent = R; \ \ } else \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ @@ -4143,11 +4146,13 @@ static void internal_malloc_stats(mstate m) { C1->parent = R; \ \ } else \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ \ } else \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 0c4a114e..4ed59364 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -325,7 +325,8 @@ u8 *describe_op(afl_state_t *afl, u8 new_bits, size_t max_description_len) { } - sprintf(ret + strlen(ret), ",time:%llu", get_cur_time() - afl->start_time); + sprintf(ret + strlen(ret), ",time:%llu", + get_cur_time() + afl->prev_run_time - afl->start_time); if (afl->current_custom_fuzz && afl->current_custom_fuzz->afl_custom_describe) { diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index 69cafd90..461bbbf6 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -1,3 +1,8 @@ +/* + * This implements rpc.statsd support, see docs/rpc_statsd.md + * + */ + #include #include #include @@ -226,37 +231,39 @@ int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen) { */ if (afl->statsd_metric_format_type == STATSD_TAGS_TYPE_SUFFIX) { - snprintf(buff, bufflen, afl->statsd_metric_format, - afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, - afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, - afl->fsrv.total_execs / - ((double)(get_cur_time() - afl->start_time) / 1000), - tags, afl->queued_paths, tags, afl->queued_favored, tags, - afl->queued_discovered, tags, afl->queued_imported, tags, - afl->max_depth, tags, afl->current_entry, tags, - afl->pending_favored, tags, afl->pending_not_fuzzed, tags, - afl->queued_variable, tags, afl->unique_crashes, tags, - afl->unique_hangs, tags, afl->total_crashes, tags, - afl->slowest_exec_ms, tags, - count_non_255_bytes(afl, afl->virgin_bits), tags, - afl->var_byte_count, tags, afl->expand_havoc, tags); + snprintf( + buff, bufflen, afl->statsd_metric_format, + afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, + afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, + afl->fsrv.total_execs / + ((double)(get_cur_time() + afl->prev_run_time - afl->start_time) / + 1000), + tags, afl->queued_paths, tags, afl->queued_favored, tags, + afl->queued_discovered, tags, afl->queued_imported, tags, + afl->max_depth, tags, afl->current_entry, tags, afl->pending_favored, + tags, afl->pending_not_fuzzed, tags, afl->queued_variable, tags, + afl->unique_crashes, tags, afl->unique_hangs, tags, afl->total_crashes, + tags, afl->slowest_exec_ms, tags, + count_non_255_bytes(afl, afl->virgin_bits), tags, afl->var_byte_count, + tags, afl->expand_havoc, tags); } else if (afl->statsd_metric_format_type == STATSD_TAGS_TYPE_MID) { - snprintf(buff, bufflen, afl->statsd_metric_format, tags, - afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, - afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, - afl->fsrv.total_execs / - ((double)(get_cur_time() - afl->start_time) / 1000), - tags, afl->queued_paths, tags, afl->queued_favored, tags, - afl->queued_discovered, tags, afl->queued_imported, tags, - afl->max_depth, tags, afl->current_entry, tags, - afl->pending_favored, tags, afl->pending_not_fuzzed, tags, - afl->queued_variable, tags, afl->unique_crashes, tags, - afl->unique_hangs, tags, afl->total_crashes, tags, - afl->slowest_exec_ms, tags, - count_non_255_bytes(afl, afl->virgin_bits), tags, - afl->var_byte_count, tags, afl->expand_havoc); + snprintf( + buff, bufflen, afl->statsd_metric_format, tags, + afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, + afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, + afl->fsrv.total_execs / + ((double)(get_cur_time() + afl->prev_run_time - afl->start_time) / + 1000), + tags, afl->queued_paths, tags, afl->queued_favored, tags, + afl->queued_discovered, tags, afl->queued_imported, tags, + afl->max_depth, tags, afl->current_entry, tags, afl->pending_favored, + tags, afl->pending_not_fuzzed, tags, afl->queued_variable, tags, + afl->unique_crashes, tags, afl->unique_hangs, tags, afl->total_crashes, + tags, afl->slowest_exec_ms, tags, + count_non_255_bytes(afl, afl->virgin_bits), tags, afl->var_byte_count, + tags, afl->expand_havoc); } diff --git a/utils/afl_untracer/afl-untracer.c b/utils/afl_untracer/afl-untracer.c index 1f1a10ea..2baeb58d 100644 --- a/utils/afl_untracer/afl-untracer.c +++ b/utils/afl_untracer/afl-untracer.c @@ -480,9 +480,9 @@ void setup_trap_instrumentation(void) { // Index into the coverage bitmap for the current trap instruction. #ifdef __aarch64__ uint64_t bitmap_index = 0; -#ifdef __APPLE__ + #ifdef __APPLE__ pthread_jit_write_protect_np(0); -#endif + #endif #else uint32_t bitmap_index = 0; #endif @@ -627,13 +627,13 @@ static void sigtrap_handler(int signum, siginfo_t *si, void *context) { // Must re-execute the instruction, so decrement PC by one instruction. ucontext_t *ctx = (ucontext_t *)context; #if defined(__APPLE__) && defined(__LP64__) -#if defined(__x86_64__) + #if defined(__x86_64__) ctx->uc_mcontext->__ss.__rip -= 1; addr = ctx->uc_mcontext->__ss.__rip; -#else + #else ctx->uc_mcontext->__ss.__pc -= 4; addr = ctx->uc_mcontext->__ss.__pc; -#endif + #endif #elif defined(__linux__) #if defined(__x86_64__) || defined(__i386__) ctx->uc_mcontext.gregs[REG_RIP] -= 1; -- cgit 1.4.1 From 0ad56167c53ae660d40ccc6cdedb39f0a52eefcd Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sun, 7 Feb 2021 07:51:29 +0100 Subject: fix scan-build issues --- instrumentation/LLVMInsTrim.so.cc | 2 +- instrumentation/SanitizerCoverageLTO.so.cc | 2 +- instrumentation/afl-llvm-common.cc | 2 +- instrumentation/afl-llvm-dict2file.so.cc | 1 - instrumentation/afl-llvm-lto-instrumentation.so.cc | 2 +- instrumentation/afl-llvm-pass.so.cc | 1 + instrumentation/compare-transform-pass.so.cc | 2 +- instrumentation/split-compares-pass.so.cc | 37 +++++++++------------- src/afl-cc.c | 7 ---- src/afl-fuzz-init.c | 2 +- src/afl-fuzz-redqueen.c | 36 ++++++++++++++++++--- 11 files changed, 53 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/instrumentation/LLVMInsTrim.so.cc b/instrumentation/LLVMInsTrim.so.cc index 235ee30f..948f8f3a 100644 --- a/instrumentation/LLVMInsTrim.so.cc +++ b/instrumentation/LLVMInsTrim.so.cc @@ -459,7 +459,7 @@ struct InsTrim : public ModulePass { BasicBlock *PBB = *PI; auto It = PredMap.insert({PBB, genLabel()}); unsigned Label = It.first->second; - cur_loc = Label; + // cur_loc = Label; PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB); } diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index e3490847..3026abc8 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -760,7 +760,7 @@ bool ModuleSanitizerCoverage::instrumentModule( if (literalLength + 1 == optLength) { Str2.append("\0", 1); // add null byte - addedNull = true; + // addedNull = true; } diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc index a27c4069..aa54f4f7 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -351,7 +351,7 @@ static std::string getSourceName(llvm::Function *F) { if (cDILoc) { instFilename = cDILoc->getFilename(); } - if (instFilename.str().empty()) { + if (instFilename.str().empty() && cDILoc) { /* If the original location is empty, try using the inlined location */ diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc index a4b33732..6f34ac5a 100644 --- a/instrumentation/afl-llvm-dict2file.so.cc +++ b/instrumentation/afl-llvm-dict2file.so.cc @@ -430,7 +430,6 @@ bool AFLdict2filePass::runOnModule(Module &M) { if (literalLength + 1 == optLength) { Str2.append("\0", 1); // add null byte - addedNull = true; } diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc index 841d52e5..f5c24e41 100644 --- a/instrumentation/afl-llvm-lto-instrumentation.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc @@ -546,7 +546,7 @@ bool AFLLTOPass::runOnModule(Module &M) { if (literalLength + 1 == optLength) { Str2.append("\0", 1); // add null byte - addedNull = true; + // addedNull = true; } diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index 57ff3b47..16fd9c94 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -538,6 +538,7 @@ bool AFLCoverage::runOnModule(Module &M) { Store = IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc); + Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); } diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index da5cf7e9..8b00d8d1 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -391,7 +391,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, if (val && !val->empty()) { Str2 = StringRef(*val); - HasStr2 = true; + // HasStr2 = true; } diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc index b6d8c466..80cd90ba 100644 --- a/instrumentation/split-compares-pass.so.cc +++ b/instrumentation/split-compares-pass.so.cc @@ -407,6 +407,7 @@ bool SplitComparesTransform::simplifyIntSignedness(Module &M) { auto op1 = IcmpInst->getOperand(1); IntegerType *intTyOp0 = dyn_cast(op0->getType()); + if (!intTyOp0) { continue; } unsigned bitw = intTyOp0->getBitWidth(); IntegerType *IntType = IntegerType::get(C, bitw); @@ -606,10 +607,11 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { : sizeInBits == 64 ? 53 : sizeInBits == 128 ? 113 : sizeInBits == 16 ? 11 - /* sizeInBits == 80 */ - : 65; + : sizeInBits == 80 ? 65 + : sizeInBits - 8; - const unsigned shiftR_exponent = precision - 1; + const unsigned shiftR_exponent = precision - 1; + // BUG FIXME TODO: u64 does not work for > 64 bit ... e.g. 80 and 128 bit const unsigned long long mask_fraction = (1ULL << (shiftR_exponent - 1)) | ((1ULL << (shiftR_exponent - 1)) - 1); const unsigned long long mask_exponent = @@ -1300,12 +1302,9 @@ bool SplitComparesTransform::runOnModule(Module &M) { case 64: count += splitIntCompares(M, bitw); - /* - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << - count - << " split\n"; - */ + if (debug) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << count + << " split\n"; bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) @@ -1313,12 +1312,9 @@ bool SplitComparesTransform::runOnModule(Module &M) { #endif case 32: count += splitIntCompares(M, bitw); - /* - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << - count - << " split\n"; - */ + if (debug) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << count + << " split\n"; bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) @@ -1326,13 +1322,10 @@ bool SplitComparesTransform::runOnModule(Module &M) { #endif case 16: count += splitIntCompares(M, bitw); - /* - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << - count - << " split\n"; - */ - bitw >>= 1; + if (debug) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << count + << " split\n"; + // bitw >>= 1; break; default: diff --git a/src/afl-cc.c b/src/afl-cc.c index 76f4a437..0ae401e7 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -315,16 +315,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, shared_linking = 0, preprocessor_only = 0, have_unroll = 0, have_o = 0, have_pic = 0, have_c = 0; - u8 *name; cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); - name = strrchr(argv[0], '/'); - if (!name) - name = argv[0]; - else - ++name; - if (lto_mode) { if (lto_flag[0] != '-') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 40ba20c7..702e732d 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -1242,7 +1242,7 @@ static void link_or_copy(u8 *old_path, u8 *new_path) { void pivot_inputs(afl_state_t *afl) { - struct queue_entry *q = afl->queue; + struct queue_entry *q; u32 id = 0, i; ACTF("Creating hard links for all input files..."); diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index f619a6d3..002929c5 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1415,7 +1415,7 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { } else if (b[k] == 0xff) { - ++cons_0; + ++cons_ff; } else { @@ -1473,7 +1473,7 @@ static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) { } else if (b[k] == 0xff) { - ++cons_0; + ++cons_ff; } else { @@ -2410,7 +2410,21 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { // manually clear the full cmp_map memset(afl->shm.cmp_map, 0, sizeof(struct cmp_map)); - if (unlikely(common_fuzz_cmplog_stuff(afl, orig_buf, len))) { return 1; } + if (unlikely(common_fuzz_cmplog_stuff(afl, orig_buf, len))) { + + afl->queue_cur->colorized = CMPLOG_LVL_MAX; + while (taint) { + + t = taint->next; + ck_free(taint); + taint = t; + + } + + return 1; + + } + if (unlikely(!afl->orig_cmp_map)) { afl->orig_cmp_map = ck_alloc_nozero(sizeof(struct cmp_map)); @@ -2419,7 +2433,20 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { memcpy(afl->orig_cmp_map, afl->shm.cmp_map, sizeof(struct cmp_map)); memset(afl->shm.cmp_map->headers, 0, sizeof(struct cmp_header) * CMP_MAP_W); - if (unlikely(common_fuzz_cmplog_stuff(afl, buf, len))) { return 1; } + if (unlikely(common_fuzz_cmplog_stuff(afl, buf, len))) { + + afl->queue_cur->colorized = CMPLOG_LVL_MAX; + while (taint) { + + t = taint->next; + ck_free(taint); + taint = t; + + } + + return 1; + + } #ifdef _DEBUG dump("ORIG", orig_buf, len); @@ -2530,7 +2557,6 @@ exit_its: afl->queue_cur->colorized = CMPLOG_LVL_MAX; ck_free(afl->queue_cur->cmplog_colorinput); - t = taint; while (taint) { t = taint->next; -- cgit 1.4.1 From 17cbb03ba7d4fc0eb3b3b47911c58e25b567e89b Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 9 Feb 2021 09:18:24 +0100 Subject: more cmplog options in config.h --- include/config.h | 12 ++++++++--- src/afl-fuzz-redqueen.c | 56 ++++++++++++++++++++++--------------------------- 2 files changed, 34 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/include/config.h b/include/config.h index 60872785..25fa1142 100644 --- a/include/config.h +++ b/include/config.h @@ -36,17 +36,23 @@ /* CMPLOG/REDQUEEN TUNING * - * Here you can tuning and solving options for cmplog. + * Here you can modify tuning and solving options for CMPLOG. * Note that these are run-time options for afl-fuzz, no target * recompilation required. * */ +/* Enable arithmetic compare solving for both path */ +#define CMPLOG_SOLVE_ARITHMETIC + /* Enable transform following (XOR/ADD/SUB manipulations, hex en/decoding) */ -// #define CMPLOG_TRANSFORM +#define CMPLOG_SOLVE_TRANSFORM /* if TRANSFORM is enabled, this additionally enables base64 en/decoding */ -// #define CMPLOG_TRANSFORM_BASE64 +// #define CMPLOG_SOLVE_TRANSFORM_BASE64 + +/* If a redqueen pass finds more than one solve, try to combine them? */ +#define CMPLOG_COMBINE /* Minimum % of the corpus to perform cmplog on. Default: 20% */ #define CMPLOG_CORPUS_PERCENT 20U diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 002929c5..7844eedf 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -30,8 +30,7 @@ //#define _DEBUG //#define CMPLOG_INTROSPECTION -#define COMBINE -#define ARITHMETIC_LESSER_GREATER +#define CMPLOG_COMBINE // CMP attribute enum enum { @@ -496,7 +495,7 @@ static u8 its_fuzz(afl_state_t *afl, u8 *buf, u32 len, u8 *status) { } -#ifdef CMPLOG_TRANSFORM +#ifdef CMPLOG_SOLVE_TRANSFORM static int strntoll(const char *str, size_t sz, char **end, int base, long long *out) { @@ -577,7 +576,7 @@ static int is_hex(const char *str) { } - #ifdef CMPLOG_TRANSFORM_BASE64 + #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 // tests 4 bytes at location static int is_base64(const char *str) { @@ -717,7 +716,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // o_pattern, pattern, repl, changed_val, idx, taint_len, // h->shape + 1, attr); -#ifdef CMPLOG_TRANSFORM +#ifdef CMPLOG_SOLVE_TRANSFORM // reverse atoi()/strnu?toll() is expensive, so we only to it in lvl 3 if (lvl & LVL3) { @@ -1009,7 +1008,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u64 tmp_64 = *buf_64; *buf_64 = repl; if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } -#ifdef COMBINE +#ifdef CMPLOG_COMBINE if (*status == 1) { memcpy(cbuf + idx, buf_64, 8); } #endif *buf_64 = tmp_64; @@ -1050,7 +1049,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u32 tmp_32 = *buf_32; *buf_32 = (u32)repl; if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } -#ifdef COMBINE +#ifdef CMPLOG_COMBINE if (*status == 1) { memcpy(cbuf + idx, buf_32, 4); } #endif *buf_32 = tmp_32; @@ -1084,7 +1083,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u16 tmp_16 = *buf_16; *buf_16 = (u16)repl; if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } -#ifdef COMBINE +#ifdef CMPLOG_COMBINE if (*status == 1) { memcpy(cbuf + idx, buf_16, 2); } #endif *buf_16 = tmp_16; @@ -1122,7 +1121,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u8 tmp_8 = *buf_8; *buf_8 = (u8)repl; if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } -#ifdef COMBINE +#ifdef CMPLOG_COMBINE if (*status == 1) { cbuf[idx] = *buf_8; } #endif *buf_8 = tmp_8; @@ -1139,7 +1138,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // 16 = modified float, 32 = modified integer (modified = wont match // in original buffer) -#ifdef ARITHMETIC_LESSER_GREATER +#ifdef CMPLOG_SOLVE_ARITHMETIC if (lvl < LVL3 || attr == IS_TRANSFORM) { return 0; } if (!(attr & (IS_GREATER | IS_LESSER)) || SHAPE_BYTES(h->shape) < 4) { @@ -1304,7 +1303,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } -#endif /* ARITHMETIC_LESSER_GREATER */ +#endif /* CMPLOG_SOLVE_ARITHMETIC */ return 0; @@ -1366,7 +1365,7 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - #ifdef COMBINE + #ifdef CMPLOG_COMBINE if (*status == 1) { memcpy(cbuf + idx, r, shape); } #endif @@ -1774,10 +1773,10 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, u8 lvl, u8 *status) { -#ifndef COMBINE +#ifndef CMPLOG_COMBINE (void)(cbuf); #endif -#ifndef CMPLOG_TRANSFORM +#ifndef CMPLOG_SOLVE_TRANSFORM (void)(changed_val); #endif @@ -1847,7 +1846,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } -#ifdef COMBINE +#ifdef CMPLOG_COMBINE if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); } #endif @@ -1859,14 +1858,14 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } -#ifdef CMPLOG_TRANSFORM +#ifdef CMPLOG_SOLVE_TRANSFORM if (*status == 1) return 0; if (lvl & LVL3) { u32 toupper = 0, tolower = 0, xor = 0, arith = 0, tohex = 0, fromhex = 0; - #ifdef CMPLOG_TRANSFORM_BASE64 + #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 u32 tob64 = 0, fromb64 = 0; #endif u32 from_0 = 0, from_x = 0, from_X = 0, from_slash = 0, from_up = 0; @@ -1964,7 +1963,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - #ifdef CMPLOG_TRANSFORM_BASE64 + #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 if (i % 3 == 2 && i < 24) { if (is_base64(repl + ((i / 3) << 2))) tob64 += 3; @@ -2012,13 +2011,13 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, "from_0=%u from_slash=%u from_x=%u\n", idx, i, xor, arith, tolower, toupper, tohex, fromhex, to_0, to_slash, to_x, from_0, from_slash, from_x); - #ifdef CMPLOG_TRANSFORM_BASE64 + #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 fprintf(stderr, "RTN idx=%u loop=%u tob64=%u from64=%u\n", tob64, fromb64); #endif #endif - #ifdef CMPLOG_TRANSFORM_BASE64 + #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 // input is base64 and converted to binary? convert repl to base64! if ((i % 4) == 3 && i < 24 && fromb64 > i) { @@ -2170,14 +2169,14 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - #ifdef COMBINE + #ifdef CMPLOG_COMBINE if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i + 1); } #endif if ((i >= 7 && (i >= xor&&i >= arith &&i >= tolower &&i >= toupper &&i > tohex &&i > (fromhex + from_0 + from_x + from_slash + 1) - #ifdef CMPLOG_TRANSFORM_BASE64 + #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 && i > tob64 + 3 && i > fromb64 + 4 #endif )) || @@ -2469,7 +2468,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { u32 lvl = (afl->queue_cur->colorized ? 0 : LVL1) + (afl->cmplog_lvl == CMPLOG_LVL_MAX ? LVL3 : 0); -#ifdef COMBINE +#ifdef CMPLOG_COMBINE u8 *cbuf = afl_realloc((void **)&afl->in_scratch_buf, len + 128); memcpy(cbuf, orig_buf, len); u8 *virgin_backup = afl_realloc((void **)&afl->ex_buf, afl->shm.map_size); @@ -2526,7 +2525,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { } else if ((lvl & LVL1) -#ifdef CMPLOG_TRANSFORM +#ifdef CMPLOG_SOLVE_TRANSFORM || (lvl & LVL3) #endif ) { @@ -2583,7 +2582,7 @@ exit_its: } -#ifdef COMBINE +#ifdef CMPLOG_COMBINE if (afl->queued_paths + afl->unique_crashes > orig_hit_cnt + 1) { // copy the current virgin bits so we can recover the information @@ -2622,7 +2621,7 @@ exit_its: dump("COMB", cbuf, len); if (status == 1) { - fprintf(stderr, "NEW COMBINED\n"); + fprintf(stderr, "NEW CMPLOG_COMBINED\n"); } else { @@ -2671,8 +2670,3 @@ exit_its: return r; } - -#ifdef COMBINE - #undef COMBINE -#endif - -- cgit 1.4.1 From 22a3c7f7d043d9dbf39c847061d88a4577537031 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 12 Feb 2021 09:42:22 +0100 Subject: fix #736 (ty b1gr3db) --- qemu_mode/QEMUAFL_VERSION | 2 +- qemu_mode/libqasan/dlmalloc.c | 5553 ++++++++++++++++++++++++----------------- qemu_mode/libqasan/hooks.c | 2 + qemu_mode/libqasan/libqasan.c | 2 +- qemu_mode/libqasan/malloc.c | 109 +- qemu_mode/libqasan/string.c | 2 +- qemu_mode/qemuafl | 2 +- src/afl-fuzz-redqueen.c | 3 +- 8 files changed, 3332 insertions(+), 2343 deletions(-) (limited to 'src') diff --git a/qemu_mode/QEMUAFL_VERSION b/qemu_mode/QEMUAFL_VERSION index d9f0ec33..e73a9588 100644 --- a/qemu_mode/QEMUAFL_VERSION +++ b/qemu_mode/QEMUAFL_VERSION @@ -1 +1 @@ -47722f64e4 +9a258d5b7a diff --git a/qemu_mode/libqasan/dlmalloc.c b/qemu_mode/libqasan/dlmalloc.c index 7e3cb159..bace0ff6 100644 --- a/qemu_mode/libqasan/dlmalloc.c +++ b/qemu_mode/libqasan/dlmalloc.c @@ -207,9 +207,12 @@ mspaces as thread-locals. For example: static __thread mspace tlms = 0; void* tlmalloc(size_t bytes) { + if (tlms == 0) tlms = create_mspace(0, 0); return mspace_malloc(tlms, bytes); + } + void tlfree(void* mem) { mspace_free(tlms, mem); } Unless FOOTERS is defined, each mspace is completely independent. @@ -525,201 +528,203 @@ MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP improvement at the expense of carrying around more memory. */ -#define USE_DL_PREFIX - -/* Version identifier to allow people to support multiple versions */ -#ifndef DLMALLOC_VERSION -#define DLMALLOC_VERSION 20806 -#endif /* DLMALLOC_VERSION */ - -#ifndef DLMALLOC_EXPORT -#define DLMALLOC_EXPORT extern -#endif - -#ifndef WIN32 -#ifdef _WIN32 -#define WIN32 1 -#endif /* _WIN32 */ -#ifdef _WIN32_WCE -#define LACKS_FCNTL_H -#define WIN32 1 -#endif /* _WIN32_WCE */ -#endif /* WIN32 */ -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include -#define HAVE_MMAP 1 -#define HAVE_MORECORE 0 -#define LACKS_UNISTD_H -#define LACKS_SYS_PARAM_H -#define LACKS_SYS_MMAN_H -#define LACKS_STRING_H -#define LACKS_STRINGS_H -#define LACKS_SYS_TYPES_H -#define LACKS_ERRNO_H -#define LACKS_SCHED_H -#ifndef MALLOC_FAILURE_ACTION -#define MALLOC_FAILURE_ACTION -#endif /* MALLOC_FAILURE_ACTION */ -#ifndef MMAP_CLEARS -#ifdef _WIN32_WCE /* WINCE reportedly does not clear */ -#define MMAP_CLEARS 0 -#else -#define MMAP_CLEARS 1 -#endif /* _WIN32_WCE */ -#endif /*MMAP_CLEARS */ -#endif /* WIN32 */ - -#if defined(DARWIN) || defined(_DARWIN) -/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ -#ifndef HAVE_MORECORE -#define HAVE_MORECORE 0 -#define HAVE_MMAP 1 -/* OSX allocators provide 16 byte alignment */ -#ifndef MALLOC_ALIGNMENT -#define MALLOC_ALIGNMENT ((size_t)16U) -#endif -#endif /* HAVE_MORECORE */ -#endif /* DARWIN */ - -#ifndef LACKS_SYS_TYPES_H -#include /* For size_t */ -#endif /* LACKS_SYS_TYPES_H */ - -/* The maximum possible size_t value has all bits set */ -#define MAX_SIZE_T (~(size_t)0) - -#ifndef USE_LOCKS /* ensure true if spin or recursive locks set */ -#define USE_LOCKS ((defined(USE_SPIN_LOCKS) && USE_SPIN_LOCKS != 0) || \ - (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0)) -#endif /* USE_LOCKS */ - -#if USE_LOCKS /* Spin locks for gcc >= 4.1, older gcc on x86, MSC >= 1310 */ -#if ((defined(__GNUC__) && \ - ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \ - defined(__i386__) || defined(__x86_64__))) || \ - (defined(_MSC_VER) && _MSC_VER>=1310)) -#ifndef USE_SPIN_LOCKS -#define USE_SPIN_LOCKS 1 -#endif /* USE_SPIN_LOCKS */ -#elif USE_SPIN_LOCKS -#error "USE_SPIN_LOCKS defined without implementation" -#endif /* ... locks available... */ -#elif !defined(USE_SPIN_LOCKS) -#define USE_SPIN_LOCKS 0 -#endif /* USE_LOCKS */ - -#ifndef ONLY_MSPACES -#define ONLY_MSPACES 0 -#endif /* ONLY_MSPACES */ -#ifndef MSPACES -#if ONLY_MSPACES -#define MSPACES 1 -#else /* ONLY_MSPACES */ -#define MSPACES 0 -#endif /* ONLY_MSPACES */ -#endif /* MSPACES */ -#ifndef MALLOC_ALIGNMENT -#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *))) -#endif /* MALLOC_ALIGNMENT */ -#ifndef FOOTERS -#define FOOTERS 0 -#endif /* FOOTERS */ -#ifndef ABORT -#define ABORT abort() -#endif /* ABORT */ -#ifndef ABORT_ON_ASSERT_FAILURE -#define ABORT_ON_ASSERT_FAILURE 1 -#endif /* ABORT_ON_ASSERT_FAILURE */ -#ifndef PROCEED_ON_ERROR -#define PROCEED_ON_ERROR 0 -#endif /* PROCEED_ON_ERROR */ - -#ifndef INSECURE -#define INSECURE 0 -#endif /* INSECURE */ -#ifndef MALLOC_INSPECT_ALL -#define MALLOC_INSPECT_ALL 0 -#endif /* MALLOC_INSPECT_ALL */ -#ifndef HAVE_MMAP -#define HAVE_MMAP 1 -#endif /* HAVE_MMAP */ -#ifndef MMAP_CLEARS -#define MMAP_CLEARS 1 -#endif /* MMAP_CLEARS */ -#ifndef HAVE_MREMAP -#ifdef linux -#define HAVE_MREMAP 1 -#define _GNU_SOURCE /* Turns on mremap() definition */ -#else /* linux */ -#define HAVE_MREMAP 0 -#endif /* linux */ -#endif /* HAVE_MREMAP */ -#ifndef MALLOC_FAILURE_ACTION -#define MALLOC_FAILURE_ACTION errno = ENOMEM; -#endif /* MALLOC_FAILURE_ACTION */ -#ifndef HAVE_MORECORE -#if ONLY_MSPACES -#define HAVE_MORECORE 0 -#else /* ONLY_MSPACES */ -#define HAVE_MORECORE 1 -#endif /* ONLY_MSPACES */ -#endif /* HAVE_MORECORE */ -#if !HAVE_MORECORE -#define MORECORE_CONTIGUOUS 0 -#else /* !HAVE_MORECORE */ -#define MORECORE_DEFAULT sbrk -#ifndef MORECORE_CONTIGUOUS -#define MORECORE_CONTIGUOUS 1 -#endif /* MORECORE_CONTIGUOUS */ -#endif /* HAVE_MORECORE */ -#ifndef DEFAULT_GRANULARITY -#if (MORECORE_CONTIGUOUS || defined(WIN32)) -#define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ -#else /* MORECORE_CONTIGUOUS */ -#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) -#endif /* MORECORE_CONTIGUOUS */ -#endif /* DEFAULT_GRANULARITY */ -#ifndef DEFAULT_TRIM_THRESHOLD -#ifndef MORECORE_CANNOT_TRIM -#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) -#else /* MORECORE_CANNOT_TRIM */ -#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T -#endif /* MORECORE_CANNOT_TRIM */ -#endif /* DEFAULT_TRIM_THRESHOLD */ -#ifndef DEFAULT_MMAP_THRESHOLD -#if HAVE_MMAP -#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) -#else /* HAVE_MMAP */ -#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T -#endif /* HAVE_MMAP */ -#endif /* DEFAULT_MMAP_THRESHOLD */ -#ifndef MAX_RELEASE_CHECK_RATE -#if HAVE_MMAP -#define MAX_RELEASE_CHECK_RATE 4095 -#else -#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T -#endif /* HAVE_MMAP */ -#endif /* MAX_RELEASE_CHECK_RATE */ -#ifndef USE_BUILTIN_FFS -#define USE_BUILTIN_FFS 0 -#endif /* USE_BUILTIN_FFS */ -#ifndef USE_DEV_RANDOM -#define USE_DEV_RANDOM 0 -#endif /* USE_DEV_RANDOM */ -#ifndef NO_MALLINFO -#define NO_MALLINFO 0 -#endif /* NO_MALLINFO */ -#ifndef MALLINFO_FIELD_TYPE -#define MALLINFO_FIELD_TYPE size_t -#endif /* MALLINFO_FIELD_TYPE */ -#ifndef NO_MALLOC_STATS -#define NO_MALLOC_STATS 0 -#endif /* NO_MALLOC_STATS */ -#ifndef NO_SEGMENT_TRAVERSAL -#define NO_SEGMENT_TRAVERSAL 0 -#endif /* NO_SEGMENT_TRAVERSAL */ + #define USE_DL_PREFIX + + /* Version identifier to allow people to support multiple versions */ + #ifndef DLMALLOC_VERSION + #define DLMALLOC_VERSION 20806 + #endif /* DLMALLOC_VERSION */ + + #ifndef DLMALLOC_EXPORT + #define DLMALLOC_EXPORT extern + #endif + + #ifndef WIN32 + #ifdef _WIN32 + #define WIN32 1 + #endif /* _WIN32 */ + #ifdef _WIN32_WCE + #define LACKS_FCNTL_H + #define WIN32 1 + #endif /* _WIN32_WCE */ + #endif /* WIN32 */ + #ifdef WIN32 + #define WIN32_LEAN_AND_MEAN + #include + #include + #define HAVE_MMAP 1 + #define HAVE_MORECORE 0 + #define LACKS_UNISTD_H + #define LACKS_SYS_PARAM_H + #define LACKS_SYS_MMAN_H + #define LACKS_STRING_H + #define LACKS_STRINGS_H + #define LACKS_SYS_TYPES_H + #define LACKS_ERRNO_H + #define LACKS_SCHED_H + #ifndef MALLOC_FAILURE_ACTION + #define MALLOC_FAILURE_ACTION + #endif /* MALLOC_FAILURE_ACTION */ + #ifndef MMAP_CLEARS + #ifdef _WIN32_WCE /* WINCE reportedly does not clear */ + #define MMAP_CLEARS 0 + #else + #define MMAP_CLEARS 1 + #endif /* _WIN32_WCE */ + #endif /*MMAP_CLEARS */ + #endif /* WIN32 */ + + #if defined(DARWIN) || defined(_DARWIN) + /* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ + #ifndef HAVE_MORECORE + #define HAVE_MORECORE 0 + #define HAVE_MMAP 1 + /* OSX allocators provide 16 byte alignment */ + #ifndef MALLOC_ALIGNMENT + #define MALLOC_ALIGNMENT ((size_t)16U) + #endif + #endif /* HAVE_MORECORE */ + #endif /* DARWIN */ + + #ifndef LACKS_SYS_TYPES_H + #include /* For size_t */ + #endif /* LACKS_SYS_TYPES_H */ + + /* The maximum possible size_t value has all bits set */ + #define MAX_SIZE_T (~(size_t)0) + + #ifndef USE_LOCKS /* ensure true if spin or recursive locks set */ + #define USE_LOCKS \ + ((defined(USE_SPIN_LOCKS) && USE_SPIN_LOCKS != 0) || \ + (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0)) + #endif /* USE_LOCKS */ + + #if USE_LOCKS /* Spin locks for gcc >= 4.1, older gcc on x86, MSC >= 1310 */ + #if ((defined(__GNUC__) && \ + ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \ + defined(__i386__) || defined(__x86_64__))) || \ + (defined(_MSC_VER) && _MSC_VER >= 1310)) + #ifndef USE_SPIN_LOCKS + #define USE_SPIN_LOCKS 1 + #endif /* USE_SPIN_LOCKS */ + #elif USE_SPIN_LOCKS + #error "USE_SPIN_LOCKS defined without implementation" + #endif /* ... locks available... */ + #elif !defined(USE_SPIN_LOCKS) + #define USE_SPIN_LOCKS 0 + #endif /* USE_LOCKS */ + + #ifndef ONLY_MSPACES + #define ONLY_MSPACES 0 + #endif /* ONLY_MSPACES */ + #ifndef MSPACES + #if ONLY_MSPACES + #define MSPACES 1 + #else /* ONLY_MSPACES */ + #define MSPACES 0 + #endif /* ONLY_MSPACES */ + #endif /* MSPACES */ + #ifndef MALLOC_ALIGNMENT + #define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *))) + #endif /* MALLOC_ALIGNMENT */ + #ifndef FOOTERS + #define FOOTERS 0 + #endif /* FOOTERS */ + #ifndef ABORT + #define ABORT abort() + #endif /* ABORT */ + #ifndef ABORT_ON_ASSERT_FAILURE + #define ABORT_ON_ASSERT_FAILURE 1 + #endif /* ABORT_ON_ASSERT_FAILURE */ + #ifndef PROCEED_ON_ERROR + #define PROCEED_ON_ERROR 0 + #endif /* PROCEED_ON_ERROR */ + + #ifndef INSECURE + #define INSECURE 0 + #endif /* INSECURE */ + #ifndef MALLOC_INSPECT_ALL + #define MALLOC_INSPECT_ALL 0 + #endif /* MALLOC_INSPECT_ALL */ + #ifndef HAVE_MMAP + #define HAVE_MMAP 1 + #endif /* HAVE_MMAP */ + #ifndef MMAP_CLEARS + #define MMAP_CLEARS 1 + #endif /* MMAP_CLEARS */ + #ifndef HAVE_MREMAP + #ifdef linux + #define HAVE_MREMAP 1 + #define _GNU_SOURCE /* Turns on mremap() definition */ + #else /* linux */ + #define HAVE_MREMAP 0 + #endif /* linux */ + #endif /* HAVE_MREMAP */ + #ifndef MALLOC_FAILURE_ACTION + #define MALLOC_FAILURE_ACTION errno = ENOMEM; + #endif /* MALLOC_FAILURE_ACTION */ + #ifndef HAVE_MORECORE + #if ONLY_MSPACES + #define HAVE_MORECORE 0 + #else /* ONLY_MSPACES */ + #define HAVE_MORECORE 1 + #endif /* ONLY_MSPACES */ + #endif /* HAVE_MORECORE */ + #if !HAVE_MORECORE + #define MORECORE_CONTIGUOUS 0 + #else /* !HAVE_MORECORE */ + #define MORECORE_DEFAULT sbrk + #ifndef MORECORE_CONTIGUOUS + #define MORECORE_CONTIGUOUS 1 + #endif /* MORECORE_CONTIGUOUS */ + #endif /* HAVE_MORECORE */ + #ifndef DEFAULT_GRANULARITY + #if (MORECORE_CONTIGUOUS || defined(WIN32)) + #define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ + #else /* MORECORE_CONTIGUOUS */ + #define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) + #endif /* MORECORE_CONTIGUOUS */ + #endif /* DEFAULT_GRANULARITY */ + #ifndef DEFAULT_TRIM_THRESHOLD + #ifndef MORECORE_CANNOT_TRIM + #define DEFAULT_TRIM_THRESHOLD \ + ((size_t)2U * (size_t)1024U * (size_t)1024U) + #else /* MORECORE_CANNOT_TRIM */ + #define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T + #endif /* MORECORE_CANNOT_TRIM */ + #endif /* DEFAULT_TRIM_THRESHOLD */ + #ifndef DEFAULT_MMAP_THRESHOLD + #if HAVE_MMAP + #define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) + #else /* HAVE_MMAP */ + #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T + #endif /* HAVE_MMAP */ + #endif /* DEFAULT_MMAP_THRESHOLD */ + #ifndef MAX_RELEASE_CHECK_RATE + #if HAVE_MMAP + #define MAX_RELEASE_CHECK_RATE 4095 + #else + #define MAX_RELEASE_CHECK_RATE MAX_SIZE_T + #endif /* HAVE_MMAP */ + #endif /* MAX_RELEASE_CHECK_RATE */ + #ifndef USE_BUILTIN_FFS + #define USE_BUILTIN_FFS 0 + #endif /* USE_BUILTIN_FFS */ + #ifndef USE_DEV_RANDOM + #define USE_DEV_RANDOM 0 + #endif /* USE_DEV_RANDOM */ + #ifndef NO_MALLINFO + #define NO_MALLINFO 0 + #endif /* NO_MALLINFO */ + #ifndef MALLINFO_FIELD_TYPE + #define MALLINFO_FIELD_TYPE size_t + #endif /* MALLINFO_FIELD_TYPE */ + #ifndef NO_MALLOC_STATS + #define NO_MALLOC_STATS 0 + #endif /* NO_MALLOC_STATS */ + #ifndef NO_SEGMENT_TRAVERSAL + #define NO_SEGMENT_TRAVERSAL 0 + #endif /* NO_SEGMENT_TRAVERSAL */ /* mallopt tuning options. SVID/XPG defines four standard parameter @@ -728,123 +733,128 @@ MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP malloc does support the following options. */ -#undef M_TRIM_THRESHOLD -#undef M_GRANULARITY -#undef M_MMAP_THRESHOLD -#define M_TRIM_THRESHOLD (-1) -#define M_GRANULARITY (-2) -#define M_MMAP_THRESHOLD (-3) + #undef M_TRIM_THRESHOLD + #undef M_GRANULARITY + #undef M_MMAP_THRESHOLD + #define M_TRIM_THRESHOLD (-1) + #define M_GRANULARITY (-2) + #define M_MMAP_THRESHOLD (-3) /* ------------------------ Mallinfo declarations ------------------------ */ -#if !NO_MALLINFO -/* - This version of malloc supports the standard SVID/XPG mallinfo - routine that returns a struct containing usage properties and - statistics. It should work on any system that has a - /usr/include/malloc.h defining struct mallinfo. The main - declaration needed is the mallinfo struct that is returned (by-copy) - by mallinfo(). The malloinfo struct contains a bunch of fields that - are not even meaningful in this version of malloc. These fields are - are instead filled by mallinfo() with other numbers that might be of - interest. - - HAVE_USR_INCLUDE_MALLOC_H should be set if you have a - /usr/include/malloc.h file that includes a declaration of struct - mallinfo. If so, it is included; else a compliant version is - declared below. These must be precisely the same for mallinfo() to - work. The original SVID version of this struct, defined on most - systems with mallinfo, declares all fields as ints. But some others - define as unsigned long. If your system defines the fields using a - type of different width than listed here, you MUST #include your - system version and #define HAVE_USR_INCLUDE_MALLOC_H. -*/ + #if !NO_MALLINFO + /* + This version of malloc supports the standard SVID/XPG mallinfo + routine that returns a struct containing usage properties and + statistics. It should work on any system that has a + /usr/include/malloc.h defining struct mallinfo. The main + declaration needed is the mallinfo struct that is returned (by-copy) + by mallinfo(). The malloinfo struct contains a bunch of fields that + are not even meaningful in this version of malloc. These fields are + are instead filled by mallinfo() with other numbers that might be of + interest. + + HAVE_USR_INCLUDE_MALLOC_H should be set if you have a + /usr/include/malloc.h file that includes a declaration of struct + mallinfo. If so, it is included; else a compliant version is + declared below. These must be precisely the same for mallinfo() to + work. The original SVID version of this struct, defined on most + systems with mallinfo, declares all fields as ints. But some others + define as unsigned long. If your system defines the fields using a + type of different width than listed here, you MUST #include your + system version and #define HAVE_USR_INCLUDE_MALLOC_H. + */ -/* #define HAVE_USR_INCLUDE_MALLOC_H */ + /* #define HAVE_USR_INCLUDE_MALLOC_H */ -#ifdef HAVE_USR_INCLUDE_MALLOC_H -#include "/usr/include/malloc.h" -#else /* HAVE_USR_INCLUDE_MALLOC_H */ -#ifndef STRUCT_MALLINFO_DECLARED -/* HP-UX (and others?) redefines mallinfo unless _STRUCT_MALLINFO is defined */ -#define _STRUCT_MALLINFO -#define STRUCT_MALLINFO_DECLARED 1 + #ifdef HAVE_USR_INCLUDE_MALLOC_H + #include "/usr/include/malloc.h" + #else /* HAVE_USR_INCLUDE_MALLOC_H */ + #ifndef STRUCT_MALLINFO_DECLARED + /* HP-UX (and others?) redefines mallinfo unless _STRUCT_MALLINFO is + * defined */ + #define _STRUCT_MALLINFO + #define STRUCT_MALLINFO_DECLARED 1 struct mallinfo { - MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ - MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ - MALLINFO_FIELD_TYPE smblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ - MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ - MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ - MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ - MALLINFO_FIELD_TYPE fordblks; /* total free space */ - MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ + + MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ + MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ + MALLINFO_FIELD_TYPE smblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ + MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ + MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ + MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ + MALLINFO_FIELD_TYPE fordblks; /* total free space */ + MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ + }; -#endif /* STRUCT_MALLINFO_DECLARED */ -#endif /* HAVE_USR_INCLUDE_MALLOC_H */ -#endif /* NO_MALLINFO */ + + #endif /* STRUCT_MALLINFO_DECLARED */ + #endif /* HAVE_USR_INCLUDE_MALLOC_H */ + #endif /* NO_MALLINFO */ /* Try to persuade compilers to inline. The most critical functions for inlining are defined as macros, so these aren't used for them. */ -#ifndef FORCEINLINE - #if defined(__GNUC__) -#define FORCEINLINE __inline __attribute__ ((always_inline)) - #elif defined(_MSC_VER) - #define FORCEINLINE __forceinline + #ifndef FORCEINLINE + #if defined(__GNUC__) + #define FORCEINLINE __inline __attribute__((always_inline)) + #elif defined(_MSC_VER) + #define FORCEINLINE __forceinline + #endif #endif -#endif -#ifndef NOINLINE - #if defined(__GNUC__) - #define NOINLINE __attribute__ ((noinline)) - #elif defined(_MSC_VER) - #define NOINLINE __declspec(noinline) - #else - #define NOINLINE + #ifndef NOINLINE + #if defined(__GNUC__) + #define NOINLINE __attribute__((noinline)) + #elif defined(_MSC_VER) + #define NOINLINE __declspec(noinline) + #else + #define NOINLINE + #endif #endif -#endif -#ifdef __cplusplus + #ifdef __cplusplus extern "C" { -#ifndef FORCEINLINE - #define FORCEINLINE inline -#endif -#endif /* __cplusplus */ -#ifndef FORCEINLINE - #define FORCEINLINE -#endif - -#if !ONLY_MSPACES - -/* ------------------- Declarations of public routines ------------------- */ - -#ifndef USE_DL_PREFIX -#define dlcalloc calloc -#define dlfree free -#define dlmalloc malloc -#define dlmemalign memalign -#define dlposix_memalign posix_memalign -#define dlrealloc realloc -#define dlrealloc_in_place realloc_in_place -#define dlvalloc valloc -#define dlpvalloc pvalloc -#define dlmallinfo mallinfo -#define dlmallopt mallopt -#define dlmalloc_trim malloc_trim -#define dlmalloc_stats malloc_stats -#define dlmalloc_usable_size malloc_usable_size -#define dlmalloc_footprint malloc_footprint -#define dlmalloc_max_footprint malloc_max_footprint -#define dlmalloc_footprint_limit malloc_footprint_limit -#define dlmalloc_set_footprint_limit malloc_set_footprint_limit -#define dlmalloc_inspect_all malloc_inspect_all -#define dlindependent_calloc independent_calloc -#define dlindependent_comalloc independent_comalloc -#define dlbulk_free bulk_free -#endif /* USE_DL_PREFIX */ + + #ifndef FORCEINLINE + #define FORCEINLINE inline + #endif + #endif /* __cplusplus */ + #ifndef FORCEINLINE + #define FORCEINLINE + #endif + + #if !ONLY_MSPACES + + /* ------------------- Declarations of public routines ------------------- */ + + #ifndef USE_DL_PREFIX + #define dlcalloc calloc + #define dlfree free + #define dlmalloc malloc + #define dlmemalign memalign + #define dlposix_memalign posix_memalign + #define dlrealloc realloc + #define dlrealloc_in_place realloc_in_place + #define dlvalloc valloc + #define dlpvalloc pvalloc + #define dlmallinfo mallinfo + #define dlmallopt mallopt + #define dlmalloc_trim malloc_trim + #define dlmalloc_stats malloc_stats + #define dlmalloc_usable_size malloc_usable_size + #define dlmalloc_footprint malloc_footprint + #define dlmalloc_max_footprint malloc_max_footprint + #define dlmalloc_footprint_limit malloc_footprint_limit + #define dlmalloc_set_footprint_limit malloc_set_footprint_limit + #define dlmalloc_inspect_all malloc_inspect_all + #define dlindependent_calloc independent_calloc + #define dlindependent_comalloc independent_comalloc + #define dlbulk_free bulk_free + #endif /* USE_DL_PREFIX */ /* malloc(size_t n) @@ -860,7 +870,7 @@ extern "C" { maximum supported value of n differs across systems, but is in all cases less than the maximum representable value of a size_t. */ -DLMALLOC_EXPORT void* dlmalloc(size_t); +DLMALLOC_EXPORT void *dlmalloc(size_t); /* free(void* p) @@ -869,14 +879,14 @@ DLMALLOC_EXPORT void* dlmalloc(size_t); It has no effect if p is null. If p was not malloced or already freed, free(p) will by default cause the current program to abort. */ -DLMALLOC_EXPORT void dlfree(void*); +DLMALLOC_EXPORT void dlfree(void *); /* calloc(size_t n_elements, size_t element_size); Returns a pointer to n_elements * element_size bytes, with all locations set to zero. */ -DLMALLOC_EXPORT void* dlcalloc(size_t, size_t); +DLMALLOC_EXPORT void *dlcalloc(size_t, size_t); /* realloc(void* p, size_t n) @@ -900,7 +910,7 @@ DLMALLOC_EXPORT void* dlcalloc(size_t, size_t); The old unix realloc convention of allowing the last-free'd chunk to be used as an argument to realloc is not supported. */ -DLMALLOC_EXPORT void* dlrealloc(void*, size_t); +DLMALLOC_EXPORT void *dlrealloc(void *, size_t); /* realloc_in_place(void* p, size_t n) @@ -915,7 +925,7 @@ DLMALLOC_EXPORT void* dlrealloc(void*, size_t); Returns p if successful; otherwise null. */ -DLMALLOC_EXPORT void* dlrealloc_in_place(void*, size_t); +DLMALLOC_EXPORT void *dlrealloc_in_place(void *, size_t); /* memalign(size_t alignment, size_t n); @@ -929,7 +939,7 @@ DLMALLOC_EXPORT void* dlrealloc_in_place(void*, size_t); Overreliance on memalign is a sure way to fragment space. */ -DLMALLOC_EXPORT void* dlmemalign(size_t, size_t); +DLMALLOC_EXPORT void *dlmemalign(size_t, size_t); /* int posix_memalign(void** pp, size_t alignment, size_t n); @@ -939,14 +949,14 @@ DLMALLOC_EXPORT void* dlmemalign(size_t, size_t); returns EINVAL if the alignment is not a power of two (3) fails and returns ENOMEM if memory cannot be allocated. */ -DLMALLOC_EXPORT int dlposix_memalign(void**, size_t, size_t); +DLMALLOC_EXPORT int dlposix_memalign(void **, size_t, size_t); /* valloc(size_t n); Equivalent to memalign(pagesize, n), where pagesize is the page size of the system. If the pagesize is unknown, 4096 is used. */ -DLMALLOC_EXPORT void* dlvalloc(size_t); +DLMALLOC_EXPORT void *dlvalloc(size_t); /* mallopt(int parameter_number, int parameter_value) @@ -1021,7 +1031,7 @@ DLMALLOC_EXPORT size_t dlmalloc_footprint_limit(); */ DLMALLOC_EXPORT size_t dlmalloc_set_footprint_limit(size_t bytes); -#if MALLOC_INSPECT_ALL + #if MALLOC_INSPECT_ALL /* malloc_inspect_all(void(*handler)(void *start, void *end, @@ -1043,19 +1053,23 @@ DLMALLOC_EXPORT size_t dlmalloc_set_footprint_limit(size_t bytes); than 1000, you could write: static int count = 0; void count_chunks(void* start, void* end, size_t used, void* arg) { + if (used >= 1000) ++count; + } + then: malloc_inspect_all(count_chunks, NULL); malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined. */ -DLMALLOC_EXPORT void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), - void* arg); +DLMALLOC_EXPORT void dlmalloc_inspect_all(void (*handler)(void *, void *, + size_t, void *), + void *arg); -#endif /* MALLOC_INSPECT_ALL */ + #endif /* MALLOC_INSPECT_ALL */ -#if !NO_MALLINFO + #if !NO_MALLINFO /* mallinfo() Returns (by copy) a struct containing various summary statistics: @@ -1079,7 +1093,7 @@ DLMALLOC_EXPORT void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, thus be inaccurate. */ DLMALLOC_EXPORT struct mallinfo dlmallinfo(void); -#endif /* NO_MALLINFO */ + #endif /* NO_MALLINFO */ /* independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); @@ -1117,6 +1131,7 @@ DLMALLOC_EXPORT struct mallinfo dlmallinfo(void); struct Node { int item; struct Node* next; }; struct Node* build_list() { + struct Node** pool; int n = read_number_of_nodes_needed(); if (n <= 0) return 0; @@ -1128,9 +1143,11 @@ DLMALLOC_EXPORT struct mallinfo dlmallinfo(void); pool[i]->next = pool[i+1]; free(pool); // Can now free the array (or not, if it is needed later) return first; + } + */ -DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**); +DLMALLOC_EXPORT void **dlindependent_calloc(size_t, size_t, void **); /* independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); @@ -1169,6 +1186,7 @@ DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**); struct Foot { ... } void send_message(char* msg) { + int msglen = strlen(msg); size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; void* chunks[3]; @@ -1178,6 +1196,7 @@ DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**); char* body = (char*)(chunks[1]); struct Foot* foot = (struct Foot*)(chunks[2]); // ... + } In general though, independent_comalloc is worth using only for @@ -1188,7 +1207,7 @@ DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**); since it cannot reuse existing noncontiguous small chunks that might be available for some of the elements. */ -DLMALLOC_EXPORT void** dlindependent_comalloc(size_t, size_t*, void**); +DLMALLOC_EXPORT void **dlindependent_comalloc(size_t, size_t *, void **); /* bulk_free(void* array[], size_t n_elements) @@ -1199,14 +1218,14 @@ DLMALLOC_EXPORT void** dlindependent_comalloc(size_t, size_t*, void**); is returned. For large arrays of pointers with poor locality, it may be worthwhile to sort this array before calling bulk_free. */ -DLMALLOC_EXPORT size_t dlbulk_free(void**, size_t n_elements); +DLMALLOC_EXPORT size_t dlbulk_free(void **, size_t n_elements); /* pvalloc(size_t n); Equivalent to valloc(minimum-page-that-holds(n)), that is, round up n to nearest pagesize. */ -DLMALLOC_EXPORT void* dlpvalloc(size_t); +DLMALLOC_EXPORT void *dlpvalloc(size_t); /* malloc_trim(size_t pad); @@ -1229,7 +1248,7 @@ DLMALLOC_EXPORT void* dlpvalloc(size_t); Malloc_trim returns 1 if it actually released any memory, else 0. */ -DLMALLOC_EXPORT int dlmalloc_trim(size_t); +DLMALLOC_EXPORT int dlmalloc_trim(size_t); /* malloc_stats(); @@ -1250,7 +1269,7 @@ DLMALLOC_EXPORT int dlmalloc_trim(size_t); malloc_stats prints only the most commonly interesting statistics. More information can be obtained by calling mallinfo. */ -DLMALLOC_EXPORT void dlmalloc_stats(void); +DLMALLOC_EXPORT void dlmalloc_stats(void); /* malloc_usable_size(void* p); @@ -1266,17 +1285,17 @@ DLMALLOC_EXPORT void dlmalloc_stats(void); p = malloc(n); assert(malloc_usable_size(p) >= 256); */ -size_t dlmalloc_usable_size(void*); +size_t dlmalloc_usable_size(void *); -#endif /* ONLY_MSPACES */ + #endif /* ONLY_MSPACES */ -#if MSPACES + #if MSPACES /* mspace is an opaque type representing an independent region of space that supports mspace_malloc, etc. */ -typedef void* mspace; +typedef void *mspace; /* create_mspace creates and returns a new independent space with the @@ -1308,7 +1327,8 @@ DLMALLOC_EXPORT size_t destroy_mspace(mspace msp); Destroying this space will deallocate all additionally allocated space (if possible) but not the initial base. */ -DLMALLOC_EXPORT mspace create_mspace_with_base(void* base, size_t capacity, int locked); +DLMALLOC_EXPORT mspace create_mspace_with_base(void *base, size_t capacity, + int locked); /* mspace_track_large_chunks controls whether requests for large chunks @@ -1323,12 +1343,11 @@ DLMALLOC_EXPORT mspace create_mspace_with_base(void* base, size_t capacity, int */ DLMALLOC_EXPORT int mspace_track_large_chunks(mspace msp, int enable); - /* mspace_malloc behaves as malloc, but operates within the given space. */ -DLMALLOC_EXPORT void* mspace_malloc(mspace msp, size_t bytes); +DLMALLOC_EXPORT void *mspace_malloc(mspace msp, size_t bytes); /* mspace_free behaves as free, but operates within @@ -1338,7 +1357,7 @@ DLMALLOC_EXPORT void* mspace_malloc(mspace msp, size_t bytes); free may be called instead of mspace_free because freed chunks from any space are handled by their originating spaces. */ -DLMALLOC_EXPORT void mspace_free(mspace msp, void* mem); +DLMALLOC_EXPORT void mspace_free(mspace msp, void *mem); /* mspace_realloc behaves as realloc, but operates within @@ -1349,33 +1368,38 @@ DLMALLOC_EXPORT void mspace_free(mspace msp, void* mem); realloced chunks from any space are handled by their originating spaces. */ -DLMALLOC_EXPORT void* mspace_realloc(mspace msp, void* mem, size_t newsize); +DLMALLOC_EXPORT void *mspace_realloc(mspace msp, void *mem, size_t newsize); /* mspace_calloc behaves as calloc, but operates within the given space. */ -DLMALLOC_EXPORT void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); +DLMALLOC_EXPORT void *mspace_calloc(mspace msp, size_t n_elements, + size_t elem_size); /* mspace_memalign behaves as memalign, but operates within the given space. */ -DLMALLOC_EXPORT void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); +DLMALLOC_EXPORT void *mspace_memalign(mspace msp, size_t alignment, + size_t bytes); /* mspace_independent_calloc behaves as independent_calloc, but operates within the given space. */ -DLMALLOC_EXPORT void** mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]); +DLMALLOC_EXPORT void **mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, + void * chunks[]); /* mspace_independent_comalloc behaves as independent_comalloc, but operates within the given space. */ -DLMALLOC_EXPORT void** mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]); +DLMALLOC_EXPORT void **mspace_independent_comalloc(mspace msp, + size_t n_elements, + size_t sizes[], + void * chunks[]); /* mspace_footprint() returns the number of bytes obtained from the @@ -1389,19 +1413,18 @@ DLMALLOC_EXPORT size_t mspace_footprint(mspace msp); */ DLMALLOC_EXPORT size_t mspace_max_footprint(mspace msp); - -#if !NO_MALLINFO + #if !NO_MALLINFO /* mspace_mallinfo behaves as mallinfo, but reports properties of the given space. */ DLMALLOC_EXPORT struct mallinfo mspace_mallinfo(mspace msp); -#endif /* NO_MALLINFO */ + #endif /* NO_MALLINFO */ /* malloc_usable_size(void* p) behaves the same as malloc_usable_size; */ -DLMALLOC_EXPORT size_t mspace_usable_size(const void* mem); +DLMALLOC_EXPORT size_t mspace_usable_size(const void *mem); /* mspace_malloc_stats behaves as malloc_stats, but reports @@ -1420,11 +1443,13 @@ DLMALLOC_EXPORT int mspace_trim(mspace msp, size_t pad); */ DLMALLOC_EXPORT int mspace_mallopt(int, int); -#endif /* MSPACES */ + #endif /* MSPACES */ -#ifdef __cplusplus -} /* end of extern "C" */ -#endif /* __cplusplus */ + #ifdef __cplusplus + +} /* end of extern "C" */ + + #endif /* __cplusplus */ /* ======================================================================== @@ -1438,392 +1463,413 @@ DLMALLOC_EXPORT int mspace_mallopt(int, int); /*------------------------------ internal #includes ---------------------- */ -#ifdef _MSC_VER -#pragma warning( disable : 4146 ) /* no "unsigned" warnings */ -#endif /* _MSC_VER */ -#if !NO_MALLOC_STATS -#include /* for printing in malloc_stats */ -#endif /* NO_MALLOC_STATS */ -#ifndef LACKS_ERRNO_H -#include /* for MALLOC_FAILURE_ACTION */ -#endif /* LACKS_ERRNO_H */ -#ifdef DEBUG -#if ABORT_ON_ASSERT_FAILURE -#undef assert -#define assert(x) if(!(x)) ABORT -#else /* ABORT_ON_ASSERT_FAILURE */ -#include -#endif /* ABORT_ON_ASSERT_FAILURE */ -#else /* DEBUG */ -#ifndef assert -#define assert(x) -#endif -#define DEBUG 0 -#endif /* DEBUG */ -#if !defined(WIN32) && !defined(LACKS_TIME_H) -#include /* for magic initialization */ -#endif /* WIN32 */ -#ifndef LACKS_STDLIB_H -#include /* for abort() */ -#endif /* LACKS_STDLIB_H */ -#ifndef LACKS_STRING_H -#include /* for memset etc */ -#endif /* LACKS_STRING_H */ -#if USE_BUILTIN_FFS -#ifndef LACKS_STRINGS_H -#include /* for ffs */ -#endif /* LACKS_STRINGS_H */ -#endif /* USE_BUILTIN_FFS */ -#if HAVE_MMAP -#ifndef LACKS_SYS_MMAN_H -/* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ -#if (defined(linux) && !defined(__USE_GNU)) -#define __USE_GNU 1 -#include /* for mmap */ -#undef __USE_GNU -#else -#include /* for mmap */ -#endif /* linux */ -#endif /* LACKS_SYS_MMAN_H */ -#ifndef LACKS_FCNTL_H -#include -#endif /* LACKS_FCNTL_H */ -#endif /* HAVE_MMAP */ -#ifndef LACKS_UNISTD_H -#include /* for sbrk, sysconf */ -#else /* LACKS_UNISTD_H */ -#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) -extern void* sbrk(ptrdiff_t); -#endif /* FreeBSD etc */ -#endif /* LACKS_UNISTD_H */ - -/* Declarations for locking */ -#if USE_LOCKS -#ifndef WIN32 -#if defined (__SVR4) && defined (__sun) /* solaris */ -#include -#elif !defined(LACKS_SCHED_H) -#include -#endif /* solaris or LACKS_SCHED_H */ -#if (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0) || !USE_SPIN_LOCKS -#include -#endif /* USE_RECURSIVE_LOCKS ... */ -#elif defined(_MSC_VER) -#ifndef _M_AMD64 -/* These are already defined on AMD64 builds */ -#ifdef __cplusplus + #ifdef _MSC_VER + #pragma warning(disable : 4146) /* no "unsigned" warnings */ + #endif /* _MSC_VER */ + #if !NO_MALLOC_STATS + #include /* for printing in malloc_stats */ + #endif /* NO_MALLOC_STATS */ + #ifndef LACKS_ERRNO_H + #include /* for MALLOC_FAILURE_ACTION */ + #endif /* LACKS_ERRNO_H */ + #ifdef DEBUG + #if ABORT_ON_ASSERT_FAILURE + #undef assert + #define assert(x) \ + if (!(x)) ABORT + #else /* ABORT_ON_ASSERT_FAILURE */ + #include + #endif /* ABORT_ON_ASSERT_FAILURE */ + #else /* DEBUG */ + #ifndef assert + #define assert(x) + #endif + #define DEBUG 0 + #endif /* DEBUG */ + #if !defined(WIN32) && !defined(LACKS_TIME_H) + #include /* for magic initialization */ + #endif /* WIN32 */ + #ifndef LACKS_STDLIB_H + #include /* for abort() */ + #endif /* LACKS_STDLIB_H */ + #ifndef LACKS_STRING_H + #include /* for memset etc */ + #endif /* LACKS_STRING_H */ + #if USE_BUILTIN_FFS + #ifndef LACKS_STRINGS_H + #include /* for ffs */ + #endif /* LACKS_STRINGS_H */ + #endif /* USE_BUILTIN_FFS */ + #if HAVE_MMAP + #ifndef LACKS_SYS_MMAN_H + /* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ + #if (defined(linux) && !defined(__USE_GNU)) + #define __USE_GNU 1 + #include /* for mmap */ + #undef __USE_GNU + #else + #include /* for mmap */ + #endif /* linux */ + #endif /* LACKS_SYS_MMAN_H */ + #ifndef LACKS_FCNTL_H + #include + #endif /* LACKS_FCNTL_H */ + #endif /* HAVE_MMAP */ + #ifndef LACKS_UNISTD_H + #include /* for sbrk, sysconf */ + #else /* LACKS_UNISTD_H */ + #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +extern void *sbrk(ptrdiff_t); + #endif /* FreeBSD etc */ + #endif /* LACKS_UNISTD_H */ + + /* Declarations for locking */ + #if USE_LOCKS + #ifndef WIN32 + #if defined(__SVR4) && defined(__sun) /* solaris */ + #include + #elif !defined(LACKS_SCHED_H) + #include + #endif /* solaris or LACKS_SCHED_H */ + #if (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0) || \ + !USE_SPIN_LOCKS + #include + #endif /* USE_RECURSIVE_LOCKS ... */ + #elif defined(_MSC_VER) + #ifndef _M_AMD64 + /* These are already defined on AMD64 builds */ + #ifdef __cplusplus extern "C" { -#endif /* __cplusplus */ -LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp); + + #endif /* __cplusplus */ +LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, + LONG Comp); LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value); -#ifdef __cplusplus + #ifdef __cplusplus + } -#endif /* __cplusplus */ -#endif /* _M_AMD64 */ -#pragma intrinsic (_InterlockedCompareExchange) -#pragma intrinsic (_InterlockedExchange) -#define interlockedcompareexchange _InterlockedCompareExchange -#define interlockedexchange _InterlockedExchange -#elif defined(WIN32) && defined(__GNUC__) -#define interlockedcompareexchange(a, b, c) __sync_val_compare_and_swap(a, c, b) -#define interlockedexchange __sync_lock_test_and_set -#endif /* Win32 */ -#else /* USE_LOCKS */ -#endif /* USE_LOCKS */ - -#ifndef LOCK_AT_FORK -#define LOCK_AT_FORK 0 -#endif - -/* Declarations for bit scanning on win32 */ -#if defined(_MSC_VER) && _MSC_VER>=1300 -#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ -#ifdef __cplusplus + + #endif /* __cplusplus */ + #endif /* _M_AMD64 */ + #pragma intrinsic(_InterlockedCompareExchange) + #pragma intrinsic(_InterlockedExchange) + #define interlockedcompareexchange _InterlockedCompareExchange + #define interlockedexchange _InterlockedExchange + #elif defined(WIN32) && defined(__GNUC__) + #define interlockedcompareexchange(a, b, c) \ + __sync_val_compare_and_swap(a, c, b) + #define interlockedexchange __sync_lock_test_and_set + #endif /* Win32 */ + #else /* USE_LOCKS */ + #endif /* USE_LOCKS */ + + #ifndef LOCK_AT_FORK + #define LOCK_AT_FORK 0 + #endif + + /* Declarations for bit scanning on win32 */ + #if defined(_MSC_VER) && _MSC_VER >= 1300 + #ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ + #ifdef __cplusplus extern "C" { -#endif /* __cplusplus */ + + #endif /* __cplusplus */ unsigned char _BitScanForward(unsigned long *index, unsigned long mask); unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); -#ifdef __cplusplus + #ifdef __cplusplus + } -#endif /* __cplusplus */ - -#define BitScanForward _BitScanForward -#define BitScanReverse _BitScanReverse -#pragma intrinsic(_BitScanForward) -#pragma intrinsic(_BitScanReverse) -#endif /* BitScanForward */ -#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ - -#ifndef WIN32 -#ifndef malloc_getpagesize -# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ -# ifndef _SC_PAGE_SIZE -# define _SC_PAGE_SIZE _SC_PAGESIZE -# endif -# endif -# ifdef _SC_PAGE_SIZE -# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) -# else -# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) - extern size_t getpagesize(); -# define malloc_getpagesize getpagesize() -# else -# ifdef WIN32 /* use supplied emulation of getpagesize */ -# define malloc_getpagesize getpagesize() -# else -# ifndef LACKS_SYS_PARAM_H -# include -# endif -# ifdef EXEC_PAGESIZE -# define malloc_getpagesize EXEC_PAGESIZE -# else -# ifdef NBPG -# ifndef CLSIZE -# define malloc_getpagesize NBPG -# else -# define malloc_getpagesize (NBPG * CLSIZE) -# endif -# else -# ifdef NBPC -# define malloc_getpagesize NBPC -# else -# ifdef PAGESIZE -# define malloc_getpagesize PAGESIZE -# else /* just guess */ -# define malloc_getpagesize ((size_t)4096U) -# endif -# endif -# endif -# endif -# endif -# endif -# endif -#endif -#endif - -/* ------------------- size_t and alignment properties -------------------- */ - -/* The byte and bit size of a size_t */ -#define SIZE_T_SIZE (sizeof(size_t)) -#define SIZE_T_BITSIZE (sizeof(size_t) << 3) - -/* Some constants coerced to size_t */ -/* Annoying but necessary to avoid errors on some platforms */ -#define SIZE_T_ZERO ((size_t)0) -#define SIZE_T_ONE ((size_t)1) -#define SIZE_T_TWO ((size_t)2) -#define SIZE_T_FOUR ((size_t)4) -#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) -#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) -#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) -#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) - -/* The bit mask value corresponding to MALLOC_ALIGNMENT */ -#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) - -/* True if address a has acceptable alignment */ -#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) - -/* the number of bytes to offset an address to align it */ -#define align_offset(A)\ - ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ - ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) - -/* -------------------------- MMAP preliminaries ------------------------- */ -/* - If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and - checks to fail so compiler optimizer can delete code rather than - using so many "#if"s. -*/ + #endif /* __cplusplus */ + + #define BitScanForward _BitScanForward + #define BitScanReverse _BitScanReverse + #pragma intrinsic(_BitScanForward) + #pragma intrinsic(_BitScanReverse) + #endif /* BitScanForward */ + #endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ + + #ifndef WIN32 + #ifndef malloc_getpagesize + #ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ + #ifndef _SC_PAGE_SIZE + #define _SC_PAGE_SIZE _SC_PAGESIZE + #endif + #endif + #ifdef _SC_PAGE_SIZE + #define malloc_getpagesize sysconf(_SC_PAGE_SIZE) + #else + #if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) +extern size_t getpagesize(); + #define malloc_getpagesize getpagesize() + #else + #ifdef WIN32 /* use supplied emulation of getpagesize */ + #define malloc_getpagesize getpagesize() + #else + #ifndef LACKS_SYS_PARAM_H + #include + #endif + #ifdef EXEC_PAGESIZE + #define malloc_getpagesize EXEC_PAGESIZE + #else + #ifdef NBPG + #ifndef CLSIZE + #define malloc_getpagesize NBPG + #else + #define malloc_getpagesize (NBPG * CLSIZE) + #endif + #else + #ifdef NBPC + #define malloc_getpagesize NBPC + #else + #ifdef PAGESIZE + #define malloc_getpagesize PAGESIZE + #else /* just guess */ + #define malloc_getpagesize ((size_t)4096U) + #endif + #endif + #endif + #endif + #endif + #endif + #endif + #endif + #endif + + /* ------------------- size_t and alignment properties -------------------- */ + + /* The byte and bit size of a size_t */ + #define SIZE_T_SIZE (sizeof(size_t)) + #define SIZE_T_BITSIZE (sizeof(size_t) << 3) + + /* Some constants coerced to size_t */ + /* Annoying but necessary to avoid errors on some platforms */ + #define SIZE_T_ZERO ((size_t)0) + #define SIZE_T_ONE ((size_t)1) + #define SIZE_T_TWO ((size_t)2) + #define SIZE_T_FOUR ((size_t)4) + #define TWO_SIZE_T_SIZES (SIZE_T_SIZE << 1) + #define FOUR_SIZE_T_SIZES (SIZE_T_SIZE << 2) + #define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES + TWO_SIZE_T_SIZES) + #define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) + + /* The bit mask value corresponding to MALLOC_ALIGNMENT */ + #define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) + + /* True if address a has acceptable alignment */ + #define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) + + /* the number of bytes to offset an address to align it */ + #define align_offset(A) \ + ((((size_t)(A)&CHUNK_ALIGN_MASK) == 0) \ + ? 0 \ + : ((MALLOC_ALIGNMENT - ((size_t)(A)&CHUNK_ALIGN_MASK)) & \ + CHUNK_ALIGN_MASK)) + + /* -------------------------- MMAP preliminaries ------------------------- */ + + /* + If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and + checks to fail so compiler optimizer can delete code rather than + using so many "#if"s. + */ + /* MORECORE and MMAP must return MFAIL on failure */ + #define MFAIL ((void *)(MAX_SIZE_T)) + #define CMFAIL ((char *)(MFAIL)) /* defined for convenience */ -/* MORECORE and MMAP must return MFAIL on failure */ -#define MFAIL ((void*)(MAX_SIZE_T)) -#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ + #if HAVE_MMAP -#if HAVE_MMAP + #ifndef WIN32 + #define MMAP_PROT (PROT_READ | PROT_WRITE) + #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) + #define MAP_ANONYMOUS MAP_ANON + #endif /* MAP_ANON */ + #ifdef MAP_ANONYMOUS -#ifndef WIN32 -#define MMAP_PROT (PROT_READ|PROT_WRITE) -#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) -#define MAP_ANONYMOUS MAP_ANON -#endif /* MAP_ANON */ -#ifdef MAP_ANONYMOUS + #define MMAP_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS) -#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) +static FORCEINLINE void *unixmmap(size_t size) { -static FORCEINLINE void* unixmmap(size_t size) { - void* result; + void *result; result = mmap(0, size, MMAP_PROT, MMAP_FLAGS, -1, 0); - if (result == MFAIL) - return MFAIL; + if (result == MFAIL) return MFAIL; return result; + } -static FORCEINLINE int unixmunmap(void* ptr, size_t size) { +static FORCEINLINE int unixmunmap(void *ptr, size_t size) { + int result; result = munmap(ptr, size); - if (result != 0) - return result; + if (result != 0) return result; return result; + } -#define MMAP_DEFAULT(s) unixmmap(s) -#define MUNMAP_DEFAULT(a, s) unixmunmap((a), (s)) + #define MMAP_DEFAULT(s) unixmmap(s) + #define MUNMAP_DEFAULT(a, s) unixmunmap((a), (s)) -#else /* MAP_ANONYMOUS */ -/* - Nearly all versions of mmap support MAP_ANONYMOUS, so the following - is unlikely to be needed, but is supplied just in case. -*/ -#define MMAP_FLAGS (MAP_PRIVATE) -static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ -#define MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \ - (dev_zero_fd = open("/dev/zero", O_RDWR), \ - mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ - mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) -#define MUNMAP_DEFAULT(a, s) munmap((a), (s)) -#endif /* MAP_ANONYMOUS */ + #else /* MAP_ANONYMOUS */ + /* + Nearly all versions of mmap support MAP_ANONYMOUS, so the following + is unlikely to be needed, but is supplied just in case. + */ + #define MMAP_FLAGS (MAP_PRIVATE) +static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ + #define MMAP_DEFAULT(s) \ + ((dev_zero_fd < 0) \ + ? (dev_zero_fd = open("/dev/zero", O_RDWR), \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) \ + : mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) + #define MUNMAP_DEFAULT(a, s) munmap((a), (s)) + #endif /* MAP_ANONYMOUS */ -#define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s) + #define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s) -#else /* WIN32 */ + #else /* WIN32 */ /* Win32 MMAP via VirtualAlloc */ -static FORCEINLINE void* win32mmap(size_t size) { - void* ptr; +static FORCEINLINE void *win32mmap(size_t size) { + + void *ptr; - ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); - if (ptr == 0) - return MFAIL; + ptr = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (ptr == 0) return MFAIL; return ptr; + } /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ -static FORCEINLINE void* win32direct_mmap(size_t size) { - void* ptr; +static FORCEINLINE void *win32direct_mmap(size_t size) { + + void *ptr; - ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, - PAGE_READWRITE); - if (ptr == 0) - return MFAIL; + ptr = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, + PAGE_READWRITE); + if (ptr == 0) return MFAIL; return ptr; + } /* This function supports releasing coalesed segments */ -static FORCEINLINE int win32munmap(void* ptr, size_t size) { +static FORCEINLINE int win32munmap(void *ptr, size_t size) { + MEMORY_BASIC_INFORMATION minfo; - char* cptr = (char*)ptr; + char *cptr = (char *)ptr; while (size) { - if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) - return -1; + + if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) return -1; if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || minfo.State != MEM_COMMIT || minfo.RegionSize > size) return -1; - if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) - return -1; + if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) return -1; cptr += minfo.RegionSize; size -= minfo.RegionSize; + } return 0; + } -#define MMAP_DEFAULT(s) win32mmap(s) -#define MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) -#define DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) -#endif /* WIN32 */ -#endif /* HAVE_MMAP */ + #define MMAP_DEFAULT(s) win32mmap(s) + #define MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) + #define DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) + #endif /* WIN32 */ + #endif /* HAVE_MMAP */ + + #if HAVE_MREMAP + #ifndef WIN32 -#if HAVE_MREMAP -#ifndef WIN32 +static FORCEINLINE void *dlmremap(void *old_address, size_t old_size, + size_t new_size, int flags) { -static FORCEINLINE void* dlmremap(void* old_address, size_t old_size, size_t new_size, int flags) { - void* result; + void *result; result = mremap(old_address, old_size, new_size, flags); - if (result == MFAIL) - return MFAIL; + if (result == MFAIL) return MFAIL; return result; + } -#define MREMAP_DEFAULT(addr, osz, nsz, mv) dlmremap((addr), (osz), (nsz), (mv)) -#endif /* WIN32 */ -#endif /* HAVE_MREMAP */ + #define MREMAP_DEFAULT(addr, osz, nsz, mv) \ + dlmremap((addr), (osz), (nsz), (mv)) + #endif /* WIN32 */ + #endif /* HAVE_MREMAP */ -/** - * Define CALL_MORECORE - */ -#if HAVE_MORECORE + /** + * Define CALL_MORECORE + */ + #if HAVE_MORECORE #ifdef MORECORE - #define CALL_MORECORE(S) MORECORE(S) - #else /* MORECORE */ - #define CALL_MORECORE(S) MORECORE_DEFAULT(S) - #endif /* MORECORE */ -#else /* HAVE_MORECORE */ - #define CALL_MORECORE(S) MFAIL -#endif /* HAVE_MORECORE */ - -/** - * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP - */ -#if HAVE_MMAP - #define USE_MMAP_BIT (SIZE_T_ONE) + #define CALL_MORECORE(S) MORECORE(S) + #else /* MORECORE */ + #define CALL_MORECORE(S) MORECORE_DEFAULT(S) + #endif /* MORECORE */ + #else /* HAVE_MORECORE */ + #define CALL_MORECORE(S) MFAIL + #endif /* HAVE_MORECORE */ + + /** + * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP + */ + #if HAVE_MMAP + #define USE_MMAP_BIT (SIZE_T_ONE) #ifdef MMAP - #define CALL_MMAP(s) MMAP(s) - #else /* MMAP */ - #define CALL_MMAP(s) MMAP_DEFAULT(s) - #endif /* MMAP */ + #define CALL_MMAP(s) MMAP(s) + #else /* MMAP */ + #define CALL_MMAP(s) MMAP_DEFAULT(s) + #endif /* MMAP */ #ifdef MUNMAP - #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) - #else /* MUNMAP */ - #define CALL_MUNMAP(a, s) MUNMAP_DEFAULT((a), (s)) - #endif /* MUNMAP */ + #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) + #else /* MUNMAP */ + #define CALL_MUNMAP(a, s) MUNMAP_DEFAULT((a), (s)) + #endif /* MUNMAP */ #ifdef DIRECT_MMAP - #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) - #else /* DIRECT_MMAP */ - #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s) - #endif /* DIRECT_MMAP */ -#else /* HAVE_MMAP */ - #define USE_MMAP_BIT (SIZE_T_ZERO) - - #define MMAP(s) MFAIL - #define MUNMAP(a, s) (-1) - #define DIRECT_MMAP(s) MFAIL - #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) - #define CALL_MMAP(s) MMAP(s) - #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) -#endif /* HAVE_MMAP */ - -/** - * Define CALL_MREMAP - */ -#if HAVE_MMAP && HAVE_MREMAP + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) + #else /* DIRECT_MMAP */ + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s) + #endif /* DIRECT_MMAP */ + #else /* HAVE_MMAP */ + #define USE_MMAP_BIT (SIZE_T_ZERO) + + #define MMAP(s) MFAIL + #define MUNMAP(a, s) (-1) + #define DIRECT_MMAP(s) MFAIL + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) + #define CALL_MMAP(s) MMAP(s) + #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) + #endif /* HAVE_MMAP */ + + /** + * Define CALL_MREMAP + */ + #if HAVE_MMAP && HAVE_MREMAP #ifdef MREMAP - #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) - #else /* MREMAP */ - #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) - #endif /* MREMAP */ -#else /* HAVE_MMAP && HAVE_MREMAP */ - #define CALL_MREMAP(addr, osz, nsz, mv) MFAIL -#endif /* HAVE_MMAP && HAVE_MREMAP */ + #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) + #else /* MREMAP */ + #define CALL_MREMAP(addr, osz, nsz, mv) \ + MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) + #endif /* MREMAP */ + #else /* HAVE_MMAP && HAVE_MREMAP */ + #define CALL_MREMAP(addr, osz, nsz, mv) MFAIL + #endif /* HAVE_MMAP && HAVE_MREMAP */ -/* mstate bit set if continguous morecore disabled or failed */ -#define USE_NONCONTIGUOUS_BIT (4U) - -/* segment bit set in create_mspace_with_base */ -#define EXTERN_BIT (8U) + /* mstate bit set if continguous morecore disabled or failed */ + #define USE_NONCONTIGUOUS_BIT (4U) + /* segment bit set in create_mspace_with_base */ + #define EXTERN_BIT (8U) /* --------------------------- Lock preliminaries ------------------------ */ @@ -1855,248 +1901,286 @@ static FORCEINLINE void* dlmremap(void* old_address, size_t old_size, size_t new */ -#if !USE_LOCKS -#define USE_LOCK_BIT (0U) -#define INITIAL_LOCK(l) (0) -#define DESTROY_LOCK(l) (0) -#define ACQUIRE_MALLOC_GLOBAL_LOCK() -#define RELEASE_MALLOC_GLOBAL_LOCK() - -#else -#if USE_LOCKS > 1 -/* ----------------------- User-defined locks ------------------------ */ -/* Define your own lock implementation here */ -/* #define INITIAL_LOCK(lk) ... */ -/* #define DESTROY_LOCK(lk) ... */ -/* #define ACQUIRE_LOCK(lk) ... */ -/* #define RELEASE_LOCK(lk) ... */ -/* #define TRY_LOCK(lk) ... */ -/* static MLOCK_T malloc_global_mutex = ... */ - -#elif USE_SPIN_LOCKS - -/* First, define CAS_LOCK and CLEAR_LOCK on ints */ -/* Note CAS_LOCK defined to return 0 on success */ - -#if defined(__GNUC__)&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) -#define CAS_LOCK(sl) __sync_lock_test_and_set(sl, 1) -#define CLEAR_LOCK(sl) __sync_lock_release(sl) - -#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) + #if !USE_LOCKS + #define USE_LOCK_BIT (0U) + #define INITIAL_LOCK(l) (0) + #define DESTROY_LOCK(l) (0) + #define ACQUIRE_MALLOC_GLOBAL_LOCK() + #define RELEASE_MALLOC_GLOBAL_LOCK() + + #else + #if USE_LOCKS > 1 + /* ----------------------- User-defined locks ------------------------ */ + /* Define your own lock implementation here */ + /* #define INITIAL_LOCK(lk) ... */ + /* #define DESTROY_LOCK(lk) ... */ + /* #define ACQUIRE_LOCK(lk) ... */ + /* #define RELEASE_LOCK(lk) ... */ + /* #define TRY_LOCK(lk) ... */ + /* static MLOCK_T malloc_global_mutex = ... */ + + #elif USE_SPIN_LOCKS + + /* First, define CAS_LOCK and CLEAR_LOCK on ints */ + /* Note CAS_LOCK defined to return 0 on success */ + + #if defined(__GNUC__) && \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) + #define CAS_LOCK(sl) __sync_lock_test_and_set(sl, 1) + #define CLEAR_LOCK(sl) __sync_lock_release(sl) + + #elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) /* Custom spin locks for older gcc on x86 */ static FORCEINLINE int x86_cas_lock(int *sl) { + int ret; int val = 1; int cmp = 0; - __asm__ __volatile__ ("lock; cmpxchgl %1, %2" - : "=a" (ret) - : "r" (val), "m" (*(sl)), "0"(cmp) - : "memory", "cc"); + __asm__ __volatile__("lock; cmpxchgl %1, %2" + : "=a"(ret) + : "r"(val), "m"(*(sl)), "0"(cmp) + : "memory", "cc"); return ret; + } -static FORCEINLINE void x86_clear_lock(int* sl) { +static FORCEINLINE void x86_clear_lock(int *sl) { + assert(*sl != 0); int prev = 0; int ret; - __asm__ __volatile__ ("lock; xchgl %0, %1" - : "=r" (ret) - : "m" (*(sl)), "0"(prev) - : "memory"); + __asm__ __volatile__("lock; xchgl %0, %1" + : "=r"(ret) + : "m"(*(sl)), "0"(prev) + : "memory"); + } -#define CAS_LOCK(sl) x86_cas_lock(sl) -#define CLEAR_LOCK(sl) x86_clear_lock(sl) - -#else /* Win32 MSC */ -#define CAS_LOCK(sl) interlockedexchange((volatile LONG *)sl, (LONG)1) -#define CLEAR_LOCK(sl) interlockedexchange((volatile LONG *)sl, (LONG)0) - -#endif /* ... gcc spins locks ... */ - -/* How to yield for a spin lock */ -#define SPINS_PER_YIELD 63 -#if defined(_MSC_VER) -#define SLEEP_EX_DURATION 50 /* delay for yield/sleep */ -#define SPIN_LOCK_YIELD SleepEx(SLEEP_EX_DURATION, FALSE) -#elif defined (__SVR4) && defined (__sun) /* solaris */ -#define SPIN_LOCK_YIELD thr_yield(); -#elif !defined(LACKS_SCHED_H) -#define SPIN_LOCK_YIELD sched_yield(); -#else -#define SPIN_LOCK_YIELD -#endif /* ... yield ... */ - -#if !defined(USE_RECURSIVE_LOCKS) || USE_RECURSIVE_LOCKS == 0 + #define CAS_LOCK(sl) x86_cas_lock(sl) + #define CLEAR_LOCK(sl) x86_clear_lock(sl) + + #else /* Win32 MSC */ + #define CAS_LOCK(sl) interlockedexchange((volatile LONG *)sl, (LONG)1) + #define CLEAR_LOCK(sl) interlockedexchange((volatile LONG *)sl, (LONG)0) + + #endif /* ... gcc spins locks ... */ + + /* How to yield for a spin lock */ + #define SPINS_PER_YIELD 63 + #if defined(_MSC_VER) + #define SLEEP_EX_DURATION 50 /* delay for yield/sleep */ + #define SPIN_LOCK_YIELD SleepEx(SLEEP_EX_DURATION, FALSE) + #elif defined(__SVR4) && defined(__sun) /* solaris */ + #define SPIN_LOCK_YIELD thr_yield(); + #elif !defined(LACKS_SCHED_H) + #define SPIN_LOCK_YIELD sched_yield(); + #else + #define SPIN_LOCK_YIELD + #endif /* ... yield ... */ + + #if !defined(USE_RECURSIVE_LOCKS) || USE_RECURSIVE_LOCKS == 0 /* Plain spin locks use single word (embedded in malloc_states) */ static int spin_acquire_lock(int *sl) { + int spins = 0; while (*(volatile int *)sl != 0 || CAS_LOCK(sl)) { - if ((++spins & SPINS_PER_YIELD) == 0) { - SPIN_LOCK_YIELD; - } + + if ((++spins & SPINS_PER_YIELD) == 0) { SPIN_LOCK_YIELD; } + } + return 0; + } -#define MLOCK_T int -#define TRY_LOCK(sl) !CAS_LOCK(sl) -#define RELEASE_LOCK(sl) CLEAR_LOCK(sl) -#define ACQUIRE_LOCK(sl) (CAS_LOCK(sl)? spin_acquire_lock(sl) : 0) -#define INITIAL_LOCK(sl) (*sl = 0) -#define DESTROY_LOCK(sl) (0) + #define MLOCK_T int + #define TRY_LOCK(sl) !CAS_LOCK(sl) + #define RELEASE_LOCK(sl) CLEAR_LOCK(sl) + #define ACQUIRE_LOCK(sl) (CAS_LOCK(sl) ? spin_acquire_lock(sl) : 0) + #define INITIAL_LOCK(sl) (*sl = 0) + #define DESTROY_LOCK(sl) (0) static MLOCK_T malloc_global_mutex = 0; -#else /* USE_RECURSIVE_LOCKS */ -/* types for lock owners */ -#ifdef WIN32 -#define THREAD_ID_T DWORD -#define CURRENT_THREAD GetCurrentThreadId() -#define EQ_OWNER(X,Y) ((X) == (Y)) -#else -/* - Note: the following assume that pthread_t is a type that can be - initialized to (casted) zero. If this is not the case, you will need to - somehow redefine these or not use spin locks. -*/ -#define THREAD_ID_T pthread_t -#define CURRENT_THREAD pthread_self() -#define EQ_OWNER(X,Y) pthread_equal(X, Y) -#endif + #else /* USE_RECURSIVE_LOCKS */ + /* types for lock owners */ + #ifdef WIN32 + #define THREAD_ID_T DWORD + #define CURRENT_THREAD GetCurrentThreadId() + #define EQ_OWNER(X, Y) ((X) == (Y)) + #else + /* + Note: the following assume that pthread_t is a type that can be + initialized to (casted) zero. If this is not the case, you will need + to somehow redefine these or not use spin locks. + */ + #define THREAD_ID_T pthread_t + #define CURRENT_THREAD pthread_self() + #define EQ_OWNER(X, Y) pthread_equal(X, Y) + #endif struct malloc_recursive_lock { - int sl; + + int sl; unsigned int c; - THREAD_ID_T threadid; + THREAD_ID_T threadid; + }; -#define MLOCK_T struct malloc_recursive_lock -static MLOCK_T malloc_global_mutex = { 0, 0, (THREAD_ID_T)0}; + #define MLOCK_T struct malloc_recursive_lock +static MLOCK_T malloc_global_mutex = {0, 0, (THREAD_ID_T)0}; static FORCEINLINE void recursive_release_lock(MLOCK_T *lk) { + assert(lk->sl != 0); - if (--lk->c == 0) { - CLEAR_LOCK(&lk->sl); - } + if (--lk->c == 0) { CLEAR_LOCK(&lk->sl); } + } static FORCEINLINE int recursive_acquire_lock(MLOCK_T *lk) { + THREAD_ID_T mythreadid = CURRENT_THREAD; - int spins = 0; + int spins = 0; for (;;) { + if (*((volatile int *)(&lk->sl)) == 0) { + if (!CAS_LOCK(&lk->sl)) { + lk->threadid = mythreadid; lk->c = 1; return 0; + } - } - else if (EQ_OWNER(lk->threadid, mythreadid)) { + + } else if (EQ_OWNER(lk->threadid, mythreadid)) { + ++lk->c; return 0; + } - if ((++spins & SPINS_PER_YIELD) == 0) { - SPIN_LOCK_YIELD; - } + + if ((++spins & SPINS_PER_YIELD) == 0) { SPIN_LOCK_YIELD; } + } + } static FORCEINLINE int recursive_try_lock(MLOCK_T *lk) { + THREAD_ID_T mythreadid = CURRENT_THREAD; if (*((volatile int *)(&lk->sl)) == 0) { + if (!CAS_LOCK(&lk->sl)) { + lk->threadid = mythreadid; lk->c = 1; return 1; + } - } - else if (EQ_OWNER(lk->threadid, mythreadid)) { + + } else if (EQ_OWNER(lk->threadid, mythreadid)) { + ++lk->c; return 1; + } + return 0; + } -#define RELEASE_LOCK(lk) recursive_release_lock(lk) -#define TRY_LOCK(lk) recursive_try_lock(lk) -#define ACQUIRE_LOCK(lk) recursive_acquire_lock(lk) -#define INITIAL_LOCK(lk) ((lk)->threadid = (THREAD_ID_T)0, (lk)->sl = 0, (lk)->c = 0) -#define DESTROY_LOCK(lk) (0) -#endif /* USE_RECURSIVE_LOCKS */ - -#elif defined(WIN32) /* Win32 critical sections */ -#define MLOCK_T CRITICAL_SECTION -#define ACQUIRE_LOCK(lk) (EnterCriticalSection(lk), 0) -#define RELEASE_LOCK(lk) LeaveCriticalSection(lk) -#define TRY_LOCK(lk) TryEnterCriticalSection(lk) -#define INITIAL_LOCK(lk) (!InitializeCriticalSectionAndSpinCount((lk), 0x80000000|4000)) -#define DESTROY_LOCK(lk) (DeleteCriticalSection(lk), 0) -#define NEED_GLOBAL_LOCK_INIT - -static MLOCK_T malloc_global_mutex; + #define RELEASE_LOCK(lk) recursive_release_lock(lk) + #define TRY_LOCK(lk) recursive_try_lock(lk) + #define ACQUIRE_LOCK(lk) recursive_acquire_lock(lk) + #define INITIAL_LOCK(lk) \ + ((lk)->threadid = (THREAD_ID_T)0, (lk)->sl = 0, (lk)->c = 0) + #define DESTROY_LOCK(lk) (0) + #endif /* USE_RECURSIVE_LOCKS */ + + #elif defined(WIN32) /* Win32 critical sections */ + #define MLOCK_T CRITICAL_SECTION + #define ACQUIRE_LOCK(lk) (EnterCriticalSection(lk), 0) + #define RELEASE_LOCK(lk) LeaveCriticalSection(lk) + #define TRY_LOCK(lk) TryEnterCriticalSection(lk) + #define INITIAL_LOCK(lk) \ + (!InitializeCriticalSectionAndSpinCount((lk), 0x80000000 | 4000)) + #define DESTROY_LOCK(lk) (DeleteCriticalSection(lk), 0) + #define NEED_GLOBAL_LOCK_INIT + +static MLOCK_T malloc_global_mutex; static volatile LONG malloc_global_mutex_status; /* Use spin loop to initialize global lock */ static void init_malloc_global_mutex() { + for (;;) { + long stat = malloc_global_mutex_status; - if (stat > 0) - return; + if (stat > 0) return; /* transition to < 0 while initializing, then to > 0) */ - if (stat == 0 && - interlockedcompareexchange(&malloc_global_mutex_status, (LONG)-1, (LONG)0) == 0) { + if (stat == 0 && interlockedcompareexchange(&malloc_global_mutex_status, + (LONG)-1, (LONG)0) == 0) { + InitializeCriticalSection(&malloc_global_mutex); interlockedexchange(&malloc_global_mutex_status, (LONG)1); return; + } + SleepEx(0, FALSE); + } + } -#else /* pthreads-based locks */ -#define MLOCK_T pthread_mutex_t -#define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk) -#define RELEASE_LOCK(lk) pthread_mutex_unlock(lk) -#define TRY_LOCK(lk) (!pthread_mutex_trylock(lk)) -#define INITIAL_LOCK(lk) pthread_init_lock(lk) -#define DESTROY_LOCK(lk) pthread_mutex_destroy(lk) + #else /* pthreads-based locks */ + #define MLOCK_T pthread_mutex_t + #define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk) + #define RELEASE_LOCK(lk) pthread_mutex_unlock(lk) + #define TRY_LOCK(lk) (!pthread_mutex_trylock(lk)) + #define INITIAL_LOCK(lk) pthread_init_lock(lk) + #define DESTROY_LOCK(lk) pthread_mutex_destroy(lk) -#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 && defined(linux) && !defined(PTHREAD_MUTEX_RECURSIVE) + #if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 && \ + defined(linux) && !defined(PTHREAD_MUTEX_RECURSIVE) /* Cope with old-style linux recursive lock initialization by adding */ /* skipped internal declaration from pthread.h */ -extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, - int __kind)); -#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP -#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) -#endif /* USE_RECURSIVE_LOCKS ... */ +extern int pthread_mutexattr_setkind_np __P((pthread_mutexattr_t * __attr, + int __kind)); + #define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP + #define pthread_mutexattr_settype(x, y) \ + pthread_mutexattr_setkind_np(x, y) + #endif /* USE_RECURSIVE_LOCKS ... */ static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; -static int pthread_init_lock (MLOCK_T *lk) { +static int pthread_init_lock(MLOCK_T *lk) { + pthread_mutexattr_t attr; if (pthread_mutexattr_init(&attr)) return 1; -#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 + #if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; -#endif + #endif if (pthread_mutex_init(lk, &attr)) return 1; if (pthread_mutexattr_destroy(&attr)) return 1; return 0; + } -#endif /* ... lock types ... */ + #endif /* ... lock types ... */ -/* Common code for all lock types */ -#define USE_LOCK_BIT (2U) + /* Common code for all lock types */ + #define USE_LOCK_BIT (2U) -#ifndef ACQUIRE_MALLOC_GLOBAL_LOCK -#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); -#endif + #ifndef ACQUIRE_MALLOC_GLOBAL_LOCK + #define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); + #endif -#ifndef RELEASE_MALLOC_GLOBAL_LOCK -#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); -#endif + #ifndef RELEASE_MALLOC_GLOBAL_LOCK + #define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); + #endif -#endif /* USE_LOCKS */ + #endif /* USE_LOCKS */ /* ----------------------- Chunk representations ------------------------ */ @@ -2236,56 +2320,56 @@ static int pthread_init_lock (MLOCK_T *lk) { */ struct malloc_chunk { - size_t prev_foot; /* Size of previous chunk (if free). */ - size_t head; /* Size and inuse bits. */ - struct malloc_chunk* fd; /* double links -- used only if free. */ - struct malloc_chunk* bk; + + size_t prev_foot; /* Size of previous chunk (if free). */ + size_t head; /* Size and inuse bits. */ + struct malloc_chunk *fd; /* double links -- used only if free. */ + struct malloc_chunk *bk; + }; typedef struct malloc_chunk mchunk; -typedef struct malloc_chunk* mchunkptr; -typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ -typedef unsigned int bindex_t; /* Described below */ -typedef unsigned int binmap_t; /* Described below */ -typedef unsigned int flag_t; /* The type of various bit flag sets */ +typedef struct malloc_chunk *mchunkptr; +typedef struct malloc_chunk *sbinptr; /* The type of bins of chunks */ +typedef unsigned int bindex_t; /* Described below */ +typedef unsigned int binmap_t; /* Described below */ +typedef unsigned int flag_t; /* The type of various bit flag sets */ /* ------------------- Chunks sizes and alignments ----------------------- */ -#define MCHUNK_SIZE (sizeof(mchunk)) + #define MCHUNK_SIZE (sizeof(mchunk)) -#if FOOTERS -#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) -#else /* FOOTERS */ -#define CHUNK_OVERHEAD (SIZE_T_SIZE) -#endif /* FOOTERS */ + #if FOOTERS + #define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) + #else /* FOOTERS */ + #define CHUNK_OVERHEAD (SIZE_T_SIZE) + #endif /* FOOTERS */ -/* MMapped chunks need a second word of overhead ... */ -#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) -/* ... and additional padding for fake next-chunk at foot */ -#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) + /* MMapped chunks need a second word of overhead ... */ + #define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) + /* ... and additional padding for fake next-chunk at foot */ + #define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) -/* The smallest size we can malloc is an aligned minimal chunk */ -#define MIN_CHUNK_SIZE\ - ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + /* The smallest size we can malloc is an aligned minimal chunk */ + #define MIN_CHUNK_SIZE ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) -/* conversion from malloc headers to user pointers, and back */ -#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) -#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) -/* chunk associated with aligned address A */ -#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) + /* conversion from malloc headers to user pointers, and back */ + #define chunk2mem(p) ((void *)((char *)(p) + TWO_SIZE_T_SIZES)) + #define mem2chunk(mem) ((mchunkptr)((char *)(mem)-TWO_SIZE_T_SIZES)) + /* chunk associated with aligned address A */ + #define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) -/* Bounds on request (not chunk) sizes. */ -#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) -#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) + /* Bounds on request (not chunk) sizes. */ + #define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) + #define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) -/* pad request bytes into a usable size */ -#define pad_request(req) \ - (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) - -/* pad request, checking for minimum (but not maximum) */ -#define request2size(req) \ - (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) + /* pad request bytes into a usable size */ + #define pad_request(req) \ + (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + /* pad request, checking for minimum (but not maximum) */ + #define request2size(req) \ + (((req) < MIN_REQUEST) ? MIN_CHUNK_SIZE : pad_request(req)) /* ------------------ Operations on head and foot fields ----------------- */ @@ -2297,61 +2381,60 @@ typedef unsigned int flag_t; /* The type of various bit flag sets */ FLAG4_BIT is not used by this malloc, but might be useful in extensions. */ -#define PINUSE_BIT (SIZE_T_ONE) -#define CINUSE_BIT (SIZE_T_TWO) -#define FLAG4_BIT (SIZE_T_FOUR) -#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) -#define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT) + #define PINUSE_BIT (SIZE_T_ONE) + #define CINUSE_BIT (SIZE_T_TWO) + #define FLAG4_BIT (SIZE_T_FOUR) + #define INUSE_BITS (PINUSE_BIT | CINUSE_BIT) + #define FLAG_BITS (PINUSE_BIT | CINUSE_BIT | FLAG4_BIT) -/* Head value for fenceposts */ -#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) + /* Head value for fenceposts */ + #define FENCEPOST_HEAD (INUSE_BITS | SIZE_T_SIZE) -/* extraction of fields from head words */ -#define cinuse(p) ((p)->head & CINUSE_BIT) -#define pinuse(p) ((p)->head & PINUSE_BIT) -#define flag4inuse(p) ((p)->head & FLAG4_BIT) -#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) -#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) + /* extraction of fields from head words */ + #define cinuse(p) ((p)->head & CINUSE_BIT) + #define pinuse(p) ((p)->head & PINUSE_BIT) + #define flag4inuse(p) ((p)->head & FLAG4_BIT) + #define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) + #define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) -#define chunksize(p) ((p)->head & ~(FLAG_BITS)) + #define chunksize(p) ((p)->head & ~(FLAG_BITS)) -#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) -#define set_flag4(p) ((p)->head |= FLAG4_BIT) -#define clear_flag4(p) ((p)->head &= ~FLAG4_BIT) + #define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) + #define set_flag4(p) ((p)->head |= FLAG4_BIT) + #define clear_flag4(p) ((p)->head &= ~FLAG4_BIT) -/* Treat space at ptr +/- offset as a chunk */ -#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) -#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) + /* Treat space at ptr +/- offset as a chunk */ + #define chunk_plus_offset(p, s) ((mchunkptr)(((char *)(p)) + (s))) + #define chunk_minus_offset(p, s) ((mchunkptr)(((char *)(p)) - (s))) -/* Ptr to next or previous physical malloc_chunk. */ -#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS))) -#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) + /* Ptr to next or previous physical malloc_chunk. */ + #define next_chunk(p) ((mchunkptr)(((char *)(p)) + ((p)->head & ~FLAG_BITS))) + #define prev_chunk(p) ((mchunkptr)(((char *)(p)) - ((p)->prev_foot))) -/* extract next chunk's pinuse bit */ -#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) + /* extract next chunk's pinuse bit */ + #define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) -/* Get/set size at footer */ -#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) -#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) + /* Get/set size at footer */ + #define get_foot(p, s) (((mchunkptr)((char *)(p) + (s)))->prev_foot) + #define set_foot(p, s) (((mchunkptr)((char *)(p) + (s)))->prev_foot = (s)) -/* Set size, pinuse bit, and foot */ -#define set_size_and_pinuse_of_free_chunk(p, s)\ - ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) + /* Set size, pinuse bit, and foot */ + #define set_size_and_pinuse_of_free_chunk(p, s) \ + ((p)->head = (s | PINUSE_BIT), set_foot(p, s)) -/* Set size, pinuse bit, foot, and clear next pinuse */ -#define set_free_with_pinuse(p, s, n)\ - (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) + /* Set size, pinuse bit, foot, and clear next pinuse */ + #define set_free_with_pinuse(p, s, n) \ + (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) -/* Get the internal overhead associated with chunk p */ -#define overhead_for(p)\ - (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) + /* Get the internal overhead associated with chunk p */ + #define overhead_for(p) (is_mmapped(p) ? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) -/* Return true if malloced space is not necessarily cleared */ -#if MMAP_CLEARS -#define calloc_must_clear(p) (!is_mmapped(p)) -#else /* MMAP_CLEARS */ -#define calloc_must_clear(p) (1) -#endif /* MMAP_CLEARS */ + /* Return true if malloced space is not necessarily cleared */ + #if MMAP_CLEARS + #define calloc_must_clear(p) (!is_mmapped(p)) + #else /* MMAP_CLEARS */ + #define calloc_must_clear(p) (1) + #endif /* MMAP_CLEARS */ /* ---------------------- Overlaid data structures ----------------------- */ @@ -2445,23 +2528,25 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct malloc_tree_chunk { + /* The first four fields must be compatible with malloc_chunk */ size_t prev_foot; size_t head; - struct malloc_tree_chunk* fd; - struct malloc_tree_chunk* bk; + struct malloc_tree_chunk *fd; + struct malloc_tree_chunk *bk; - struct malloc_tree_chunk* child[2]; - struct malloc_tree_chunk* parent; + struct malloc_tree_chunk *child[2]; + struct malloc_tree_chunk *parent; bindex_t index; + }; typedef struct malloc_tree_chunk tchunk; -typedef struct malloc_tree_chunk* tchunkptr; -typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ +typedef struct malloc_tree_chunk *tchunkptr; +typedef struct malloc_tree_chunk *tbinptr; /* The type of bins of trees */ -/* A little helper macro for trees */ -#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) + /* A little helper macro for trees */ + #define leftmost_child(t) ((t)->child[0] != 0 ? (t)->child[0] : (t)->child[1]) /* ----------------------------- Segments -------------------------------- */ @@ -2521,141 +2606,145 @@ typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ */ struct malloc_segment { - char* base; /* base address */ - size_t size; /* allocated size */ - struct malloc_segment* next; /* ptr to next segment */ - flag_t sflags; /* mmap and extern flag */ + + char * base; /* base address */ + size_t size; /* allocated size */ + struct malloc_segment *next; /* ptr to next segment */ + flag_t sflags; /* mmap and extern flag */ + }; -#define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) -#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) + #define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) + #define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) typedef struct malloc_segment msegment; -typedef struct malloc_segment* msegmentptr; +typedef struct malloc_segment *msegmentptr; -/* ---------------------------- malloc_state ----------------------------- */ + /* ---------------------------- malloc_state ----------------------------- */ -/* - A malloc_state holds all of the bookkeeping for a space. - The main fields are: - - Top - The topmost chunk of the currently active segment. Its size is - cached in topsize. The actual size of topmost space is - topsize+TOP_FOOT_SIZE, which includes space reserved for adding - fenceposts and segment records if necessary when getting more - space from the system. The size at which to autotrim top is - cached from mparams in trim_check, except that it is disabled if - an autotrim fails. - - Designated victim (dv) - This is the preferred chunk for servicing small requests that - don't have exact fits. It is normally the chunk split off most - recently to service another small request. Its size is cached in - dvsize. The link fields of this chunk are not maintained since it - is not kept in a bin. - - SmallBins - An array of bin headers for free chunks. These bins hold chunks - with sizes less than MIN_LARGE_SIZE bytes. Each bin contains - chunks of all the same size, spaced 8 bytes apart. To simplify - use in double-linked lists, each bin header acts as a malloc_chunk - pointing to the real first node, if it exists (else pointing to - itself). This avoids special-casing for headers. But to avoid - waste, we allocate only the fd/bk pointers of bins, and then use - repositioning tricks to treat these as the fields of a chunk. - - TreeBins - Treebins are pointers to the roots of trees holding a range of - sizes. There are 2 equally spaced treebins for each power of two - from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything - larger. - - Bin maps - There is one bit map for small bins ("smallmap") and one for - treebins ("treemap). Each bin sets its bit when non-empty, and - clears the bit when empty. Bit operations are then used to avoid - bin-by-bin searching -- nearly all "search" is done without ever - looking at bins that won't be selected. The bit maps - conservatively use 32 bits per map word, even if on 64bit system. - For a good description of some of the bit-based techniques used - here, see Henry S. Warren Jr's book "Hacker's Delight" (and - supplement at http://hackersdelight.org/). Many of these are - intended to reduce the branchiness of paths through malloc etc, as - well as to reduce the number of memory locations read or written. - - Segments - A list of segments headed by an embedded malloc_segment record - representing the initial space. - - Address check support - The least_addr field is the least address ever obtained from - MORECORE or MMAP. Attempted frees and reallocs of any address less - than this are trapped (unless INSECURE is defined). - - Magic tag - A cross-check field that should always hold same value as mparams.magic. - - Max allowed footprint - The maximum allowed bytes to allocate from system (zero means no limit) - - Flags - Bits recording whether to use MMAP, locks, or contiguous MORECORE - - Statistics - Each space keeps track of current and maximum system memory - obtained via MORECORE or MMAP. - - Trim support - Fields holding the amount of unused topmost memory that should trigger - trimming, and a counter to force periodic scanning to release unused - non-topmost segments. - - Locking - If USE_LOCKS is defined, the "mutex" lock is acquired and released - around every public call using this mspace. - - Extension support - A void* pointer and a size_t field that can be used to help implement - extensions to this malloc. -*/ + /* + A malloc_state holds all of the bookkeeping for a space. + The main fields are: + + Top + The topmost chunk of the currently active segment. Its size is + cached in topsize. The actual size of topmost space is + topsize+TOP_FOOT_SIZE, which includes space reserved for adding + fenceposts and segment records if necessary when getting more + space from the system. The size at which to autotrim top is + cached from mparams in trim_check, except that it is disabled if + an autotrim fails. + + Designated victim (dv) + This is the preferred chunk for servicing small requests that + don't have exact fits. It is normally the chunk split off most + recently to service another small request. Its size is cached in + dvsize. The link fields of this chunk are not maintained since it + is not kept in a bin. + + SmallBins + An array of bin headers for free chunks. These bins hold chunks + with sizes less than MIN_LARGE_SIZE bytes. Each bin contains + chunks of all the same size, spaced 8 bytes apart. To simplify + use in double-linked lists, each bin header acts as a malloc_chunk + pointing to the real first node, if it exists (else pointing to + itself). This avoids special-casing for headers. But to avoid + waste, we allocate only the fd/bk pointers of bins, and then use + repositioning tricks to treat these as the fields of a chunk. + + TreeBins + Treebins are pointers to the roots of trees holding a range of + sizes. There are 2 equally spaced treebins for each power of two + from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything + larger. + + Bin maps + There is one bit map for small bins ("smallmap") and one for + treebins ("treemap). Each bin sets its bit when non-empty, and + clears the bit when empty. Bit operations are then used to avoid + bin-by-bin searching -- nearly all "search" is done without ever + looking at bins that won't be selected. The bit maps + conservatively use 32 bits per map word, even if on 64bit system. + For a good description of some of the bit-based techniques used + here, see Henry S. Warren Jr's book "Hacker's Delight" (and + supplement at http://hackersdelight.org/). Many of these are + intended to reduce the branchiness of paths through malloc etc, as + well as to reduce the number of memory locations read or written. + + Segments + A list of segments headed by an embedded malloc_segment record + representing the initial space. + + Address check support + The least_addr field is the least address ever obtained from + MORECORE or MMAP. Attempted frees and reallocs of any address less + than this are trapped (unless INSECURE is defined). + + Magic tag + A cross-check field that should always hold same value as mparams.magic. + + Max allowed footprint + The maximum allowed bytes to allocate from system (zero means no limit) + + Flags + Bits recording whether to use MMAP, locks, or contiguous MORECORE + + Statistics + Each space keeps track of current and maximum system memory + obtained via MORECORE or MMAP. + + Trim support + Fields holding the amount of unused topmost memory that should trigger + trimming, and a counter to force periodic scanning to release unused + non-topmost segments. + + Locking + If USE_LOCKS is defined, the "mutex" lock is acquired and released + around every public call using this mspace. + + Extension support + A void* pointer and a size_t field that can be used to help implement + extensions to this malloc. + */ -/* Bin types, widths and sizes */ -#define NSMALLBINS (32U) -#define NTREEBINS (32U) -#define SMALLBIN_SHIFT (3U) -#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) -#define TREEBIN_SHIFT (8U) -#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) -#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) -#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) + /* Bin types, widths and sizes */ + #define NSMALLBINS (32U) + #define NTREEBINS (32U) + #define SMALLBIN_SHIFT (3U) + #define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) + #define TREEBIN_SHIFT (8U) + #define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) + #define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) + #define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) struct malloc_state { - binmap_t smallmap; - binmap_t treemap; - size_t dvsize; - size_t topsize; - char* least_addr; - mchunkptr dv; - mchunkptr top; - size_t trim_check; - size_t release_checks; - size_t magic; - mchunkptr smallbins[(NSMALLBINS+1)*2]; - tbinptr treebins[NTREEBINS]; - size_t footprint; - size_t max_footprint; - size_t footprint_limit; /* zero means no limit */ - flag_t mflags; -#if USE_LOCKS - MLOCK_T mutex; /* locate lock among fields that rarely change */ -#endif /* USE_LOCKS */ - msegment seg; - void* extp; /* Unused but available for extensions */ - size_t exts; + + binmap_t smallmap; + binmap_t treemap; + size_t dvsize; + size_t topsize; + char * least_addr; + mchunkptr dv; + mchunkptr top; + size_t trim_check; + size_t release_checks; + size_t magic; + mchunkptr smallbins[(NSMALLBINS + 1) * 2]; + tbinptr treebins[NTREEBINS]; + size_t footprint; + size_t max_footprint; + size_t footprint_limit; /* zero means no limit */ + flag_t mflags; + #if USE_LOCKS + MLOCK_T mutex; /* locate lock among fields that rarely change */ + #endif /* USE_LOCKS */ + msegment seg; + void * extp; /* Unused but available for extensions */ + size_t exts; + }; -typedef struct malloc_state* mstate; +typedef struct malloc_state *mstate; /* ------------- Global malloc_state and malloc_params ------------------- */ @@ -2667,123 +2756,128 @@ typedef struct malloc_state* mstate; */ struct malloc_params { + size_t magic; size_t page_size; size_t granularity; size_t mmap_threshold; size_t trim_threshold; flag_t default_mflags; + }; static struct malloc_params mparams; -/* Ensure mparams initialized */ -#define ensure_initialization() (void)(mparams.magic != 0 || init_mparams()) + /* Ensure mparams initialized */ + #define ensure_initialization() (void)(mparams.magic != 0 || init_mparams()) -#if !ONLY_MSPACES + #if !ONLY_MSPACES /* The global malloc_state used for all non-"mspace" calls */ static struct malloc_state _gm_; -#define gm (&_gm_) -#define is_global(M) ((M) == &_gm_) + #define gm (&_gm_) + #define is_global(M) ((M) == &_gm_) -#endif /* !ONLY_MSPACES */ + #endif /* !ONLY_MSPACES */ -#define is_initialized(M) ((M)->top != 0) + #define is_initialized(M) ((M)->top != 0) /* -------------------------- system alloc setup ------------------------- */ /* Operations on mflags */ -#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) -#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) -#if USE_LOCKS -#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) -#else -#define disable_lock(M) -#endif - -#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) -#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) -#if HAVE_MMAP -#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) -#else -#define disable_mmap(M) -#endif - -#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) -#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) - -#define set_lock(M,L)\ - ((M)->mflags = (L)?\ - ((M)->mflags | USE_LOCK_BIT) :\ - ((M)->mflags & ~USE_LOCK_BIT)) - -/* page-align a size */ -#define page_align(S)\ - (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) - -/* granularity-align a size */ -#define granularity_align(S)\ - (((S) + (mparams.granularity - SIZE_T_ONE))\ - & ~(mparams.granularity - SIZE_T_ONE)) - - -/* For mmap, use granularity alignment on windows, else page-align */ -#ifdef WIN32 -#define mmap_align(S) granularity_align(S) -#else -#define mmap_align(S) page_align(S) -#endif - -/* For sys_alloc, enough padding to ensure can malloc request on success */ -#define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) - -#define is_page_aligned(S)\ - (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) -#define is_granularity_aligned(S)\ - (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) - -/* True if segment S holds address A */ -#define segment_holds(S, A)\ - ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) + #define use_lock(M) ((M)->mflags & USE_LOCK_BIT) + #define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) + #if USE_LOCKS + #define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) + #else + #define disable_lock(M) + #endif + + #define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) + #define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) + #if HAVE_MMAP + #define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) + #else + #define disable_mmap(M) + #endif + + #define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) + #define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) + + #define set_lock(M, L) \ + ((M)->mflags = \ + (L) ? ((M)->mflags | USE_LOCK_BIT) : ((M)->mflags & ~USE_LOCK_BIT)) + + /* page-align a size */ + #define page_align(S) \ + (((S) + (mparams.page_size - SIZE_T_ONE)) & \ + ~(mparams.page_size - SIZE_T_ONE)) + + /* granularity-align a size */ + #define granularity_align(S) \ + (((S) + (mparams.granularity - SIZE_T_ONE)) & \ + ~(mparams.granularity - SIZE_T_ONE)) + + /* For mmap, use granularity alignment on windows, else page-align */ + #ifdef WIN32 + #define mmap_align(S) granularity_align(S) + #else + #define mmap_align(S) page_align(S) + #endif + + /* For sys_alloc, enough padding to ensure can malloc request on success */ + #define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) + + #define is_page_aligned(S) \ + (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) + #define is_granularity_aligned(S) \ + (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) + + /* True if segment S holds address A */ + #define segment_holds(S, A) \ + ((char *)(A) >= S->base && (char *)(A) < S->base + S->size) /* Return segment holding given address */ -static msegmentptr segment_holding(mstate m, char* addr) { +static msegmentptr segment_holding(mstate m, char *addr) { + msegmentptr sp = &m->seg; for (;;) { - if (addr >= sp->base && addr < sp->base + sp->size) - return sp; - if ((sp = sp->next) == 0) - return 0; + + if (addr >= sp->base && addr < sp->base + sp->size) return sp; + if ((sp = sp->next) == 0) return 0; + } + } /* Return true if segment contains a segment link */ static int has_segment_link(mstate m, msegmentptr ss) { + msegmentptr sp = &m->seg; for (;;) { - if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) - return 1; - if ((sp = sp->next) == 0) - return 0; + + if ((char *)sp >= ss->base && (char *)sp < ss->base + ss->size) return 1; + if ((sp = sp->next) == 0) return 0; + } -} -#ifndef MORECORE_CANNOT_TRIM -#define should_trim(M,s) ((s) > (M)->trim_check) -#else /* MORECORE_CANNOT_TRIM */ -#define should_trim(M,s) (0) -#endif /* MORECORE_CANNOT_TRIM */ +} -/* - TOP_FOOT_SIZE is padding at the end of a segment, including space - that may be needed to place segment records and fenceposts when new - noncontiguous segments are added. -*/ -#define TOP_FOOT_SIZE\ - (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) + #ifndef MORECORE_CANNOT_TRIM + #define should_trim(M, s) ((s) > (M)->trim_check) + #else /* MORECORE_CANNOT_TRIM */ + #define should_trim(M, s) (0) + #endif /* MORECORE_CANNOT_TRIM */ + /* + TOP_FOOT_SIZE is padding at the end of a segment, including space + that may be needed to place segment records and fenceposts when new + noncontiguous segments are added. + */ + #define TOP_FOOT_SIZE \ + (align_offset(chunk2mem(0)) + pad_request(sizeof(struct malloc_segment)) + \ + MIN_CHUNK_SIZE) /* ------------------------------- Hooks -------------------------------- */ @@ -2793,20 +2887,25 @@ static int has_segment_link(mstate m, msegmentptr ss) { anything you like. */ -#if USE_LOCKS -#define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) -#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } -#else /* USE_LOCKS */ + #if USE_LOCKS + #define PREACTION(M) ((use_lock(M)) ? ACQUIRE_LOCK(&(M)->mutex) : 0) + #define POSTACTION(M) \ + { \ + \ + if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); \ + \ + } + #else /* USE_LOCKS */ -#ifndef PREACTION -#define PREACTION(M) (0) -#endif /* PREACTION */ + #ifndef PREACTION + #define PREACTION(M) (0) + #endif /* PREACTION */ -#ifndef POSTACTION -#define POSTACTION(M) -#endif /* POSTACTION */ + #ifndef POSTACTION + #define POSTACTION(M) + #endif /* POSTACTION */ -#endif /* USE_LOCKS */ + #endif /* USE_LOCKS */ /* CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. @@ -2816,7 +2915,7 @@ static int has_segment_link(mstate m, msegmentptr ss) { useful in custom actions that try to help diagnose errors. */ -#if PROCEED_ON_ERROR + #if PROCEED_ON_ERROR /* A count of the number of corruption errors causing resets */ int malloc_corruption_error_count; @@ -2824,211 +2923,240 @@ int malloc_corruption_error_count; /* default corruption action */ static void reset_on_error(mstate m); -#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) -#define USAGE_ERROR_ACTION(m, p) - -#else /* PROCEED_ON_ERROR */ + #define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) + #define USAGE_ERROR_ACTION(m, p) -#ifndef CORRUPTION_ERROR_ACTION -#define CORRUPTION_ERROR_ACTION(m) ABORT -#endif /* CORRUPTION_ERROR_ACTION */ + #else /* PROCEED_ON_ERROR */ -#ifndef USAGE_ERROR_ACTION -#define USAGE_ERROR_ACTION(m,p) ABORT -#endif /* USAGE_ERROR_ACTION */ + #ifndef CORRUPTION_ERROR_ACTION + #define CORRUPTION_ERROR_ACTION(m) ABORT + #endif /* CORRUPTION_ERROR_ACTION */ -#endif /* PROCEED_ON_ERROR */ + #ifndef USAGE_ERROR_ACTION + #define USAGE_ERROR_ACTION(m, p) ABORT + #endif /* USAGE_ERROR_ACTION */ + #endif /* PROCEED_ON_ERROR */ /* -------------------------- Debugging setup ---------------------------- */ -#if ! DEBUG + #if !DEBUG -#define check_free_chunk(M,P) -#define check_inuse_chunk(M,P) -#define check_malloced_chunk(M,P,N) -#define check_mmapped_chunk(M,P) -#define check_malloc_state(M) -#define check_top_chunk(M,P) + #define check_free_chunk(M, P) + #define check_inuse_chunk(M, P) + #define check_malloced_chunk(M, P, N) + #define check_mmapped_chunk(M, P) + #define check_malloc_state(M) + #define check_top_chunk(M, P) -#else /* DEBUG */ -#define check_free_chunk(M,P) do_check_free_chunk(M,P) -#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) -#define check_top_chunk(M,P) do_check_top_chunk(M,P) -#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) -#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) -#define check_malloc_state(M) do_check_malloc_state(M) + #else /* DEBUG */ + #define check_free_chunk(M, P) do_check_free_chunk(M, P) + #define check_inuse_chunk(M, P) do_check_inuse_chunk(M, P) + #define check_top_chunk(M, P) do_check_top_chunk(M, P) + #define check_malloced_chunk(M, P, N) do_check_malloced_chunk(M, P, N) + #define check_mmapped_chunk(M, P) do_check_mmapped_chunk(M, P) + #define check_malloc_state(M) do_check_malloc_state(M) static void do_check_any_chunk(mstate m, mchunkptr p); static void do_check_top_chunk(mstate m, mchunkptr p); static void do_check_mmapped_chunk(mstate m, mchunkptr p); static void do_check_inuse_chunk(mstate m, mchunkptr p); static void do_check_free_chunk(mstate m, mchunkptr p); -static void do_check_malloced_chunk(mstate m, void* mem, size_t s); +static void do_check_malloced_chunk(mstate m, void *mem, size_t s); static void do_check_tree(mstate m, tchunkptr t); static void do_check_treebin(mstate m, bindex_t i); static void do_check_smallbin(mstate m, bindex_t i); static void do_check_malloc_state(mstate m); static int bin_find(mstate m, mchunkptr x); static size_t traverse_and_check(mstate m); -#endif /* DEBUG */ + #endif /* DEBUG */ /* ---------------------------- Indexing Bins ---------------------------- */ -#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) -#define small_index(s) (bindex_t)((s) >> SMALLBIN_SHIFT) -#define small_index2size(i) ((i) << SMALLBIN_SHIFT) -#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) - -/* addressing by index. See above about smallbin repositioning */ -#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) -#define treebin_at(M,i) (&((M)->treebins[i])) - -/* assign tree index for size S to variable I. Use x86 asm if possible */ -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -#define compute_tree_index(S, I)\ -{\ - unsigned int X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int K = (unsigned) sizeof(X)*__CHAR_BIT__ - 1 - (unsigned) __builtin_clz(X); \ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ - }\ -} - -#elif defined (__INTEL_COMPILER) -#define compute_tree_index(S, I)\ -{\ - size_t X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int K = _bit_scan_reverse (X); \ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ - }\ -} + #define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) + #define small_index(s) (bindex_t)((s) >> SMALLBIN_SHIFT) + #define small_index2size(i) ((i) << SMALLBIN_SHIFT) + #define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) + + /* addressing by index. See above about smallbin repositioning */ + #define smallbin_at(M, i) ((sbinptr)((char *)&((M)->smallbins[(i) << 1]))) + #define treebin_at(M, i) (&((M)->treebins[i])) + + /* assign tree index for size S to variable I. Use x86 asm if possible */ + #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + #define compute_tree_index(S, I) \ + { \ + \ + unsigned int X = S >> TREEBIN_SHIFT; \ + if (X == 0) \ + I = 0; \ + else if (X > 0xFFFF) \ + I = NTREEBINS - 1; \ + else { \ + \ + unsigned int K = (unsigned)sizeof(X) * __CHAR_BIT__ - 1 - \ + (unsigned)__builtin_clz(X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1))); \ + \ + } \ + \ + } -#elif defined(_MSC_VER) && _MSC_VER>=1300 -#define compute_tree_index(S, I)\ -{\ - size_t X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int K;\ - _BitScanReverse((DWORD *) &K, (DWORD) X);\ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ - }\ -} + #elif defined(__INTEL_COMPILER) + #define compute_tree_index(S, I) \ + { \ + \ + size_t X = S >> TREEBIN_SHIFT; \ + if (X == 0) \ + I = 0; \ + else if (X > 0xFFFF) \ + I = NTREEBINS - 1; \ + else { \ + \ + unsigned int K = _bit_scan_reverse(X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1))); \ + \ + } \ + \ + } -#else /* GNUC */ -#define compute_tree_index(S, I)\ -{\ - size_t X = S >> TREEBIN_SHIFT;\ - if (X == 0)\ - I = 0;\ - else if (X > 0xFFFF)\ - I = NTREEBINS-1;\ - else {\ - unsigned int Y = (unsigned int)X;\ - unsigned int N = ((Y - 0x100) >> 16) & 8;\ - unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ - N += K;\ - N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ - K = 14 - N + ((Y <<= K) >> 15);\ - I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ - }\ -} -#endif /* GNUC */ + #elif defined(_MSC_VER) && _MSC_VER >= 1300 + #define compute_tree_index(S, I) \ + { \ + \ + size_t X = S >> TREEBIN_SHIFT; \ + if (X == 0) \ + I = 0; \ + else if (X > 0xFFFF) \ + I = NTREEBINS - 1; \ + else { \ + \ + unsigned int K; \ + _BitScanReverse((DWORD *)&K, (DWORD)X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1))); \ + \ + } \ + \ + } -/* Bit representing maximum resolved size in a treebin at i */ -#define bit_for_tree_index(i) \ - (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) + #else /* GNUC */ + #define compute_tree_index(S, I) \ + { \ + \ + size_t X = S >> TREEBIN_SHIFT; \ + if (X == 0) \ + I = 0; \ + else if (X > 0xFFFF) \ + I = NTREEBINS - 1; \ + else { \ + \ + unsigned int Y = (unsigned int)X; \ + unsigned int N = ((Y - 0x100) >> 16) & 8; \ + unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4; \ + N += K; \ + N += K = (((Y <<= K) - 0x4000) >> 16) & 2; \ + K = 14 - N + ((Y <<= K) >> 15); \ + I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1)); \ + \ + } \ + \ + } + #endif /* GNUC */ -/* Shift placing maximum resolved bit in a treebin at i as sign bit */ -#define leftshift_for_tree_index(i) \ - ((i == NTREEBINS-1)? 0 : \ - ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) + /* Bit representing maximum resolved size in a treebin at i */ + #define bit_for_tree_index(i) \ + (i == NTREEBINS - 1) ? (SIZE_T_BITSIZE - 1) \ + : (((i) >> 1) + TREEBIN_SHIFT - 2) -/* The size of the smallest chunk held in bin with index i */ -#define minsize_for_tree_index(i) \ - ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ - (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) + /* Shift placing maximum resolved bit in a treebin at i as sign bit */ + #define leftshift_for_tree_index(i) \ + ((i == NTREEBINS - 1) \ + ? 0 \ + : ((SIZE_T_BITSIZE - SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) + /* The size of the smallest chunk held in bin with index i */ + #define minsize_for_tree_index(i) \ + ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ + (((size_t)((i)&SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) -/* ------------------------ Operations on bin maps ----------------------- */ + /* ------------------------ Operations on bin maps ----------------------- */ -/* bit corresponding to given index */ -#define idx2bit(i) ((binmap_t)(1) << (i)) + /* bit corresponding to given index */ + #define idx2bit(i) ((binmap_t)(1) << (i)) -/* Mark/Clear bits with given index */ -#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) -#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) -#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) + /* Mark/Clear bits with given index */ + #define mark_smallmap(M, i) ((M)->smallmap |= idx2bit(i)) + #define clear_smallmap(M, i) ((M)->smallmap &= ~idx2bit(i)) + #define smallmap_is_marked(M, i) ((M)->smallmap & idx2bit(i)) -#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) -#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) -#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) + #define mark_treemap(M, i) ((M)->treemap |= idx2bit(i)) + #define clear_treemap(M, i) ((M)->treemap &= ~idx2bit(i)) + #define treemap_is_marked(M, i) ((M)->treemap & idx2bit(i)) -/* isolate the least set bit of a bitmap */ -#define least_bit(x) ((x) & -(x)) + /* isolate the least set bit of a bitmap */ + #define least_bit(x) ((x) & -(x)) -/* mask with all bits to left of least bit of x on */ -#define left_bits(x) ((x<<1) | -(x<<1)) + /* mask with all bits to left of least bit of x on */ + #define left_bits(x) ((x << 1) | -(x << 1)) -/* mask with all bits to left of or equal to least bit of x on */ -#define same_or_left_bits(x) ((x) | -(x)) + /* mask with all bits to left of or equal to least bit of x on */ + #define same_or_left_bits(x) ((x) | -(x)) /* index corresponding to given bit. Use x86 asm if possible */ -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -#define compute_bit2idx(X, I)\ -{\ - unsigned int J;\ - J = __builtin_ctz(X); \ - I = (bindex_t)J;\ -} + #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + #define compute_bit2idx(X, I) \ + { \ + \ + unsigned int J; \ + J = __builtin_ctz(X); \ + I = (bindex_t)J; \ + \ + } -#elif defined (__INTEL_COMPILER) -#define compute_bit2idx(X, I)\ -{\ - unsigned int J;\ - J = _bit_scan_forward (X); \ - I = (bindex_t)J;\ -} + #elif defined(__INTEL_COMPILER) + #define compute_bit2idx(X, I) \ + { \ + \ + unsigned int J; \ + J = _bit_scan_forward(X); \ + I = (bindex_t)J; \ + \ + } -#elif defined(_MSC_VER) && _MSC_VER>=1300 -#define compute_bit2idx(X, I)\ -{\ - unsigned int J;\ - _BitScanForward((DWORD *) &J, X);\ - I = (bindex_t)J;\ -} + #elif defined(_MSC_VER) && _MSC_VER >= 1300 + #define compute_bit2idx(X, I) \ + { \ + \ + unsigned int J; \ + _BitScanForward((DWORD *)&J, X); \ + I = (bindex_t)J; \ + \ + } -#elif USE_BUILTIN_FFS -#define compute_bit2idx(X, I) I = ffs(X)-1 - -#else -#define compute_bit2idx(X, I)\ -{\ - unsigned int Y = X - 1;\ - unsigned int K = Y >> (16-4) & 16;\ - unsigned int N = K; Y >>= K;\ - N += K = Y >> (8-3) & 8; Y >>= K;\ - N += K = Y >> (4-2) & 4; Y >>= K;\ - N += K = Y >> (2-1) & 2; Y >>= K;\ - N += K = Y >> (1-0) & 1; Y >>= K;\ - I = (bindex_t)(N + Y);\ -} -#endif /* GNUC */ + #elif USE_BUILTIN_FFS + #define compute_bit2idx(X, I) I = ffs(X) - 1 + #else + #define compute_bit2idx(X, I) \ + { \ + \ + unsigned int Y = X - 1; \ + unsigned int K = Y >> (16 - 4) & 16; \ + unsigned int N = K; \ + Y >>= K; \ + N += K = Y >> (8 - 3) & 8; \ + Y >>= K; \ + N += K = Y >> (4 - 2) & 4; \ + Y >>= K; \ + N += K = Y >> (2 - 1) & 2; \ + Y >>= K; \ + N += K = Y >> (1 - 0) & 1; \ + Y >>= K; \ + I = (bindex_t)(N + Y); \ + \ + } + #endif /* GNUC */ /* ----------------------- Runtime Check Support ------------------------- */ @@ -3058,122 +3186,142 @@ static size_t traverse_and_check(mstate m); next, etc). This turns out to be cheaper than relying on hashes. */ -#if !INSECURE -/* Check if address a is at least as high as any from MORECORE or MMAP */ -#define ok_address(M, a) ((char*)(a) >= (M)->least_addr) -/* Check if address of next chunk n is higher than base chunk p */ -#define ok_next(p, n) ((char*)(p) < (char*)(n)) -/* Check if p has inuse status */ -#define ok_inuse(p) is_inuse(p) -/* Check if p has its pinuse bit on */ -#define ok_pinuse(p) pinuse(p) - -#else /* !INSECURE */ -#define ok_address(M, a) (1) -#define ok_next(b, n) (1) -#define ok_inuse(p) (1) -#define ok_pinuse(p) (1) -#endif /* !INSECURE */ - -#if (FOOTERS && !INSECURE) -/* Check if (alleged) mstate m has expected magic field */ -#define ok_magic(M) ((M)->magic == mparams.magic) -#else /* (FOOTERS && !INSECURE) */ -#define ok_magic(M) (1) -#endif /* (FOOTERS && !INSECURE) */ - -/* In gcc, use __builtin_expect to minimize impact of checks */ -#if !INSECURE -#if defined(__GNUC__) && __GNUC__ >= 3 -#define RTCHECK(e) __builtin_expect(e, 1) -#else /* GNUC */ -#define RTCHECK(e) (e) -#endif /* GNUC */ -#else /* !INSECURE */ -#define RTCHECK(e) (1) -#endif /* !INSECURE */ + #if !INSECURE + /* Check if address a is at least as high as any from MORECORE or MMAP */ + #define ok_address(M, a) ((char *)(a) >= (M)->least_addr) + /* Check if address of next chunk n is higher than base chunk p */ + #define ok_next(p, n) ((char *)(p) < (char *)(n)) + /* Check if p has inuse status */ + #define ok_inuse(p) is_inuse(p) + /* Check if p has its pinuse bit on */ + #define ok_pinuse(p) pinuse(p) + + #else /* !INSECURE */ + #define ok_address(M, a) (1) + #define ok_next(b, n) (1) + #define ok_inuse(p) (1) + #define ok_pinuse(p) (1) + #endif /* !INSECURE */ + + #if (FOOTERS && !INSECURE) + /* Check if (alleged) mstate m has expected magic field */ + #define ok_magic(M) ((M)->magic == mparams.magic) + #else /* (FOOTERS && !INSECURE) */ + #define ok_magic(M) (1) + #endif /* (FOOTERS && !INSECURE) */ + + /* In gcc, use __builtin_expect to minimize impact of checks */ + #if !INSECURE + #if defined(__GNUC__) && __GNUC__ >= 3 + #define RTCHECK(e) __builtin_expect(e, 1) + #else /* GNUC */ + #define RTCHECK(e) (e) + #endif /* GNUC */ + #else /* !INSECURE */ + #define RTCHECK(e) (1) + #endif /* !INSECURE */ /* macros to set up inuse chunks with or without footers */ -#if !FOOTERS + #if !FOOTERS -#define mark_inuse_foot(M,p,s) + #define mark_inuse_foot(M, p, s) -/* Macros for setting head/foot of non-mmapped chunks */ + /* Macros for setting head/foot of non-mmapped chunks */ -/* Set cinuse bit and pinuse bit of next chunk */ -#define set_inuse(M,p,s)\ - ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ - ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + /* Set cinuse bit and pinuse bit of next chunk */ + #define set_inuse(M, p, s) \ + ((p)->head = (((p)->head & PINUSE_BIT) | s | CINUSE_BIT), \ + ((mchunkptr)(((char *)(p)) + (s)))->head |= PINUSE_BIT) -/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ -#define set_inuse_and_pinuse(M,p,s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ - ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + /* Set cinuse and pinuse of this chunk and pinuse of next chunk */ + #define set_inuse_and_pinuse(M, p, s) \ + ((p)->head = (s | PINUSE_BIT | CINUSE_BIT), \ + ((mchunkptr)(((char *)(p)) + (s)))->head |= PINUSE_BIT) -/* Set size, cinuse and pinuse bit of this chunk */ -#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) + /* Set size, cinuse and pinuse bit of this chunk */ + #define set_size_and_pinuse_of_inuse_chunk(M, p, s) \ + ((p)->head = (s | PINUSE_BIT | CINUSE_BIT)) -#else /* FOOTERS */ + #else /* FOOTERS */ -/* Set foot of inuse chunk to be xor of mstate and seed */ -#define mark_inuse_foot(M,p,s)\ - (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) + /* Set foot of inuse chunk to be xor of mstate and seed */ + #define mark_inuse_foot(M, p, s) \ + (((mchunkptr)((char *)(p) + (s)))->prev_foot = \ + ((size_t)(M) ^ mparams.magic)) -#define get_mstate_for(p)\ - ((mstate)(((mchunkptr)((char*)(p) +\ - (chunksize(p))))->prev_foot ^ mparams.magic)) + #define get_mstate_for(p) \ + ((mstate)(((mchunkptr)((char *)(p) + (chunksize(p))))->prev_foot ^ \ + mparams.magic)) -#define set_inuse(M,p,s)\ - ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ - (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ - mark_inuse_foot(M,p,s)) + #define set_inuse(M, p, s) \ + ((p)->head = (((p)->head & PINUSE_BIT) | s | CINUSE_BIT), \ + (((mchunkptr)(((char *)(p)) + (s)))->head |= PINUSE_BIT), \ + mark_inuse_foot(M, p, s)) -#define set_inuse_and_pinuse(M,p,s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ - (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ - mark_inuse_foot(M,p,s)) + #define set_inuse_and_pinuse(M, p, s) \ + ((p)->head = (s | PINUSE_BIT | CINUSE_BIT), \ + (((mchunkptr)(((char *)(p)) + (s)))->head |= PINUSE_BIT), \ + mark_inuse_foot(M, p, s)) -#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ - ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ - mark_inuse_foot(M, p, s)) + #define set_size_and_pinuse_of_inuse_chunk(M, p, s) \ + ((p)->head = (s | PINUSE_BIT | CINUSE_BIT), mark_inuse_foot(M, p, s)) -#endif /* !FOOTERS */ + #endif /* !FOOTERS */ /* ---------------------------- setting mparams -------------------------- */ -#if LOCK_AT_FORK -static void pre_fork(void) { ACQUIRE_LOCK(&(gm)->mutex); } -static void post_fork_parent(void) { RELEASE_LOCK(&(gm)->mutex); } -static void post_fork_child(void) { INITIAL_LOCK(&(gm)->mutex); } -#endif /* LOCK_AT_FORK */ + #if LOCK_AT_FORK +static void pre_fork(void) { + + ACQUIRE_LOCK(&(gm)->mutex); + +} + +static void post_fork_parent(void) { + + RELEASE_LOCK(&(gm)->mutex); + +} + +static void post_fork_child(void) { + + INITIAL_LOCK(&(gm)->mutex); + +} + + #endif /* LOCK_AT_FORK */ /* Initialize mparams */ static int init_mparams(void) { -#ifdef NEED_GLOBAL_LOCK_INIT - if (malloc_global_mutex_status <= 0) - init_malloc_global_mutex(); -#endif + + #ifdef NEED_GLOBAL_LOCK_INIT + if (malloc_global_mutex_status <= 0) init_malloc_global_mutex(); + #endif ACQUIRE_MALLOC_GLOBAL_LOCK(); if (mparams.magic == 0) { + size_t magic; size_t psize; size_t gsize; -#ifndef WIN32 + #ifndef WIN32 psize = malloc_getpagesize; - gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); -#else /* WIN32 */ + gsize = ((DEFAULT_GRANULARITY != 0) ? DEFAULT_GRANULARITY : psize); + #else /* WIN32 */ { + SYSTEM_INFO system_info; GetSystemInfo(&system_info); psize = system_info.dwPageSize; - gsize = ((DEFAULT_GRANULARITY != 0)? - DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); + gsize = + ((DEFAULT_GRANULARITY != 0) ? DEFAULT_GRANULARITY + : system_info.dwAllocationGranularity); + } -#endif /* WIN32 */ + + #endif /* WIN32 */ /* Sanity-check configuration: size_t must be unsigned and as wide as pointer type. @@ -3181,187 +3329,216 @@ static int init_mparams(void) { alignment must be at least 8. Alignment, min chunk size, and page size must all be powers of 2. */ - if ((sizeof(size_t) != sizeof(char*)) || - (MAX_SIZE_T < MIN_CHUNK_SIZE) || - (sizeof(int) < 4) || - (MALLOC_ALIGNMENT < (size_t)8U) || - ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || - ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || - ((gsize & (gsize-SIZE_T_ONE)) != 0) || - ((psize & (psize-SIZE_T_ONE)) != 0)) + if ((sizeof(size_t) != sizeof(char *)) || (MAX_SIZE_T < MIN_CHUNK_SIZE) || + (sizeof(int) < 4) || (MALLOC_ALIGNMENT < (size_t)8U) || + ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT - SIZE_T_ONE)) != 0) || + ((MCHUNK_SIZE & (MCHUNK_SIZE - SIZE_T_ONE)) != 0) || + ((gsize & (gsize - SIZE_T_ONE)) != 0) || + ((psize & (psize - SIZE_T_ONE)) != 0)) ABORT; mparams.granularity = gsize; mparams.page_size = psize; mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; -#if MORECORE_CONTIGUOUS - mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; -#else /* MORECORE_CONTIGUOUS */ - mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; -#endif /* MORECORE_CONTIGUOUS */ - -#if !ONLY_MSPACES + #if MORECORE_CONTIGUOUS + mparams.default_mflags = USE_LOCK_BIT | USE_MMAP_BIT; + #else /* MORECORE_CONTIGUOUS */ + mparams.default_mflags = + USE_LOCK_BIT | USE_MMAP_BIT | USE_NONCONTIGUOUS_BIT; + #endif /* MORECORE_CONTIGUOUS */ + + #if !ONLY_MSPACES /* Set up lock for main malloc area */ gm->mflags = mparams.default_mflags; (void)INITIAL_LOCK(&gm->mutex); -#endif -#if LOCK_AT_FORK + #endif + #if LOCK_AT_FORK pthread_atfork(&pre_fork, &post_fork_parent, &post_fork_child); -#endif + #endif { -#if USE_DEV_RANDOM - int fd; + + #if USE_DEV_RANDOM + int fd; unsigned char buf[sizeof(size_t)]; /* Try to use /dev/urandom, else fall back on using time */ if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && read(fd, buf, sizeof(buf)) == sizeof(buf)) { - magic = *((size_t *) buf); + + magic = *((size_t *)buf); close(fd); - } - else -#endif /* USE_DEV_RANDOM */ -#ifdef WIN32 - magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); -#elif defined(LACKS_TIME_H) + + } else + + #endif /* USE_DEV_RANDOM */ + #ifdef WIN32 + magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); + #elif defined(LACKS_TIME_H) magic = (size_t)&magic ^ (size_t)0x55555555U; -#else + #else magic = (size_t)(time(0) ^ (size_t)0x55555555U); -#endif - magic |= (size_t)8U; /* ensure nonzero */ - magic &= ~(size_t)7U; /* improve chances of fault for bad values */ + #endif + magic |= (size_t)8U; /* ensure nonzero */ + magic &= ~(size_t)7U; /* improve chances of fault for bad values */ /* Until memory modes commonly available, use volatile-write */ (*(volatile size_t *)(&(mparams.magic))) = magic; + } + } RELEASE_MALLOC_GLOBAL_LOCK(); return 1; + } /* support for mallopt */ static int change_mparam(int param_number, int value) { + size_t val; ensure_initialization(); - val = (value == -1)? MAX_SIZE_T : (size_t)value; - switch(param_number) { - case M_TRIM_THRESHOLD: - mparams.trim_threshold = val; - return 1; - case M_GRANULARITY: - if (val >= mparams.page_size && ((val & (val-1)) == 0)) { - mparams.granularity = val; + val = (value == -1) ? MAX_SIZE_T : (size_t)value; + switch (param_number) { + + case M_TRIM_THRESHOLD: + mparams.trim_threshold = val; return 1; - } - else + case M_GRANULARITY: + if (val >= mparams.page_size && ((val & (val - 1)) == 0)) { + + mparams.granularity = val; + return 1; + + } else + + return 0; + case M_MMAP_THRESHOLD: + mparams.mmap_threshold = val; + return 1; + default: return 0; - case M_MMAP_THRESHOLD: - mparams.mmap_threshold = val; - return 1; - default: - return 0; + } + } -#if DEBUG + #if DEBUG /* ------------------------- Debugging Support --------------------------- */ /* Check properties of any chunk, whether free, inuse, mmapped etc */ static void do_check_any_chunk(mstate m, mchunkptr p) { + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(ok_address(m, p)); + } /* Check properties of top chunk */ static void do_check_top_chunk(mstate m, mchunkptr p) { - msegmentptr sp = segment_holding(m, (char*)p); - size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ + + msegmentptr sp = segment_holding(m, (char *)p); + size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ assert(sp != 0); assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(ok_address(m, p)); assert(sz == m->topsize); assert(sz > 0); - assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); + assert(sz == ((sp->base + sp->size) - (char *)p) - TOP_FOOT_SIZE); assert(pinuse(p)); assert(!pinuse(chunk_plus_offset(p, sz))); + } /* Check properties of (inuse) mmapped chunks */ static void do_check_mmapped_chunk(mstate m, mchunkptr p) { - size_t sz = chunksize(p); + + size_t sz = chunksize(p); size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD); assert(is_mmapped(p)); assert(use_mmap(m)); assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(ok_address(m, p)); assert(!is_small(sz)); - assert((len & (mparams.page_size-SIZE_T_ONE)) == 0); + assert((len & (mparams.page_size - SIZE_T_ONE)) == 0); assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); - assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); + assert(chunk_plus_offset(p, sz + SIZE_T_SIZE)->head == 0); + } /* Check properties of inuse chunks */ static void do_check_inuse_chunk(mstate m, mchunkptr p) { + do_check_any_chunk(m, p); assert(is_inuse(p)); assert(next_pinuse(p)); /* If not pinuse and not mmapped, previous chunk has OK offset */ assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); - if (is_mmapped(p)) - do_check_mmapped_chunk(m, p); + if (is_mmapped(p)) do_check_mmapped_chunk(m, p); + } /* Check properties of free chunks */ static void do_check_free_chunk(mstate m, mchunkptr p) { - size_t sz = chunksize(p); + + size_t sz = chunksize(p); mchunkptr next = chunk_plus_offset(p, sz); do_check_any_chunk(m, p); assert(!is_inuse(p)); assert(!next_pinuse(p)); - assert (!is_mmapped(p)); + assert(!is_mmapped(p)); if (p != m->dv && p != m->top) { + if (sz >= MIN_CHUNK_SIZE) { + assert((sz & CHUNK_ALIGN_MASK) == 0); assert(is_aligned(chunk2mem(p))); assert(next->prev_foot == sz); assert(pinuse(p)); - assert (next == m->top || is_inuse(next)); + assert(next == m->top || is_inuse(next)); assert(p->fd->bk == p); assert(p->bk->fd == p); - } - else /* markers are always of size SIZE_T_SIZE */ + + } else /* markers are always of size SIZE_T_SIZE */ + assert(sz == SIZE_T_SIZE); + } + } /* Check properties of malloced chunks at the point they are malloced */ -static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { +static void do_check_malloced_chunk(mstate m, void *mem, size_t s) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); - size_t sz = p->head & ~INUSE_BITS; + size_t sz = p->head & ~INUSE_BITS; do_check_inuse_chunk(m, p); assert((sz & CHUNK_ALIGN_MASK) == 0); assert(sz >= MIN_CHUNK_SIZE); assert(sz >= s); /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); + } + } /* Check a tree and its subtrees. */ static void do_check_tree(mstate m, tchunkptr t) { + tchunkptr head = 0; tchunkptr u = t; - bindex_t tindex = t->index; - size_t tsize = chunksize(t); - bindex_t idx; + bindex_t tindex = t->index; + size_t tsize = chunksize(t); + bindex_t idx; compute_tree_index(tsize, idx); assert(tindex == idx); assert(tsize >= MIN_LARGE_SIZE); assert(tsize >= minsize_for_tree_index(idx)); - assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); + assert((idx == NTREEBINS - 1) || (tsize < minsize_for_tree_index((idx + 1)))); - do { /* traverse through chain of same-sized nodes */ + do { /* traverse through chain of same-sized nodes */ do_check_any_chunk(m, ((mchunkptr)u)); assert(u->index == tindex); assert(chunksize(u) == tsize); @@ -3370,56 +3547,72 @@ static void do_check_tree(mstate m, tchunkptr t) { assert(u->fd->bk == u); assert(u->bk->fd == u); if (u->parent == 0) { + assert(u->child[0] == 0); assert(u->child[1] == 0); - } - else { - assert(head == 0); /* only one node on chain has parent */ + + } else { + + assert(head == 0); /* only one node on chain has parent */ head = u; assert(u->parent != u); - assert (u->parent->child[0] == u || - u->parent->child[1] == u || - *((tbinptr*)(u->parent)) == u); + assert(u->parent->child[0] == u || u->parent->child[1] == u || + *((tbinptr *)(u->parent)) == u); if (u->child[0] != 0) { + assert(u->child[0]->parent == u); assert(u->child[0] != u); do_check_tree(m, u->child[0]); + } + if (u->child[1] != 0) { + assert(u->child[1]->parent == u); assert(u->child[1] != u); do_check_tree(m, u->child[1]); + } + if (u->child[0] != 0 && u->child[1] != 0) { + assert(chunksize(u->child[0]) < chunksize(u->child[1])); + } + } + u = u->fd; + } while (u != t); + assert(head != 0); + } /* Check all the chunks in a treebin. */ static void do_check_treebin(mstate m, bindex_t i) { - tbinptr* tb = treebin_at(m, i); + + tbinptr * tb = treebin_at(m, i); tchunkptr t = *tb; - int empty = (m->treemap & (1U << i)) == 0; - if (t == 0) - assert(empty); - if (!empty) - do_check_tree(m, t); + int empty = (m->treemap & (1U << i)) == 0; + if (t == 0) assert(empty); + if (!empty) do_check_tree(m, t); + } /* Check all the chunks in a smallbin. */ static void do_check_smallbin(mstate m, bindex_t i) { - sbinptr b = smallbin_at(m, i); - mchunkptr p = b->bk; + + sbinptr b = smallbin_at(m, i); + mchunkptr p = b->bk; unsigned int empty = (m->smallmap & (1U << i)) == 0; - if (p == b) - assert(empty); + if (p == b) assert(empty); if (!empty) { + for (; p != b; p = p->bk) { - size_t size = chunksize(p); + + size_t size = chunksize(p); mchunkptr q; /* each chunk claims to be free */ do_check_free_chunk(m, p); @@ -3428,324 +3621,435 @@ static void do_check_smallbin(mstate m, bindex_t i) { assert(p->bk == b || chunksize(p->bk) == chunksize(p)); /* chunk is followed by an inuse chunk */ q = next_chunk(p); - if (q->head != FENCEPOST_HEAD) - do_check_inuse_chunk(m, q); + if (q->head != FENCEPOST_HEAD) do_check_inuse_chunk(m, q); + } + } + } /* Find x in a bin. Used in other check functions. */ static int bin_find(mstate m, mchunkptr x) { + size_t size = chunksize(x); if (is_small(size)) { + bindex_t sidx = small_index(size); - sbinptr b = smallbin_at(m, sidx); + sbinptr b = smallbin_at(m, sidx); if (smallmap_is_marked(m, sidx)) { + mchunkptr p = b; do { - if (p == x) - return 1; + + if (p == x) return 1; + } while ((p = p->fd) != b); + } - } - else { + + } else { + bindex_t tidx; compute_tree_index(size, tidx); if (treemap_is_marked(m, tidx)) { + tchunkptr t = *treebin_at(m, tidx); - size_t sizebits = size << leftshift_for_tree_index(tidx); + size_t sizebits = size << leftshift_for_tree_index(tidx); while (t != 0 && chunksize(t) != size) { - t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + + t = t->child[(sizebits >> (SIZE_T_BITSIZE - SIZE_T_ONE)) & 1]; sizebits <<= 1; + } + if (t != 0) { + tchunkptr u = t; do { - if (u == (tchunkptr)x) - return 1; + + if (u == (tchunkptr)x) return 1; + } while ((u = u->fd) != t); + } + } + } + return 0; + } /* Traverse each chunk and check it; return total */ static size_t traverse_and_check(mstate m) { + size_t sum = 0; if (is_initialized(m)) { + msegmentptr s = &m->seg; sum += m->topsize + TOP_FOOT_SIZE; while (s != 0) { + mchunkptr q = align_as_chunk(s->base); mchunkptr lastq = 0; assert(pinuse(q)); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { + while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { + sum += chunksize(q); if (is_inuse(q)) { + assert(!bin_find(m, q)); do_check_inuse_chunk(m, q); - } - else { + + } else { + assert(q == m->dv || bin_find(m, q)); - assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ + assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ do_check_free_chunk(m, q); + } + lastq = q; q = next_chunk(q); + } + s = s->next; + } + } + return sum; -} +} /* Check all properties of malloc_state. */ static void do_check_malloc_state(mstate m) { + bindex_t i; - size_t total; + size_t total; /* check bins */ for (i = 0; i < NSMALLBINS; ++i) do_check_smallbin(m, i); for (i = 0; i < NTREEBINS; ++i) do_check_treebin(m, i); - if (m->dvsize != 0) { /* check dv chunk */ + if (m->dvsize != 0) { /* check dv chunk */ do_check_any_chunk(m, m->dv); assert(m->dvsize == chunksize(m->dv)); assert(m->dvsize >= MIN_CHUNK_SIZE); assert(bin_find(m, m->dv) == 0); + } - if (m->top != 0) { /* check top chunk */ + if (m->top != 0) { /* check top chunk */ do_check_top_chunk(m, m->top); /*assert(m->topsize == chunksize(m->top)); redundant */ assert(m->topsize > 0); assert(bin_find(m, m->top) == 0); + } total = traverse_and_check(m); assert(total <= m->footprint); assert(m->footprint <= m->max_footprint); + } -#endif /* DEBUG */ + + #endif /* DEBUG */ /* ----------------------------- statistics ------------------------------ */ -#if !NO_MALLINFO + #if !NO_MALLINFO static struct mallinfo internal_mallinfo(mstate m) { - struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + struct mallinfo nm = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; ensure_initialization(); if (!PREACTION(m)) { + check_malloc_state(m); if (is_initialized(m)) { - size_t nfree = SIZE_T_ONE; /* top always free */ - size_t mfree = m->topsize + TOP_FOOT_SIZE; - size_t sum = mfree; + + size_t nfree = SIZE_T_ONE; /* top always free */ + size_t mfree = m->topsize + TOP_FOOT_SIZE; + size_t sum = mfree; msegmentptr s = &m->seg; while (s != 0) { + mchunkptr q = align_as_chunk(s->base); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { + while (segment_holds(s, q) && q != m->top && + q->head != FENCEPOST_HEAD) { + size_t sz = chunksize(q); sum += sz; if (!is_inuse(q)) { + mfree += sz; ++nfree; + } + q = next_chunk(q); + } + s = s->next; + } - nm.arena = sum; - nm.ordblks = nfree; - nm.hblkhd = m->footprint - sum; - nm.usmblks = m->max_footprint; + nm.arena = sum; + nm.ordblks = nfree; + nm.hblkhd = m->footprint - sum; + nm.usmblks = m->max_footprint; nm.uordblks = m->footprint - mfree; nm.fordblks = mfree; nm.keepcost = m->topsize; + } POSTACTION(m); + } + return nm; + } -#endif /* !NO_MALLINFO */ -#if !NO_MALLOC_STATS + #endif /* !NO_MALLINFO */ + + #if !NO_MALLOC_STATS static void internal_malloc_stats(mstate m) { + ensure_initialization(); if (!PREACTION(m)) { + size_t maxfp = 0; size_t fp = 0; size_t used = 0; check_malloc_state(m); if (is_initialized(m)) { + msegmentptr s = &m->seg; maxfp = m->max_footprint; fp = m->footprint; used = fp - (m->topsize + TOP_FOOT_SIZE); while (s != 0) { + mchunkptr q = align_as_chunk(s->base); - while (segment_holds(s, q) && - q != m->top && q->head != FENCEPOST_HEAD) { - if (!is_inuse(q)) - used -= chunksize(q); + while (segment_holds(s, q) && q != m->top && + q->head != FENCEPOST_HEAD) { + + if (!is_inuse(q)) used -= chunksize(q); q = next_chunk(q); + } + s = s->next; + } + } - POSTACTION(m); /* drop lock */ + + POSTACTION(m); /* drop lock */ fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); + } + } -#endif /* NO_MALLOC_STATS */ -/* ----------------------- Operations on smallbins ----------------------- */ + #endif /* NO_MALLOC_STATS */ -/* - Various forms of linking and unlinking are defined as macros. Even - the ones for trees, which are very long but have very short typical - paths. This is ugly but reduces reliance on inlining support of - compilers. -*/ + /* ----------------------- Operations on smallbins ----------------------- */ -/* Link a free chunk into a smallbin */ -#define insert_small_chunk(M, P, S) {\ - bindex_t I = small_index(S);\ - mchunkptr B = smallbin_at(M, I);\ - mchunkptr F = B;\ - assert(S >= MIN_CHUNK_SIZE);\ - if (!smallmap_is_marked(M, I))\ - mark_smallmap(M, I);\ - else if (RTCHECK(ok_address(M, B->fd)))\ - F = B->fd;\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - B->fd = P;\ - F->bk = P;\ - P->fd = F;\ - P->bk = B;\ -} + /* + Various forms of linking and unlinking are defined as macros. Even + the ones for trees, which are very long but have very short typical + paths. This is ugly but reduces reliance on inlining support of + compilers. + */ -/* Unlink a chunk from a smallbin */ -#define unlink_small_chunk(M, P, S) {\ - mchunkptr F = P->fd;\ - mchunkptr B = P->bk;\ - bindex_t I = small_index(S);\ - assert(P != B);\ - assert(P != F);\ - assert(chunksize(P) == small_index2size(I));\ - if (RTCHECK(F == smallbin_at(M,I) || (ok_address(M, F) && F->bk == P))) { \ - if (B == F) {\ - clear_smallmap(M, I);\ - }\ - else if (RTCHECK(B == smallbin_at(M,I) ||\ - (ok_address(M, B) && B->fd == P))) {\ - F->bk = B;\ - B->fd = F;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - }\ -} + /* Link a free chunk into a smallbin */ + #define insert_small_chunk(M, P, S) \ + { \ + \ + bindex_t I = small_index(S); \ + mchunkptr B = smallbin_at(M, I); \ + mchunkptr F = B; \ + assert(S >= MIN_CHUNK_SIZE); \ + if (!smallmap_is_marked(M, I)) \ + mark_smallmap(M, I); \ + else if (RTCHECK(ok_address(M, B->fd))) \ + F = B->fd; \ + else { \ + \ + CORRUPTION_ERROR_ACTION(M); \ + \ + } \ + B->fd = P; \ + F->bk = P; \ + P->fd = F; \ + P->bk = B; \ + \ + } -/* Unlink the first chunk from a smallbin */ -#define unlink_first_small_chunk(M, B, P, I) {\ - mchunkptr F = P->fd;\ - assert(P != B);\ - assert(P != F);\ - assert(chunksize(P) == small_index2size(I));\ - if (B == F) {\ - clear_smallmap(M, I);\ - }\ - else if (RTCHECK(ok_address(M, F) && F->bk == P)) {\ - F->bk = B;\ - B->fd = F;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - }\ -} + /* Unlink a chunk from a smallbin */ + #define unlink_small_chunk(M, P, S) \ + { \ + \ + mchunkptr F = P->fd; \ + mchunkptr B = P->bk; \ + bindex_t I = small_index(S); \ + assert(P != B); \ + assert(P != F); \ + assert(chunksize(P) == small_index2size(I)); \ + if (RTCHECK(F == smallbin_at(M, I) || \ + (ok_address(M, F) && F->bk == P))) { \ + \ + if (B == F) { \ + \ + clear_smallmap(M, I); \ + \ + } else if (RTCHECK(B == smallbin_at(M, I) || \ + \ + (ok_address(M, B) && B->fd == P))) { \ + \ + F->bk = B; \ + B->fd = F; \ + \ + } else { \ + \ + CORRUPTION_ERROR_ACTION(M); \ + \ + } \ + \ + } else { \ + \ + CORRUPTION_ERROR_ACTION(M); \ + \ + } \ + \ + } -/* Replace dv node, binning the old one */ -/* Used only when dvsize known to be small */ -#define replace_dv(M, P, S) {\ - size_t DVS = M->dvsize;\ - assert(is_small(DVS));\ - if (DVS != 0) {\ - mchunkptr DV = M->dv;\ - insert_small_chunk(M, DV, DVS);\ - }\ - M->dvsize = S;\ - M->dv = P;\ -} + /* Unlink the first chunk from a smallbin */ + #define unlink_first_small_chunk(M, B, P, I) \ + { \ + \ + mchunkptr F = P->fd; \ + assert(P != B); \ + assert(P != F); \ + assert(chunksize(P) == small_index2size(I)); \ + if (B == F) { \ + \ + clear_smallmap(M, I); \ + \ + } else if (RTCHECK(ok_address(M, F) && F->bk == P)) { \ + \ + F->bk = B; \ + B->fd = F; \ + \ + } else { \ + \ + CORRUPTION_ERROR_ACTION(M); \ + \ + } \ + \ + } -/* ------------------------- Operations on trees ------------------------- */ - -/* Insert chunk into tree */ -#define insert_large_chunk(M, X, S) {\ - tbinptr* H;\ - bindex_t I;\ - compute_tree_index(S, I);\ - H = treebin_at(M, I);\ - X->index = I;\ - X->child[0] = X->child[1] = 0;\ - if (!treemap_is_marked(M, I)) {\ - mark_treemap(M, I);\ - *H = X;\ - X->parent = (tchunkptr)H;\ - X->fd = X->bk = X;\ - }\ - else {\ - tchunkptr T = *H;\ - size_t K = S << leftshift_for_tree_index(I);\ - for (;;) {\ - if (chunksize(T) != S) {\ - tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ - K <<= 1;\ - if (*C != 0)\ - T = *C;\ - else if (RTCHECK(ok_address(M, C))) {\ - *C = X;\ - X->parent = T;\ - X->fd = X->bk = X;\ - break;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - break;\ - }\ - }\ - else {\ - tchunkptr F = T->fd;\ - if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ - T->fd = F->bk = X;\ - X->fd = F;\ - X->bk = T;\ - X->parent = 0;\ - break;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - break;\ - }\ - }\ - }\ - }\ -} + /* Replace dv node, binning the old one */ + /* Used only when dvsize known to be small */ + #define replace_dv(M, P, S) \ + { \ + \ + size_t DVS = M->dvsize; \ + assert(is_small(DVS)); \ + if (DVS != 0) { \ + \ + mchunkptr DV = M->dv; \ + insert_small_chunk(M, DV, DVS); \ + \ + } \ + M->dvsize = S; \ + M->dv = P; \ + \ + } + + /* ------------------------- Operations on trees ------------------------- */ + + /* Insert chunk into tree */ + #define insert_large_chunk(M, X, S) \ + { \ + \ + tbinptr *H; \ + bindex_t I; \ + compute_tree_index(S, I); \ + H = treebin_at(M, I); \ + X->index = I; \ + X->child[0] = X->child[1] = 0; \ + if (!treemap_is_marked(M, I)) { \ + \ + mark_treemap(M, I); \ + *H = X; \ + X->parent = (tchunkptr)H; \ + X->fd = X->bk = X; \ + \ + } else { \ + \ + tchunkptr T = *H; \ + size_t K = S << leftshift_for_tree_index(I); \ + for (;;) { \ + \ + if (chunksize(T) != S) { \ + \ + tchunkptr *C = \ + &(T->child[(K >> (SIZE_T_BITSIZE - SIZE_T_ONE)) & 1]); \ + K <<= 1; \ + if (*C != 0) \ + T = *C; \ + else if (RTCHECK(ok_address(M, C))) { \ + \ + *C = X; \ + X->parent = T; \ + X->fd = X->bk = X; \ + break; \ + \ + } else { \ + \ + CORRUPTION_ERROR_ACTION(M); \ + break; \ + \ + } \ + \ + } else { \ + \ + tchunkptr F = T->fd; \ + if (RTCHECK(ok_address(M, T) && ok_address(M, F))) { \ + \ + T->fd = F->bk = X; \ + X->fd = F; \ + X->bk = T; \ + X->parent = 0; \ + break; \ + \ + } else { \ + \ + CORRUPTION_ERROR_ACTION(M); \ + break; \ + \ + } \ + \ + } \ + \ + } \ + \ + } \ + \ + } /* Unlink steps: @@ -3764,104 +4068,145 @@ static void internal_malloc_stats(mstate m) { x's parent and children to x's replacement (or null if none). */ -#define unlink_large_chunk(M, X) {\ - tchunkptr XP = X->parent;\ - tchunkptr R;\ - if (X->bk != X) {\ - tchunkptr F = X->fd;\ - R = X->bk;\ - if (RTCHECK(ok_address(M, F) && F->bk == X && R->fd == X)) {\ - F->bk = R;\ - R->fd = F;\ - }\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ - else {\ - tchunkptr* RP;\ - if (((R = *(RP = &(X->child[1]))) != 0) ||\ - ((R = *(RP = &(X->child[0]))) != 0)) {\ - tchunkptr* CP;\ - while ((*(CP = &(R->child[1])) != 0) ||\ - (*(CP = &(R->child[0])) != 0)) {\ - R = *(RP = CP);\ - }\ - if (RTCHECK(ok_address(M, RP)))\ - *RP = 0;\ - else {\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ - }\ - if (XP != 0) {\ - tbinptr* H = treebin_at(M, X->index);\ - if (X == *H) {\ - if ((*H = R) == 0) \ - clear_treemap(M, X->index);\ - }\ - else if (RTCHECK(ok_address(M, XP))) {\ - if (XP->child[0] == X) \ - XP->child[0] = R;\ - else \ - XP->child[1] = R;\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - if (R != 0) {\ - if (RTCHECK(ok_address(M, R))) {\ - tchunkptr C0, C1;\ - R->parent = XP;\ - if ((C0 = X->child[0]) != 0) {\ - if (RTCHECK(ok_address(M, C0))) {\ - R->child[0] = C0;\ - C0->parent = R;\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - if ((C1 = X->child[1]) != 0) {\ - if (RTCHECK(ok_address(M, C1))) {\ - R->child[1] = C1;\ - C1->parent = R;\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ - else\ - CORRUPTION_ERROR_ACTION(M);\ - }\ - }\ -} + #define unlink_large_chunk(M, X) \ + { \ + \ + tchunkptr XP = X->parent; \ + tchunkptr R; \ + if (X->bk != X) { \ + \ + tchunkptr F = X->fd; \ + R = X->bk; \ + if (RTCHECK(ok_address(M, F) && F->bk == X && R->fd == X)) { \ + \ + F->bk = R; \ + R->fd = F; \ + \ + } else { \ + \ + CORRUPTION_ERROR_ACTION(M); \ + \ + } \ + \ + } else { \ + \ + tchunkptr *RP; \ + if (((R = *(RP = &(X->child[1]))) != 0) || \ + ((R = *(RP = &(X->child[0]))) != 0)) { \ + \ + tchunkptr *CP; \ + while ((*(CP = &(R->child[1])) != 0) || \ + (*(CP = &(R->child[0])) != 0)) { \ + \ + R = *(RP = CP); \ + \ + } \ + if (RTCHECK(ok_address(M, RP))) \ + *RP = 0; \ + else { \ + \ + CORRUPTION_ERROR_ACTION(M); \ + \ + } \ + \ + } \ + \ + } \ + if (XP != 0) { \ + \ + tbinptr *H = treebin_at(M, X->index); \ + if (X == *H) { \ + \ + if ((*H = R) == 0) clear_treemap(M, X->index); \ + \ + } else if (RTCHECK(ok_address(M, XP))) { \ + \ + if (XP->child[0] == X) \ + XP->child[0] = R; \ + else \ + XP->child[1] = R; \ + \ + } else \ + \ + CORRUPTION_ERROR_ACTION(M); \ + if (R != 0) { \ + \ + if (RTCHECK(ok_address(M, R))) { \ + \ + tchunkptr C0, C1; \ + R->parent = XP; \ + if ((C0 = X->child[0]) != 0) { \ + \ + if (RTCHECK(ok_address(M, C0))) { \ + \ + R->child[0] = C0; \ + C0->parent = R; \ + \ + } else \ + \ + CORRUPTION_ERROR_ACTION(M); \ + \ + } \ + if ((C1 = X->child[1]) != 0) { \ + \ + if (RTCHECK(ok_address(M, C1))) { \ + \ + R->child[1] = C1; \ + C1->parent = R; \ + \ + } else \ + \ + CORRUPTION_ERROR_ACTION(M); \ + \ + } \ + \ + } else \ + \ + CORRUPTION_ERROR_ACTION(M); \ + \ + } \ + \ + } \ + \ + } /* Relays to large vs small bin operations */ -#define insert_chunk(M, P, S)\ - if (is_small(S)) insert_small_chunk(M, P, S)\ - else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } - -#define unlink_chunk(M, P, S)\ - if (is_small(S)) unlink_small_chunk(M, P, S)\ - else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } + #define insert_chunk(M, P, S) \ + if (is_small(S)) insert_small_chunk(M, P, S) else { \ + \ + tchunkptr TP = (tchunkptr)(P); \ + insert_large_chunk(M, TP, S); \ + \ + } + #define unlink_chunk(M, P, S) \ + if (is_small(S)) unlink_small_chunk(M, P, S) else { \ + \ + tchunkptr TP = (tchunkptr)(P); \ + unlink_large_chunk(M, TP); \ + \ + } /* Relays to internal calls to malloc/free from realloc, memalign etc */ -#if ONLY_MSPACES -#define internal_malloc(m, b) mspace_malloc(m, b) -#define internal_free(m, mem) mspace_free(m,mem); -#else /* ONLY_MSPACES */ -#if MSPACES -#define internal_malloc(m, b)\ - ((m == gm)? dlmalloc(b) : mspace_malloc(m, b)) -#define internal_free(m, mem)\ - if (m == gm) dlfree(mem); else mspace_free(m,mem); -#else /* MSPACES */ -#define internal_malloc(m, b) dlmalloc(b) -#define internal_free(m, mem) dlfree(mem) -#endif /* MSPACES */ -#endif /* ONLY_MSPACES */ + #if ONLY_MSPACES + #define internal_malloc(m, b) mspace_malloc(m, b) + #define internal_free(m, mem) mspace_free(m, mem); + #else /* ONLY_MSPACES */ + #if MSPACES + #define internal_malloc(m, b) \ + ((m == gm) ? dlmalloc(b) : mspace_malloc(m, b)) + #define internal_free(m, mem) \ + if (m == gm) \ + dlfree(mem); \ + else \ + mspace_free(m, mem); + #else /* MSPACES */ + #define internal_malloc(m, b) dlmalloc(b) + #define internal_free(m, mem) dlfree(mem) + #endif /* MSPACES */ + #endif /* ONLY_MSPACES */ /* ----------------------- Direct-mmapping chunks ----------------------- */ @@ -3874,80 +4219,93 @@ static void internal_malloc_stats(mstate m) { */ /* Malloc using mmap */ -static void* mmap_alloc(mstate m, size_t nb) { +static void *mmap_alloc(mstate m, size_t nb) { + size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); if (m->footprint_limit != 0) { + size_t fp = m->footprint + mmsize; - if (fp <= m->footprint || fp > m->footprint_limit) - return 0; + if (fp <= m->footprint || fp > m->footprint_limit) return 0; + } - if (mmsize > nb) { /* Check for wrap around 0 */ - char* mm = (char*)(CALL_DIRECT_MMAP(mmsize)); + + if (mmsize > nb) { /* Check for wrap around 0 */ + char *mm = (char *)(CALL_DIRECT_MMAP(mmsize)); if (mm != CMFAIL) { - size_t offset = align_offset(chunk2mem(mm)); - size_t psize = mmsize - offset - MMAP_FOOT_PAD; + + size_t offset = align_offset(chunk2mem(mm)); + size_t psize = mmsize - offset - MMAP_FOOT_PAD; mchunkptr p = (mchunkptr)(mm + offset); p->prev_foot = offset; p->head = psize; mark_inuse_foot(m, p, psize); chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; - chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; + chunk_plus_offset(p, psize + SIZE_T_SIZE)->head = 0; - if (m->least_addr == 0 || mm < m->least_addr) - m->least_addr = mm; + if (m->least_addr == 0 || mm < m->least_addr) m->least_addr = mm; if ((m->footprint += mmsize) > m->max_footprint) m->max_footprint = m->footprint; assert(is_aligned(chunk2mem(p))); check_mmapped_chunk(m, p); return chunk2mem(p); + } + } + return 0; + } /* Realloc using mmap */ static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb, int flags) { + size_t oldsize = chunksize(oldp); - (void)flags; /* placate people compiling -Wunused */ - if (is_small(nb)) /* Can't shrink mmap regions below small size */ + (void)flags; /* placate people compiling -Wunused */ + if (is_small(nb)) /* Can't shrink mmap regions below small size */ return 0; /* Keep old chunk if big enough but not too big */ if (oldsize >= nb + SIZE_T_SIZE && (oldsize - nb) <= (mparams.granularity << 1)) return oldp; else { + size_t offset = oldp->prev_foot; size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); - char* cp = (char*)CALL_MREMAP((char*)oldp - offset, - oldmmsize, newmmsize, flags); + char * cp = + (char *)CALL_MREMAP((char *)oldp - offset, oldmmsize, newmmsize, flags); if (cp != CMFAIL) { + mchunkptr newp = (mchunkptr)(cp + offset); - size_t psize = newmmsize - offset - MMAP_FOOT_PAD; + size_t psize = newmmsize - offset - MMAP_FOOT_PAD; newp->head = psize; mark_inuse_foot(m, newp, psize); chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; - chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; + chunk_plus_offset(newp, psize + SIZE_T_SIZE)->head = 0; - if (cp < m->least_addr) - m->least_addr = cp; + if (cp < m->least_addr) m->least_addr = cp; if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) m->max_footprint = m->footprint; check_mmapped_chunk(m, newp); return newp; + } + } + return 0; -} +} /* -------------------------- mspace management -------------------------- */ /* Initialize top chunk and its size */ static void init_top(mstate m, mchunkptr p, size_t psize) { + /* Ensure alignment */ size_t offset = align_offset(chunk2mem(p)); - p = (mchunkptr)((char*)p + offset); + p = (mchunkptr)((char *)p + offset); psize -= offset; m->top = p; @@ -3955,23 +4313,29 @@ static void init_top(mstate m, mchunkptr p, size_t psize) { p->head = psize | PINUSE_BIT; /* set size of fake trailing chunk holding overhead space only once */ chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; - m->trim_check = mparams.trim_threshold; /* reset on each update */ + m->trim_check = mparams.trim_threshold; /* reset on each update */ + } /* Initialize bins for a new mstate that is otherwise zeroed out */ static void init_bins(mstate m) { + /* Establish circular links for smallbins */ bindex_t i; for (i = 0; i < NSMALLBINS; ++i) { - sbinptr bin = smallbin_at(m,i); + + sbinptr bin = smallbin_at(m, i); bin->fd = bin->bk = bin; + } + } -#if PROCEED_ON_ERROR + #if PROCEED_ON_ERROR /* default corruption action */ static void reset_on_error(mstate m) { + int i; ++malloc_corruption_error_count; /* Reinitialize fields to forget about all memory */ @@ -3984,67 +4348,78 @@ static void reset_on_error(mstate m) { for (i = 0; i < NTREEBINS; ++i) *treebin_at(m, i) = 0; init_bins(m); + } -#endif /* PROCEED_ON_ERROR */ + + #endif /* PROCEED_ON_ERROR */ /* Allocate chunk and prepend remainder with chunk in successor base. */ -static void* prepend_alloc(mstate m, char* newbase, char* oldbase, - size_t nb) { +static void *prepend_alloc(mstate m, char *newbase, char *oldbase, size_t nb) { + mchunkptr p = align_as_chunk(newbase); mchunkptr oldfirst = align_as_chunk(oldbase); - size_t psize = (char*)oldfirst - (char*)p; + size_t psize = (char *)oldfirst - (char *)p; mchunkptr q = chunk_plus_offset(p, nb); - size_t qsize = psize - nb; + size_t qsize = psize - nb; set_size_and_pinuse_of_inuse_chunk(m, p, nb); - assert((char*)oldfirst > (char*)q); + assert((char *)oldfirst > (char *)q); assert(pinuse(oldfirst)); assert(qsize >= MIN_CHUNK_SIZE); /* consolidate remainder with first chunk of old base */ if (oldfirst == m->top) { + size_t tsize = m->topsize += qsize; m->top = q; q->head = tsize | PINUSE_BIT; check_top_chunk(m, q); - } - else if (oldfirst == m->dv) { + + } else if (oldfirst == m->dv) { + size_t dsize = m->dvsize += qsize; m->dv = q; set_size_and_pinuse_of_free_chunk(q, dsize); - } - else { + + } else { + if (!is_inuse(oldfirst)) { + size_t nsize = chunksize(oldfirst); unlink_chunk(m, oldfirst, nsize); oldfirst = chunk_plus_offset(oldfirst, nsize); qsize += nsize; + } + set_free_with_pinuse(q, qsize, oldfirst); insert_chunk(m, q, qsize); check_free_chunk(m, q); + } check_malloced_chunk(m, chunk2mem(p), nb); return chunk2mem(p); + } /* Add a segment to hold a new noncontiguous region */ -static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { +static void add_segment(mstate m, char *tbase, size_t tsize, flag_t mmapped) { + /* Determine locations and sizes of segment, fenceposts, old top */ - char* old_top = (char*)m->top; + char * old_top = (char *)m->top; msegmentptr oldsp = segment_holding(m, old_top); - char* old_end = oldsp->base + oldsp->size; - size_t ssize = pad_request(sizeof(struct malloc_segment)); - char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); - size_t offset = align_offset(chunk2mem(rawsp)); - char* asp = rawsp + offset; - char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; - mchunkptr sp = (mchunkptr)csp; + char * old_end = oldsp->base + oldsp->size; + size_t ssize = pad_request(sizeof(struct malloc_segment)); + char * rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + size_t offset = align_offset(chunk2mem(rawsp)); + char * asp = rawsp + offset; + char * csp = (asp < (old_top + MIN_CHUNK_SIZE)) ? old_top : asp; + mchunkptr sp = (mchunkptr)csp; msegmentptr ss = (msegmentptr)(chunk2mem(sp)); - mchunkptr tnext = chunk_plus_offset(sp, ssize); - mchunkptr p = tnext; - int nfences = 0; + mchunkptr tnext = chunk_plus_offset(sp, ssize); + mchunkptr p = tnext; + int nfences = 0; /* reset top to new space */ init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); @@ -4052,7 +4427,7 @@ static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { /* Set up segment record */ assert(is_aligned(ss)); set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); - *ss = m->seg; /* Push current record */ + *ss = m->seg; /* Push current record */ m->seg.base = tbase; m->seg.size = tsize; m->seg.sflags = mmapped; @@ -4060,53 +4435,61 @@ static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { /* Insert trailing fenceposts */ for (;;) { + mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); p->head = FENCEPOST_HEAD; ++nfences; - if ((char*)(&(nextp->head)) < old_end) + if ((char *)(&(nextp->head)) < old_end) p = nextp; else break; + } + assert(nfences >= 2); /* Insert the rest of old top into a bin as an ordinary free chunk */ if (csp != old_top) { + mchunkptr q = (mchunkptr)old_top; - size_t psize = csp - old_top; + size_t psize = csp - old_top; mchunkptr tn = chunk_plus_offset(q, psize); set_free_with_pinuse(q, psize, tn); insert_chunk(m, q, psize); + } check_top_chunk(m, m->top); + } /* -------------------------- System allocation -------------------------- */ /* Get memory from system using MORECORE or MMAP */ -static void* sys_alloc(mstate m, size_t nb) { - char* tbase = CMFAIL; +static void *sys_alloc(mstate m, size_t nb) { + + char * tbase = CMFAIL; size_t tsize = 0; flag_t mmap_flag = 0; - size_t asize; /* allocation size */ + size_t asize; /* allocation size */ ensure_initialization(); /* Directly map large chunks, but only if already initialized */ if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) { - void* mem = mmap_alloc(m, nb); - if (mem != 0) - return mem; + + void *mem = mmap_alloc(m, nb); + if (mem != 0) return mem; + } asize = granularity_align(nb + SYS_ALLOC_PADDING); - if (asize <= nb) - return 0; /* wraparound */ + if (asize <= nb) return 0; /* wraparound */ if (m->footprint_limit != 0) { + size_t fp = m->footprint + asize; - if (fp <= m->footprint || fp > m->footprint_limit) - return 0; + if (fp <= m->footprint || fp > m->footprint_limit) return 0; + } /* @@ -4132,91 +4515,119 @@ static void* sys_alloc(mstate m, size_t nb) { */ if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { - char* br = CMFAIL; - size_t ssize = asize; /* sbrk call size */ - msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); + + char * br = CMFAIL; + size_t ssize = asize; /* sbrk call size */ + msegmentptr ss = (m->top == 0) ? 0 : segment_holding(m, (char *)m->top); ACQUIRE_MALLOC_GLOBAL_LOCK(); - if (ss == 0) { /* First time through or recovery */ - char* base = (char*)CALL_MORECORE(0); + if (ss == 0) { /* First time through or recovery */ + char *base = (char *)CALL_MORECORE(0); if (base != CMFAIL) { + size_t fp; /* Adjust to end on a page boundary */ if (!is_page_aligned(base)) ssize += (page_align((size_t)base) - (size_t)base); - fp = m->footprint + ssize; /* recheck limits */ + fp = m->footprint + ssize; /* recheck limits */ if (ssize > nb && ssize < HALF_MAX_SIZE_T && (m->footprint_limit == 0 || (fp > m->footprint && fp <= m->footprint_limit)) && - (br = (char*)(CALL_MORECORE(ssize))) == base) { + (br = (char *)(CALL_MORECORE(ssize))) == base) { + tbase = base; tsize = ssize; + } + } - } - else { + + } else { + /* Subtract out existing available top space from MORECORE request. */ ssize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING); /* Use mem here only if it did continuously extend old space */ if (ssize < HALF_MAX_SIZE_T && - (br = (char*)(CALL_MORECORE(ssize))) == ss->base+ss->size) { + (br = (char *)(CALL_MORECORE(ssize))) == ss->base + ss->size) { + tbase = br; tsize = ssize; + } + } - if (tbase == CMFAIL) { /* Cope with partial failure */ - if (br != CMFAIL) { /* Try to use/extend the space we did get */ - if (ssize < HALF_MAX_SIZE_T && - ssize < nb + SYS_ALLOC_PADDING) { + if (tbase == CMFAIL) { /* Cope with partial failure */ + if (br != CMFAIL) { /* Try to use/extend the space we did get */ + if (ssize < HALF_MAX_SIZE_T && ssize < nb + SYS_ALLOC_PADDING) { + size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - ssize); if (esize < HALF_MAX_SIZE_T) { - char* end = (char*)CALL_MORECORE(esize); + + char *end = (char *)CALL_MORECORE(esize); if (end != CMFAIL) ssize += esize; - else { /* Can't use; try to release */ - (void) CALL_MORECORE(-ssize); + else { /* Can't use; try to release */ + (void)CALL_MORECORE(-ssize); br = CMFAIL; + } + } + } + } - if (br != CMFAIL) { /* Use the space we did get */ + + if (br != CMFAIL) { /* Use the space we did get */ tbase = br; tsize = ssize; - } - else - disable_contiguous(m); /* Don't try contiguous path in the future */ + + } else + + disable_contiguous(m); /* Don't try contiguous path in the future */ + } RELEASE_MALLOC_GLOBAL_LOCK(); + } - if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ - char* mp = (char*)(CALL_MMAP(asize)); + if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ + char *mp = (char *)(CALL_MMAP(asize)); if (mp != CMFAIL) { + tbase = mp; tsize = asize; mmap_flag = USE_MMAP_BIT; + } + } - if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ + if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ if (asize < HALF_MAX_SIZE_T) { - char* br = CMFAIL; - char* end = CMFAIL; + + char *br = CMFAIL; + char *end = CMFAIL; ACQUIRE_MALLOC_GLOBAL_LOCK(); - br = (char*)(CALL_MORECORE(asize)); - end = (char*)(CALL_MORECORE(0)); + br = (char *)(CALL_MORECORE(asize)); + end = (char *)(CALL_MORECORE(0)); RELEASE_MALLOC_GLOBAL_LOCK(); if (br != CMFAIL && end != CMFAIL && br < end) { + size_t ssize = end - br; if (ssize > nb + TOP_FOOT_SIZE) { + tbase = br; tsize = ssize; + } + } + } + } if (tbase != CMFAIL) { @@ -4224,61 +4635,66 @@ static void* sys_alloc(mstate m, size_t nb) { if ((m->footprint += tsize) > m->max_footprint) m->max_footprint = m->footprint; - if (!is_initialized(m)) { /* first-time initialization */ - if (m->least_addr == 0 || tbase < m->least_addr) - m->least_addr = tbase; + if (!is_initialized(m)) { /* first-time initialization */ + if (m->least_addr == 0 || tbase < m->least_addr) m->least_addr = tbase; m->seg.base = tbase; m->seg.size = tsize; m->seg.sflags = mmap_flag; m->magic = mparams.magic; m->release_checks = MAX_RELEASE_CHECK_RATE; init_bins(m); -#if !ONLY_MSPACES + #if !ONLY_MSPACES if (is_global(m)) init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); else -#endif + #endif { + /* Offset top by embedded malloc_state */ mchunkptr mn = next_chunk(mem2chunk(m)); - init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); + init_top(m, mn, (size_t)((tbase + tsize) - (char *)mn) - TOP_FOOT_SIZE); + } + } else { + /* Try to merge with an existing segment */ msegmentptr sp = &m->seg; /* Only consider most recent segment if traversal suppressed */ while (sp != 0 && tbase != sp->base + sp->size) sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; - if (sp != 0 && - !is_extern_segment(sp) && + if (sp != 0 && !is_extern_segment(sp) && (sp->sflags & USE_MMAP_BIT) == mmap_flag && - segment_holds(sp, m->top)) { /* append */ + segment_holds(sp, m->top)) { /* append */ sp->size += tsize; init_top(m, m->top, m->topsize + tsize); - } - else { - if (tbase < m->least_addr) - m->least_addr = tbase; + + } else { + + if (tbase < m->least_addr) m->least_addr = tbase; sp = &m->seg; while (sp != 0 && sp->base != tbase + tsize) sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; - if (sp != 0 && - !is_extern_segment(sp) && + if (sp != 0 && !is_extern_segment(sp) && (sp->sflags & USE_MMAP_BIT) == mmap_flag) { - char* oldbase = sp->base; + + char *oldbase = sp->base; sp->base = tbase; sp->size += tsize; return prepend_alloc(m, tbase, oldbase, nb); - } - else + + } else + add_segment(m, tbase, tsize, mmap_flag); + } + } - if (nb < m->topsize) { /* Allocate from new or extended top space */ - size_t rsize = m->topsize -= nb; + if (nb < m->topsize) { /* Allocate from new or extended top space */ + size_t rsize = m->topsize -= nb; mchunkptr p = m->top; mchunkptr r = m->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; @@ -4286,353 +4702,463 @@ static void* sys_alloc(mstate m, size_t nb) { check_top_chunk(m, m->top); check_malloced_chunk(m, chunk2mem(p), nb); return chunk2mem(p); + } + } MALLOC_FAILURE_ACTION; return 0; + } /* ----------------------- system deallocation -------------------------- */ /* Unmap and unlink any mmapped segments that don't contain used chunks */ static size_t release_unused_segments(mstate m) { - size_t released = 0; - int nsegs = 0; + + size_t released = 0; + int nsegs = 0; msegmentptr pred = &m->seg; msegmentptr sp = pred->next; while (sp != 0) { - char* base = sp->base; - size_t size = sp->size; + + char * base = sp->base; + size_t size = sp->size; msegmentptr next = sp->next; ++nsegs; if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { + mchunkptr p = align_as_chunk(base); - size_t psize = chunksize(p); + size_t psize = chunksize(p); /* Can unmap if first chunk holds entire segment and not pinned */ - if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { + if (!is_inuse(p) && (char *)p + psize >= base + size - TOP_FOOT_SIZE) { + tchunkptr tp = (tchunkptr)p; - assert(segment_holds(sp, (char*)sp)); + assert(segment_holds(sp, (char *)sp)); if (p == m->dv) { + m->dv = 0; m->dvsize = 0; - } - else { + + } else { + unlink_large_chunk(m, tp); + } + if (CALL_MUNMAP(base, size) == 0) { + released += size; m->footprint -= size; /* unlink obsoleted record */ sp = pred; sp->next = next; - } - else { /* back out if cannot unmap */ + + } else { /* back out if cannot unmap */ + insert_large_chunk(m, tp, psize); + } + } + } - if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ + + if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ break; pred = sp; sp = next; + } + /* Reset check counter */ - m->release_checks = (((size_t) nsegs > (size_t) MAX_RELEASE_CHECK_RATE)? - (size_t) nsegs : (size_t) MAX_RELEASE_CHECK_RATE); + m->release_checks = (((size_t)nsegs > (size_t)MAX_RELEASE_CHECK_RATE) + ? (size_t)nsegs + : (size_t)MAX_RELEASE_CHECK_RATE); return released; + } static int sys_trim(mstate m, size_t pad) { + size_t released = 0; ensure_initialization(); if (pad < MAX_REQUEST && is_initialized(m)) { - pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ + + pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ if (m->topsize > pad) { + /* Shrink top space in granularity-size units, keeping at least one */ size_t unit = mparams.granularity; - size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - - SIZE_T_ONE) * unit; - msegmentptr sp = segment_holding(m, (char*)m->top); + size_t extra = + ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - SIZE_T_ONE) * unit; + msegmentptr sp = segment_holding(m, (char *)m->top); if (!is_extern_segment(sp)) { + if (is_mmapped_segment(sp)) { - if (HAVE_MMAP && - sp->size >= extra && - !has_segment_link(m, sp)) { /* can't shrink if pinned */ + + if (HAVE_MMAP && sp->size >= extra && + !has_segment_link(m, sp)) { /* can't shrink if pinned */ size_t newsize = sp->size - extra; - (void)newsize; /* placate people compiling -Wunused-variable */ + (void)newsize; /* placate people compiling -Wunused-variable */ /* Prefer mremap, fall back to munmap */ if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { + released = extra; + } + } - } - else if (HAVE_MORECORE) { - if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ + + } else if (HAVE_MORECORE) { + + if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; ACQUIRE_MALLOC_GLOBAL_LOCK(); { + /* Make sure end of memory is where we last set it. */ - char* old_br = (char*)(CALL_MORECORE(0)); + char *old_br = (char *)(CALL_MORECORE(0)); if (old_br == sp->base + sp->size) { - char* rel_br = (char*)(CALL_MORECORE(-extra)); - char* new_br = (char*)(CALL_MORECORE(0)); + + char *rel_br = (char *)(CALL_MORECORE(-extra)); + char *new_br = (char *)(CALL_MORECORE(0)); if (rel_br != CMFAIL && new_br < old_br) released = old_br - new_br; + } + } + RELEASE_MALLOC_GLOBAL_LOCK(); + } + } if (released != 0) { + sp->size -= released; m->footprint -= released; init_top(m, m->top, m->topsize - released); check_top_chunk(m, m->top); + } + } /* Unmap any unused mmapped segments */ - if (HAVE_MMAP) - released += release_unused_segments(m); + if (HAVE_MMAP) released += release_unused_segments(m); /* On failure, disable autotrim to avoid repeated failed future calls */ - if (released == 0 && m->topsize > m->trim_check) - m->trim_check = MAX_SIZE_T; + if (released == 0 && m->topsize > m->trim_check) m->trim_check = MAX_SIZE_T; + } - return (released != 0)? 1 : 0; + return (released != 0) ? 1 : 0; + } /* Consolidate and bin a chunk. Differs from exported versions of free mainly in that the chunk need not be marked as inuse. */ static void dispose_chunk(mstate m, mchunkptr p, size_t psize) { + mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { + mchunkptr prev; - size_t prevsize = p->prev_foot; + size_t prevsize = p->prev_foot; if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) - m->footprint -= psize; + if (CALL_MUNMAP((char *)p - prevsize, psize) == 0) m->footprint -= psize; return; + } + prev = chunk_minus_offset(p, prevsize); psize += prevsize; p = prev; - if (RTCHECK(ok_address(m, prev))) { /* consolidate backward */ + if (RTCHECK(ok_address(m, prev))) { /* consolidate backward */ if (p != m->dv) { + unlink_chunk(m, p, prevsize); - } - else if ((next->head & INUSE_BITS) == INUSE_BITS) { + + } else if ((next->head & INUSE_BITS) == INUSE_BITS) { + m->dvsize = psize; set_free_with_pinuse(p, psize, next); return; + } - } - else { + + } else { + CORRUPTION_ERROR_ACTION(m); return; + } + } + if (RTCHECK(ok_address(m, next))) { - if (!cinuse(next)) { /* consolidate forward */ + + if (!cinuse(next)) { /* consolidate forward */ if (next == m->top) { + size_t tsize = m->topsize += psize; m->top = p; p->head = tsize | PINUSE_BIT; if (p == m->dv) { + m->dv = 0; m->dvsize = 0; + } + return; - } - else if (next == m->dv) { + + } else if (next == m->dv) { + size_t dsize = m->dvsize += psize; m->dv = p; set_size_and_pinuse_of_free_chunk(p, dsize); return; - } - else { + + } else { + size_t nsize = chunksize(next); psize += nsize; unlink_chunk(m, next, nsize); set_size_and_pinuse_of_free_chunk(p, psize); if (p == m->dv) { + m->dvsize = psize; return; + } + } - } - else { + + } else { + set_free_with_pinuse(p, psize, next); + } + insert_chunk(m, p, psize); - } - else { + + } else { + CORRUPTION_ERROR_ACTION(m); + } + } /* ---------------------------- malloc --------------------------- */ /* allocate a large request from the best fitting chunk in a treebin */ -static void* tmalloc_large(mstate m, size_t nb) { +static void *tmalloc_large(mstate m, size_t nb) { + tchunkptr v = 0; - size_t rsize = -nb; /* Unsigned negation */ + size_t rsize = -nb; /* Unsigned negation */ tchunkptr t; - bindex_t idx; + bindex_t idx; compute_tree_index(nb, idx); if ((t = *treebin_at(m, idx)) != 0) { + /* Traverse tree for this bin looking for node with size == nb */ - size_t sizebits = nb << leftshift_for_tree_index(idx); - tchunkptr rst = 0; /* The deepest untaken right subtree */ + size_t sizebits = nb << leftshift_for_tree_index(idx); + tchunkptr rst = 0; /* The deepest untaken right subtree */ for (;;) { + tchunkptr rt; - size_t trem = chunksize(t) - nb; + size_t trem = chunksize(t) - nb; if (trem < rsize) { + v = t; - if ((rsize = trem) == 0) - break; + if ((rsize = trem) == 0) break; + } + rt = t->child[1]; - t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; - if (rt != 0 && rt != t) - rst = rt; + t = t->child[(sizebits >> (SIZE_T_BITSIZE - SIZE_T_ONE)) & 1]; + if (rt != 0 && rt != t) rst = rt; if (t == 0) { - t = rst; /* set t to least subtree holding sizes > nb */ + + t = rst; /* set t to least subtree holding sizes > nb */ break; + } + sizebits <<= 1; + } + } - if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ + + if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; if (leftbits != 0) { + bindex_t i; binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); t = *treebin_at(m, i); + } + } - while (t != 0) { /* find smallest of tree or subtree */ + while (t != 0) { /* find smallest of tree or subtree */ size_t trem = chunksize(t) - nb; if (trem < rsize) { + rsize = trem; v = t; + } + t = leftmost_child(t); + } /* If dv is a better fit, return 0 so malloc will use it */ if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { - if (RTCHECK(ok_address(m, v))) { /* split */ + + if (RTCHECK(ok_address(m, v))) { /* split */ mchunkptr r = chunk_plus_offset(v, nb); assert(chunksize(v) == rsize + nb); if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); if (rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(m, v, (rsize + nb)); else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); set_size_and_pinuse_of_free_chunk(r, rsize); insert_chunk(m, r, rsize); + } + return chunk2mem(v); + } + } + CORRUPTION_ERROR_ACTION(m); + } + return 0; + } /* allocate a small request from the best fitting chunk in a treebin */ -static void* tmalloc_small(mstate m, size_t nb) { +static void *tmalloc_small(mstate m, size_t nb) { + tchunkptr t, v; - size_t rsize; - bindex_t i; - binmap_t leastbit = least_bit(m->treemap); + size_t rsize; + bindex_t i; + binmap_t leastbit = least_bit(m->treemap); compute_bit2idx(leastbit, i); v = t = *treebin_at(m, i); rsize = chunksize(t) - nb; while ((t = leftmost_child(t)) != 0) { + size_t trem = chunksize(t) - nb; if (trem < rsize) { + rsize = trem; v = t; + } + } if (RTCHECK(ok_address(m, v))) { + mchunkptr r = chunk_plus_offset(v, nb); assert(chunksize(v) == rsize + nb); if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); if (rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(m, v, (rsize + nb)); else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(m, r, rsize); + } + return chunk2mem(v); + } + } CORRUPTION_ERROR_ACTION(m); return 0; -} -#if !ONLY_MSPACES +} -void* dlmalloc(size_t bytes) { - /* - Basic algorithm: - If a small request (< 256 bytes minus per-chunk overhead): - 1. If one exists, use a remainderless chunk in associated smallbin. - (Remainderless means that there are too few excess bytes to - represent as a chunk.) - 2. If it is big enough, use the dv chunk, which is normally the - chunk adjacent to the one used for the most recent small request. - 3. If one exists, split the smallest available chunk in a bin, - saving remainder in dv. - 4. If it is big enough, use the top chunk. - 5. If available, get memory from system and use it - Otherwise, for a large request: - 1. Find the smallest available binned chunk that fits, and use it - if it is better fitting than dv chunk, splitting if necessary. - 2. If better fitting than any binned chunk, use the dv chunk. - 3. If it is big enough, use the top chunk. - 4. If request size >= mmap threshold, try to directly mmap this chunk. - 5. If available, get memory from system and use it - - The ugly goto's here ensure that postaction occurs along all paths. - */ + #if !ONLY_MSPACES + +void *dlmalloc(size_t bytes) { + + /* + Basic algorithm: + If a small request (< 256 bytes minus per-chunk overhead): + 1. If one exists, use a remainderless chunk in associated smallbin. + (Remainderless means that there are too few excess bytes to + represent as a chunk.) + 2. If it is big enough, use the dv chunk, which is normally the + chunk adjacent to the one used for the most recent small request. + 3. If one exists, split the smallest available chunk in a bin, + saving remainder in dv. + 4. If it is big enough, use the top chunk. + 5. If available, get memory from system and use it + Otherwise, for a large request: + 1. Find the smallest available binned chunk that fits, and use it + if it is better fitting than dv chunk, splitting if necessary. + 2. If better fitting than any binned chunk, use the dv chunk. + 3. If it is big enough, use the top chunk. + 4. If request size >= mmap threshold, try to directly mmap this chunk. + 5. If available, get memory from system and use it + + The ugly goto's here ensure that postaction occurs along all paths. + */ -#if USE_LOCKS - ensure_initialization(); /* initialize in sys_alloc if not using locks */ -#endif + #if USE_LOCKS + ensure_initialization(); /* initialize in sys_alloc if not using locks */ + #endif if (!PREACTION(gm)) { - void* mem; + + void * mem; size_t nb; if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; binmap_t smallbits; - nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + nb = (bytes < MIN_REQUEST) ? MIN_CHUNK_SIZE : pad_request(bytes); idx = small_index(nb); smallbits = gm->smallmap >> idx; - if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ mchunkptr b, p; - idx += ~smallbits & 1; /* Uses next bin if idx empty */ + idx += ~smallbits & 1; /* Uses next bin if idx empty */ b = smallbin_at(gm, idx); p = b->fd; assert(chunksize(p) == small_index2size(idx)); @@ -4641,15 +5167,17 @@ void* dlmalloc(size_t bytes) { mem = chunk2mem(p); check_malloced_chunk(gm, mem, nb); goto postaction; + } else if (nb > gm->dvsize) { - if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ mchunkptr b, p, r; - size_t rsize; - bindex_t i; - binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); - binmap_t leastbit = least_bit(leftbits); + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); b = smallbin_at(gm, i); p = b->fd; @@ -4660,54 +5188,71 @@ void* dlmalloc(size_t bytes) { if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(gm, p, small_index2size(i)); else { + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); r = chunk_plus_offset(p, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(gm, r, rsize); + } + mem = chunk2mem(p); check_malloced_chunk(gm, mem, nb); goto postaction; + } else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); goto postaction; + } + } - } - else if (bytes >= MAX_REQUEST) + + } else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ else { + nb = pad_request(bytes); if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); goto postaction; + } + } if (nb <= gm->dvsize) { - size_t rsize = gm->dvsize - nb; + + size_t rsize = gm->dvsize - nb; mchunkptr p = gm->dv; - if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ mchunkptr r = gm->dv = chunk_plus_offset(p, nb); gm->dvsize = rsize; set_size_and_pinuse_of_free_chunk(r, rsize); set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - } - else { /* exhaust dv */ + + } else { /* exhaust dv */ + size_t dvs = gm->dvsize; gm->dvsize = 0; gm->dv = 0; set_inuse_and_pinuse(gm, p, dvs); + } + mem = chunk2mem(p); check_malloced_chunk(gm, mem, nb); goto postaction; + } - else if (nb < gm->topsize) { /* Split top */ - size_t rsize = gm->topsize -= nb; + else if (nb < gm->topsize) { /* Split top */ + size_t rsize = gm->topsize -= nb; mchunkptr p = gm->top; mchunkptr r = gm->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; @@ -4716,6 +5261,7 @@ void* dlmalloc(size_t bytes) { check_top_chunk(gm, gm->top); check_malloced_chunk(gm, mem, nb); goto postaction; + } mem = sys_alloc(gm, nb); @@ -4723,14 +5269,17 @@ void* dlmalloc(size_t bytes) { postaction: POSTACTION(gm); return mem; + } return 0; + } /* ---------------------------- free --------------------------- */ -void dlfree(void* mem) { +void dlfree(void *mem) { + /* Consolidate freed chunks with preceeding or succeeding bordering free chunks, if they exist, and then place in a bin. Intermixed @@ -4738,164 +5287,216 @@ void dlfree(void* mem) { */ if (mem != 0) { - mchunkptr p = mem2chunk(mem); -#if FOOTERS + + mchunkptr p = mem2chunk(mem); + #if FOOTERS mstate fm = get_mstate_for(p); if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); return; + } -#else /* FOOTERS */ -#define fm gm -#endif /* FOOTERS */ + + #else /* FOOTERS */ + #define fm gm + #endif /* FOOTERS */ if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { - size_t psize = chunksize(p); + + size_t psize = chunksize(p); mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { + size_t prevsize = p->prev_foot; if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + if (CALL_MUNMAP((char *)p - prevsize, psize) == 0) fm->footprint -= psize; goto postaction; - } - else { + + } else { + mchunkptr prev = chunk_minus_offset(p, prevsize); psize += prevsize; p = prev; - if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); - } - else if ((next->head & INUSE_BITS) == INUSE_BITS) { + + } else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; set_free_with_pinuse(p, psize, next); goto postaction; + } - } - else + + } else + goto erroraction; + } + } if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { - if (!cinuse(next)) { /* consolidate forward */ + + if (!cinuse(next)) { /* consolidate forward */ if (next == fm->top) { + size_t tsize = fm->topsize += psize; fm->top = p; p->head = tsize | PINUSE_BIT; if (p == fm->dv) { + fm->dv = 0; fm->dvsize = 0; + } - if (should_trim(fm, tsize)) - sys_trim(fm, 0); + + if (should_trim(fm, tsize)) sys_trim(fm, 0); goto postaction; - } - else if (next == fm->dv) { + + } else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; fm->dv = p; set_size_and_pinuse_of_free_chunk(p, dsize); goto postaction; - } - else { + + } else { + size_t nsize = chunksize(next); psize += nsize; unlink_chunk(fm, next, nsize); set_size_and_pinuse_of_free_chunk(p, psize); if (p == fm->dv) { + fm->dvsize = psize; goto postaction; + } + } - } - else + + } else + set_free_with_pinuse(p, psize, next); if (is_small(psize)) { + insert_small_chunk(fm, p, psize); check_free_chunk(fm, p); - } - else { + + } else { + tchunkptr tp = (tchunkptr)p; insert_large_chunk(fm, tp, psize); check_free_chunk(fm, p); - if (--fm->release_checks == 0) - release_unused_segments(fm); + if (--fm->release_checks == 0) release_unused_segments(fm); + } + goto postaction; + } + } + erroraction: USAGE_ERROR_ACTION(fm, p); postaction: POSTACTION(fm); + } + } -#if !FOOTERS -#undef fm -#endif /* FOOTERS */ + + #if !FOOTERS + #undef fm + #endif /* FOOTERS */ + } -void* dlcalloc(size_t n_elements, size_t elem_size) { - void* mem; +void *dlcalloc(size_t n_elements, size_t elem_size) { + + void * mem; size_t req = 0; if (n_elements != 0) { + req = n_elements * elem_size; if (((n_elements | elem_size) & ~(size_t)0xffff) && (req / n_elements != elem_size)) - req = MAX_SIZE_T; /* force downstream failure on overflow */ + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = dlmalloc(req); if (mem != 0 && calloc_must_clear(mem2chunk(mem))) __builtin_memset(mem, 0, req); return mem; + } -#endif /* !ONLY_MSPACES */ + #endif /* !ONLY_MSPACES */ /* ------------ Internal support for realloc, memalign, etc -------------- */ /* Try to realloc; only in-place unless can_move true */ static mchunkptr try_realloc_chunk(mstate m, mchunkptr p, size_t nb, int can_move) { + mchunkptr newp = 0; - size_t oldsize = chunksize(p); + size_t oldsize = chunksize(p); mchunkptr next = chunk_plus_offset(p, oldsize); - if (RTCHECK(ok_address(m, p) && ok_inuse(p) && - ok_next(p, next) && ok_pinuse(next))) { + if (RTCHECK(ok_address(m, p) && ok_inuse(p) && ok_next(p, next) && + ok_pinuse(next))) { + if (is_mmapped(p)) { + newp = mmap_resize(m, p, nb, can_move); - } - else if (oldsize >= nb) { /* already big enough */ + + } else if (oldsize >= nb) { /* already big enough */ + size_t rsize = oldsize - nb; - if (rsize >= MIN_CHUNK_SIZE) { /* split off remainder */ + if (rsize >= MIN_CHUNK_SIZE) { /* split off remainder */ mchunkptr r = chunk_plus_offset(p, nb); set_inuse(m, p, nb); set_inuse(m, r, rsize); dispose_chunk(m, r, rsize); + } + newp = p; - } - else if (next == m->top) { /* extend into top */ + + } else if (next == m->top) { /* extend into top */ + if (oldsize + m->topsize > nb) { - size_t newsize = oldsize + m->topsize; - size_t newtopsize = newsize - nb; + + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; mchunkptr newtop = chunk_plus_offset(p, nb); set_inuse(m, p, nb); - newtop->head = newtopsize |PINUSE_BIT; + newtop->head = newtopsize | PINUSE_BIT; m->top = newtop; m->topsize = newtopsize; newp = p; + } - } - else if (next == m->dv) { /* extend into dv */ + + } else if (next == m->dv) { /* extend into dv */ + size_t dvs = m->dvsize; if (oldsize + dvs >= nb) { + size_t dsize = oldsize + dvs - nb; if (dsize >= MIN_CHUNK_SIZE) { + mchunkptr r = chunk_plus_offset(p, nb); mchunkptr n = chunk_plus_offset(r, dsize); set_inuse(m, p, nb); @@ -4903,64 +5504,87 @@ static mchunkptr try_realloc_chunk(mstate m, mchunkptr p, size_t nb, clear_pinuse(n); m->dvsize = dsize; m->dv = r; - } - else { /* exhaust dv */ + + } else { /* exhaust dv */ + size_t newsize = oldsize + dvs; set_inuse(m, p, newsize); m->dvsize = 0; m->dv = 0; + } + newp = p; + } - } - else if (!cinuse(next)) { /* extend into next free chunk */ + + } else if (!cinuse(next)) { /* extend into next free chunk */ + size_t nextsize = chunksize(next); if (oldsize + nextsize >= nb) { + size_t rsize = oldsize + nextsize - nb; unlink_chunk(m, next, nextsize); if (rsize < MIN_CHUNK_SIZE) { + size_t newsize = oldsize + nextsize; set_inuse(m, p, newsize); - } - else { + + } else { + mchunkptr r = chunk_plus_offset(p, nb); set_inuse(m, p, nb); set_inuse(m, r, rsize); dispose_chunk(m, r, rsize); + } + newp = p; + } + } - } - else { + + } else { + USAGE_ERROR_ACTION(m, chunk2mem(p)); + } + return newp; + } -static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { - void* mem = 0; - if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ +static void *internal_memalign(mstate m, size_t alignment, size_t bytes) { + + void *mem = 0; + if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ alignment = MIN_CHUNK_SIZE; - if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ + if ((alignment & (alignment - SIZE_T_ONE)) != 0) { /* Ensure a power of 2 */ size_t a = MALLOC_ALIGNMENT << 1; - while (a < alignment) a <<= 1; + while (a < alignment) + a <<= 1; alignment = a; + } + if (bytes >= MAX_REQUEST - alignment) { - if (m != 0) { /* Test isn't needed but avoids compiler warning */ + + if (m != 0) { /* Test isn't needed but avoids compiler warning */ MALLOC_FAILURE_ACTION; + } - } - else { + + } else { + size_t nb = request2size(bytes); size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; mem = internal_malloc(m, req); if (mem != 0) { + mchunkptr p = mem2chunk(mem); - if (PREACTION(m)) - return 0; - if ((((size_t)(mem)) & (alignment - 1)) != 0) { /* misaligned */ + if (PREACTION(m)) return 0; + if ((((size_t)(mem)) & (alignment - 1)) != 0) { /* misaligned */ /* Find an aligned spot inside chunk. Since we need to give back leading space in a chunk of at least MIN_CHUNK_SIZE, if @@ -4969,47 +5593,59 @@ static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { We've allocated enough total room so that this is always possible. */ - char* br = (char*)mem2chunk((size_t)(((size_t)((char*)mem + alignment - - SIZE_T_ONE)) & - -alignment)); - char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? - br : br+alignment; + char * br = (char *)mem2chunk((size_t)( + ((size_t)((char *)mem + alignment - SIZE_T_ONE)) & -alignment)); + char * pos = ((size_t)(br - (char *)(p)) >= MIN_CHUNK_SIZE) + ? br + : br + alignment; mchunkptr newp = (mchunkptr)pos; - size_t leadsize = pos - (char*)(p); - size_t newsize = chunksize(p) - leadsize; + size_t leadsize = pos - (char *)(p); + size_t newsize = chunksize(p) - leadsize; - if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ + if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ newp->prev_foot = p->prev_foot + leadsize; newp->head = newsize; - } - else { /* Otherwise, give back leader, use the rest */ + + } else { /* Otherwise, give back leader, use the rest */ + set_inuse(m, newp, newsize); set_inuse(m, p, leadsize); dispose_chunk(m, p, leadsize); + } + p = newp; + } /* Give back spare room at the end */ if (!is_mmapped(p)) { + size_t size = chunksize(p); if (size > nb + MIN_CHUNK_SIZE) { - size_t remainder_size = size - nb; + + size_t remainder_size = size - nb; mchunkptr remainder = chunk_plus_offset(p, nb); set_inuse(m, p, nb); set_inuse(m, remainder, remainder_size); dispose_chunk(m, remainder, remainder_size); + } + } mem = chunk2mem(p); - assert (chunksize(p) >= nb); + assert(chunksize(p) >= nb); assert(((size_t)mem & (alignment - 1)) == 0); check_inuse_chunk(m, p); POSTACTION(m); + } + } + return mem; + } /* @@ -5019,50 +5655,50 @@ static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { bit 0 set if all elements are same size (using sizes[0]) bit 1 set if elements should be zeroed */ -static void** ialloc(mstate m, - size_t n_elements, - size_t* sizes, - int opts, - void* chunks[]) { - - size_t element_size; /* chunksize of each element, if all same */ - size_t contents_size; /* total size of elements */ - size_t array_size; /* request size of pointer array */ - void* mem; /* malloced aggregate space */ - mchunkptr p; /* corresponding chunk */ - size_t remainder_size; /* remaining bytes while splitting */ - void** marray; /* either "chunks" or malloced ptr array */ - mchunkptr array_chunk; /* chunk for malloced ptr array */ - flag_t was_enabled; /* to disable mmap */ +static void **ialloc(mstate m, size_t n_elements, size_t *sizes, int opts, + void *chunks[]) { + + size_t element_size; /* chunksize of each element, if all same */ + size_t contents_size; /* total size of elements */ + size_t array_size; /* request size of pointer array */ + void * mem; /* malloced aggregate space */ + mchunkptr p; /* corresponding chunk */ + size_t remainder_size; /* remaining bytes while splitting */ + void ** marray; /* either "chunks" or malloced ptr array */ + mchunkptr array_chunk; /* chunk for malloced ptr array */ + flag_t was_enabled; /* to disable mmap */ size_t size; size_t i; ensure_initialization(); /* compute array length, if needed */ if (chunks != 0) { - if (n_elements == 0) - return chunks; /* nothing to do */ + + if (n_elements == 0) return chunks; /* nothing to do */ marray = chunks; array_size = 0; - } - else { + + } else { + /* if empty req, must still return chunk representing empty array */ - if (n_elements == 0) - return (void**)internal_malloc(m, 0); + if (n_elements == 0) return (void **)internal_malloc(m, 0); marray = 0; - array_size = request2size(n_elements * (sizeof(void*))); + array_size = request2size(n_elements * (sizeof(void *))); + } /* compute total element size */ - if (opts & 0x1) { /* all-same-size */ + if (opts & 0x1) { /* all-same-size */ element_size = request2size(*sizes); contents_size = n_elements * element_size; - } - else { /* add up all the sizes */ + + } else { /* add up all the sizes */ + element_size = 0; contents_size = 0; for (i = 0; i != n_elements; ++i) contents_size += request2size(sizes[i]); + } size = contents_size + array_size; @@ -5075,10 +5711,8 @@ static void** ialloc(mstate m, was_enabled = use_mmap(m); disable_mmap(m); mem = internal_malloc(m, size - CHUNK_OVERHEAD); - if (was_enabled) - enable_mmap(m); - if (mem == 0) - return 0; + if (was_enabled) enable_mmap(m); + if (mem == 0) return 0; if (PREACTION(m)) return 0; p = mem2chunk(mem); @@ -5086,24 +5720,30 @@ static void** ialloc(mstate m, assert(!is_mmapped(p)); - if (opts & 0x2) { /* optionally clear the elements */ - __builtin_memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); + if (opts & 0x2) { /* optionally clear the elements */ + __builtin_memset((size_t *)mem, 0, + remainder_size - SIZE_T_SIZE - array_size); + } /* If not provided, allocate the pointer array as final part of chunk */ if (marray == 0) { - size_t array_chunk_size; + + size_t array_chunk_size; array_chunk = chunk_plus_offset(p, contents_size); array_chunk_size = remainder_size - contents_size; - marray = (void**) (chunk2mem(array_chunk)); + marray = (void **)(chunk2mem(array_chunk)); set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); remainder_size = contents_size; + } /* split out elements */ - for (i = 0; ; ++i) { + for (i = 0;; ++i) { + marray[i] = chunk2mem(p); - if (i != n_elements-1) { + if (i != n_elements - 1) { + if (element_size != 0) size = element_size; else @@ -5111,31 +5751,42 @@ static void** ialloc(mstate m, remainder_size -= size; set_size_and_pinuse_of_inuse_chunk(m, p, size); p = chunk_plus_offset(p, size); - } - else { /* the final element absorbs any overallocation slop */ + + } else { /* the final element absorbs any overallocation slop */ + set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); break; + } + } -#if DEBUG + #if DEBUG if (marray != chunks) { + /* final element must have exactly exhausted chunk */ if (element_size != 0) { + assert(remainder_size == element_size); - } - else { + + } else { + assert(remainder_size == request2size(sizes[i])); + } + check_inuse_chunk(m, mem2chunk(marray)); + } + for (i = 0; i != n_elements; ++i) check_inuse_chunk(m, mem2chunk(marray[i])); -#endif /* DEBUG */ + #endif /* DEBUG */ POSTACTION(m); return marray; + } /* Try to free all pointers in the given array. @@ -5145,316 +5796,431 @@ static void** ialloc(mstate m, chunks before freeing, which will occur often if allocated with ialloc or the array is sorted. */ -static size_t internal_bulk_free(mstate m, void* array[], size_t nelem) { +static size_t internal_bulk_free(mstate m, void *array[], size_t nelem) { + size_t unfreed = 0; if (!PREACTION(m)) { - void** a; - void** fence = &(array[nelem]); + + void **a; + void **fence = &(array[nelem]); for (a = array; a != fence; ++a) { - void* mem = *a; + + void *mem = *a; if (mem != 0) { + mchunkptr p = mem2chunk(mem); - size_t psize = chunksize(p); -#if FOOTERS + size_t psize = chunksize(p); + #if FOOTERS if (get_mstate_for(p) != m) { + ++unfreed; continue; + } -#endif + + #endif check_inuse_chunk(m, p); *a = 0; if (RTCHECK(ok_address(m, p) && ok_inuse(p))) { - void ** b = a + 1; /* try to merge with next chunk */ + + void ** b = a + 1; /* try to merge with next chunk */ mchunkptr next = next_chunk(p); if (b != fence && *b == chunk2mem(next)) { + size_t newsize = chunksize(next) + psize; set_inuse(m, p, newsize); *b = chunk2mem(p); - } - else + + } else + dispose_chunk(m, p, psize); - } - else { + + } else { + CORRUPTION_ERROR_ACTION(m); break; + } + } + } - if (should_trim(m, m->topsize)) - sys_trim(m, 0); + + if (should_trim(m, m->topsize)) sys_trim(m, 0); POSTACTION(m); + } + return unfreed; + } -/* Traversal */ -#if MALLOC_INSPECT_ALL + /* Traversal */ + #if MALLOC_INSPECT_ALL static void internal_inspect_all(mstate m, - void(*handler)(void *start, - void *end, - size_t used_bytes, - void* callback_arg), - void* arg) { + void (*handler)(void *start, void *end, + size_t used_bytes, + void * callback_arg), + void *arg) { + if (is_initialized(m)) { - mchunkptr top = m->top; + + mchunkptr top = m->top; msegmentptr s; for (s = &m->seg; s != 0; s = s->next) { + mchunkptr q = align_as_chunk(s->base); while (segment_holds(s, q) && q->head != FENCEPOST_HEAD) { + mchunkptr next = next_chunk(q); - size_t sz = chunksize(q); - size_t used; - void* start; + size_t sz = chunksize(q); + size_t used; + void * start; if (is_inuse(q)) { - used = sz - CHUNK_OVERHEAD; /* must not be mmapped */ + + used = sz - CHUNK_OVERHEAD; /* must not be mmapped */ start = chunk2mem(q); - } - else { + + } else { + used = 0; - if (is_small(sz)) { /* offset by possible bookkeeping */ - start = (void*)((char*)q + sizeof(struct malloc_chunk)); - } - else { - start = (void*)((char*)q + sizeof(struct malloc_tree_chunk)); + if (is_small(sz)) { /* offset by possible bookkeeping */ + start = (void *)((char *)q + sizeof(struct malloc_chunk)); + + } else { + + start = (void *)((char *)q + sizeof(struct malloc_tree_chunk)); + } + } - if (start < (void*)next) /* skip if all space is bookkeeping */ + + if (start < (void *)next) /* skip if all space is bookkeeping */ handler(start, next, used, arg); - if (q == top) - break; + if (q == top) break; q = next; + } + } + } + } -#endif /* MALLOC_INSPECT_ALL */ + + #endif /* MALLOC_INSPECT_ALL */ /* ------------------ Exported realloc, memalign, etc -------------------- */ -#if !ONLY_MSPACES + #if !ONLY_MSPACES -void* dlrealloc(void* oldmem, size_t bytes) { - void* mem = 0; +void *dlrealloc(void *oldmem, size_t bytes) { + + void *mem = 0; if (oldmem == 0) { + mem = dlmalloc(bytes); - } - else if (bytes >= MAX_REQUEST) { + + } else if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } -#ifdef REALLOC_ZERO_BYTES_FREES + + #ifdef REALLOC_ZERO_BYTES_FREES else if (bytes == 0) { + dlfree(oldmem); + } -#endif /* REALLOC_ZERO_BYTES_FREES */ + + #endif /* REALLOC_ZERO_BYTES_FREES */ else { - size_t nb = request2size(bytes); + + size_t nb = request2size(bytes); mchunkptr oldp = mem2chunk(oldmem); -#if ! FOOTERS + #if !FOOTERS mstate m = gm; -#else /* FOOTERS */ + #else /* FOOTERS */ mstate m = get_mstate_for(oldp); if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); return 0; + } -#endif /* FOOTERS */ + + #endif /* FOOTERS */ if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); POSTACTION(m); if (newp != 0) { + check_inuse_chunk(m, newp); mem = chunk2mem(newp); - } - else { + + } else { + mem = internal_malloc(m, bytes); if (mem != 0) { + size_t oc = chunksize(oldp) - overhead_for(oldp); - __builtin_memcpy(mem, oldmem, (oc < bytes)? oc : bytes); + __builtin_memcpy(mem, oldmem, (oc < bytes) ? oc : bytes); internal_free(m, oldmem); + } + } + } + } + return mem; + } -void* dlrealloc_in_place(void* oldmem, size_t bytes) { - void* mem = 0; +void *dlrealloc_in_place(void *oldmem, size_t bytes) { + + void *mem = 0; if (oldmem != 0) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; - } - else { - size_t nb = request2size(bytes); + + } else { + + size_t nb = request2size(bytes); mchunkptr oldp = mem2chunk(oldmem); -#if ! FOOTERS + #if !FOOTERS mstate m = gm; -#else /* FOOTERS */ + #else /* FOOTERS */ mstate m = get_mstate_for(oldp); if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); return 0; + } -#endif /* FOOTERS */ + + #endif /* FOOTERS */ if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); POSTACTION(m); if (newp == oldp) { + check_inuse_chunk(m, newp); mem = oldmem; + } + } + } + } + return mem; + } -void* dlmemalign(size_t alignment, size_t bytes) { - if (alignment <= MALLOC_ALIGNMENT) { - return dlmalloc(bytes); - } +void *dlmemalign(size_t alignment, size_t bytes) { + + if (alignment <= MALLOC_ALIGNMENT) { return dlmalloc(bytes); } return internal_memalign(gm, alignment, bytes); + } -int dlposix_memalign(void** pp, size_t alignment, size_t bytes) { - void* mem = 0; +int dlposix_memalign(void **pp, size_t alignment, size_t bytes) { + + void *mem = 0; if (alignment == MALLOC_ALIGNMENT) mem = dlmalloc(bytes); else { - size_t d = alignment / sizeof(void*); - size_t r = alignment % sizeof(void*); - if (r != 0 || d == 0 || (d & (d-SIZE_T_ONE)) != 0) + + size_t d = alignment / sizeof(void *); + size_t r = alignment % sizeof(void *); + if (r != 0 || d == 0 || (d & (d - SIZE_T_ONE)) != 0) return EINVAL; else if (bytes <= MAX_REQUEST - alignment) { - if (alignment < MIN_CHUNK_SIZE) - alignment = MIN_CHUNK_SIZE; + + if (alignment < MIN_CHUNK_SIZE) alignment = MIN_CHUNK_SIZE; mem = internal_memalign(gm, alignment, bytes); + } + } + if (mem == 0) return ENOMEM; else { + *pp = mem; return 0; + } + } -void* dlvalloc(size_t bytes) { +void *dlvalloc(size_t bytes) { + size_t pagesz; ensure_initialization(); pagesz = mparams.page_size; return dlmemalign(pagesz, bytes); + } -void* dlpvalloc(size_t bytes) { +void *dlpvalloc(size_t bytes) { + size_t pagesz; ensure_initialization(); pagesz = mparams.page_size; - return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); + return dlmemalign(pagesz, + (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); + } -void** dlindependent_calloc(size_t n_elements, size_t elem_size, - void* chunks[]) { - size_t sz = elem_size; /* serves as 1-element array */ +void **dlindependent_calloc(size_t n_elements, size_t elem_size, + void *chunks[]) { + + size_t sz = elem_size; /* serves as 1-element array */ return ialloc(gm, n_elements, &sz, 3, chunks); + } -void** dlindependent_comalloc(size_t n_elements, size_t sizes[], - void* chunks[]) { +void **dlindependent_comalloc(size_t n_elements, size_t sizes[], + void *chunks[]) { + return ialloc(gm, n_elements, sizes, 0, chunks); + } -size_t dlbulk_free(void* array[], size_t nelem) { +size_t dlbulk_free(void *array[], size_t nelem) { + return internal_bulk_free(gm, array, nelem); + } -#if MALLOC_INSPECT_ALL -void dlmalloc_inspect_all(void(*handler)(void *start, - void *end, - size_t used_bytes, - void* callback_arg), - void* arg) { + #if MALLOC_INSPECT_ALL +void dlmalloc_inspect_all(void (*handler)(void *start, void *end, + size_t used_bytes, + void * callback_arg), + void *arg) { + ensure_initialization(); if (!PREACTION(gm)) { + internal_inspect_all(gm, handler, arg); POSTACTION(gm); + } + } -#endif /* MALLOC_INSPECT_ALL */ + + #endif /* MALLOC_INSPECT_ALL */ int dlmalloc_trim(size_t pad) { + int result = 0; ensure_initialization(); if (!PREACTION(gm)) { + result = sys_trim(gm, pad); POSTACTION(gm); + } + return result; + } size_t dlmalloc_footprint(void) { + return gm->footprint; + } size_t dlmalloc_max_footprint(void) { + return gm->max_footprint; + } size_t dlmalloc_footprint_limit(void) { + size_t maf = gm->footprint_limit; return maf == 0 ? MAX_SIZE_T : maf; + } size_t dlmalloc_set_footprint_limit(size_t bytes) { - size_t result; /* invert sense of 0 */ - if (bytes == 0) - result = granularity_align(1); /* Use minimal size */ + + size_t result; /* invert sense of 0 */ + if (bytes == 0) result = granularity_align(1); /* Use minimal size */ if (bytes == MAX_SIZE_T) - result = 0; /* disable */ + result = 0; /* disable */ else result = granularity_align(bytes); return gm->footprint_limit = result; + } -#if !NO_MALLINFO + #if !NO_MALLINFO struct mallinfo dlmallinfo(void) { + return internal_mallinfo(gm); + } -#endif /* NO_MALLINFO */ -#if !NO_MALLOC_STATS + #endif /* NO_MALLINFO */ + + #if !NO_MALLOC_STATS void dlmalloc_stats() { + internal_malloc_stats(gm); + } -#endif /* NO_MALLOC_STATS */ + + #endif /* NO_MALLOC_STATS */ int dlmallopt(int param_number, int value) { + return change_mparam(param_number, value); + } -size_t dlmalloc_usable_size(void* mem) { +size_t dlmalloc_usable_size(void *mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); - if (is_inuse(p)) - return chunksize(p) - overhead_for(p); + if (is_inuse(p)) return chunksize(p) - overhead_for(p); + } + return 0; + } -#endif /* !ONLY_MSPACES */ + #endif /* !ONLY_MSPACES */ /* ----------------------------- user mspaces ---------------------------- */ -#if MSPACES + #if MSPACES + +static mstate init_user_mstate(char *tbase, size_t tsize) { -static mstate init_user_mstate(char* tbase, size_t tsize) { - size_t msize = pad_request(sizeof(struct malloc_state)); + size_t msize = pad_request(sizeof(struct malloc_state)); mchunkptr mn; mchunkptr msp = align_as_chunk(tbase); - mstate m = (mstate)(chunk2mem(msp)); + mstate m = (mstate)(chunk2mem(msp)); __builtin_memset(m, 0, msize); (void)INITIAL_LOCK(&m->mutex); - msp->head = (msize|INUSE_BITS); + msp->head = (msize | INUSE_BITS); m->seg.base = m->least_addr = tbase; m->seg.size = m->footprint = m->max_footprint = tsize; m->magic = mparams.magic; @@ -5465,82 +6231,111 @@ static mstate init_user_mstate(char* tbase, size_t tsize) { disable_contiguous(m); init_bins(m); mn = next_chunk(mem2chunk(m)); - init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); + init_top(m, mn, (size_t)((tbase + tsize) - (char *)mn) - TOP_FOOT_SIZE); check_top_chunk(m, m->top); return m; + } mspace create_mspace(size_t capacity, int locked) { + mstate m = 0; size_t msize; ensure_initialization(); msize = pad_request(sizeof(struct malloc_state)); - if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { - size_t rs = ((capacity == 0)? mparams.granularity : - (capacity + TOP_FOOT_SIZE + msize)); + if (capacity < (size_t) - (msize + TOP_FOOT_SIZE + mparams.page_size)) { + + size_t rs = ((capacity == 0) ? mparams.granularity + : (capacity + TOP_FOOT_SIZE + msize)); size_t tsize = granularity_align(rs); - char* tbase = (char*)(CALL_MMAP(tsize)); + char * tbase = (char *)(CALL_MMAP(tsize)); if (tbase != CMFAIL) { + m = init_user_mstate(tbase, tsize); m->seg.sflags = USE_MMAP_BIT; set_lock(m, locked); + } + } + return (mspace)m; + } -mspace create_mspace_with_base(void* base, size_t capacity, int locked) { +mspace create_mspace_with_base(void *base, size_t capacity, int locked) { + mstate m = 0; size_t msize; ensure_initialization(); msize = pad_request(sizeof(struct malloc_state)); if (capacity > msize + TOP_FOOT_SIZE && - capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { - m = init_user_mstate((char*)base, capacity); + capacity < (size_t) - (msize + TOP_FOOT_SIZE + mparams.page_size)) { + + m = init_user_mstate((char *)base, capacity); m->seg.sflags = EXTERN_BIT; set_lock(m, locked); + } + return (mspace)m; + } int mspace_track_large_chunks(mspace msp, int enable) { - int ret = 0; + + int ret = 0; mstate ms = (mstate)msp; if (!PREACTION(ms)) { - if (!use_mmap(ms)) { - ret = 1; - } + + if (!use_mmap(ms)) { ret = 1; } if (!enable) { + enable_mmap(ms); + } else { + disable_mmap(ms); + } + POSTACTION(ms); + } + return ret; + } size_t destroy_mspace(mspace msp) { + size_t freed = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { + msegmentptr sp = &ms->seg; - (void)DESTROY_LOCK(&ms->mutex); /* destroy before unmapped */ + (void)DESTROY_LOCK(&ms->mutex); /* destroy before unmapped */ while (sp != 0) { - char* base = sp->base; + + char * base = sp->base; size_t size = sp->size; flag_t flag = sp->sflags; - (void)base; /* placate people compiling -Wunused-variable */ + (void)base; /* placate people compiling -Wunused-variable */ sp = sp->next; if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && CALL_MUNMAP(base, size) == 0) freed += size; + } + + } else { + + USAGE_ERROR_ACTION(ms, ms); + } - else { - USAGE_ERROR_ACTION(ms,ms); - } + return freed; + } /* @@ -5548,25 +6343,31 @@ size_t destroy_mspace(mspace msp) { versions. This is not so nice but better than the alternatives. */ -void* mspace_malloc(mspace msp, size_t bytes) { +void *mspace_malloc(mspace msp, size_t bytes) { + mstate ms = (mstate)msp; if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); + + USAGE_ERROR_ACTION(ms, ms); return 0; + } + if (!PREACTION(ms)) { - void* mem; + + void * mem; size_t nb; if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; binmap_t smallbits; - nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + nb = (bytes < MIN_REQUEST) ? MIN_CHUNK_SIZE : pad_request(bytes); idx = small_index(nb); smallbits = ms->smallmap >> idx; - if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ mchunkptr b, p; - idx += ~smallbits & 1; /* Uses next bin if idx empty */ + idx += ~smallbits & 1; /* Uses next bin if idx empty */ b = smallbin_at(ms, idx); p = b->fd; assert(chunksize(p) == small_index2size(idx)); @@ -5575,15 +6376,17 @@ void* mspace_malloc(mspace msp, size_t bytes) { mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; + } else if (nb > ms->dvsize) { - if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ mchunkptr b, p, r; - size_t rsize; - bindex_t i; - binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); - binmap_t leastbit = least_bit(leftbits); + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); b = smallbin_at(ms, i); p = b->fd; @@ -5594,54 +6397,71 @@ void* mspace_malloc(mspace msp, size_t bytes) { if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(ms, p, small_index2size(i)); else { + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); r = chunk_plus_offset(p, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(ms, r, rsize); + } + mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; + } else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); goto postaction; + } + } - } - else if (bytes >= MAX_REQUEST) + + } else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ else { + nb = pad_request(bytes); if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); goto postaction; + } + } if (nb <= ms->dvsize) { - size_t rsize = ms->dvsize - nb; + + size_t rsize = ms->dvsize - nb; mchunkptr p = ms->dv; - if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ mchunkptr r = ms->dv = chunk_plus_offset(p, nb); ms->dvsize = rsize; set_size_and_pinuse_of_free_chunk(r, rsize); set_size_and_pinuse_of_inuse_chunk(ms, p, nb); - } - else { /* exhaust dv */ + + } else { /* exhaust dv */ + size_t dvs = ms->dvsize; ms->dvsize = 0; ms->dv = 0; set_inuse_and_pinuse(ms, p, dvs); + } + mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; + } - else if (nb < ms->topsize) { /* Split top */ - size_t rsize = ms->topsize -= nb; + else if (nb < ms->topsize) { /* Split top */ + size_t rsize = ms->topsize -= nb; mchunkptr p = ms->top; mchunkptr r = ms->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; @@ -5650,6 +6470,7 @@ void* mspace_malloc(mspace msp, size_t bytes) { check_top_chunk(ms, ms->top); check_malloced_chunk(ms, mem, nb); goto postaction; + } mem = sys_alloc(ms, nb); @@ -5657,372 +6478,519 @@ void* mspace_malloc(mspace msp, size_t bytes) { postaction: POSTACTION(ms); return mem; + } return 0; + } -void mspace_free(mspace msp, void* mem) { +void mspace_free(mspace msp, void *mem) { + if (mem != 0) { - mchunkptr p = mem2chunk(mem); -#if FOOTERS + + mchunkptr p = mem2chunk(mem); + #if FOOTERS mstate fm = get_mstate_for(p); - (void)msp; /* placate people compiling -Wunused */ -#else /* FOOTERS */ + (void)msp; /* placate people compiling -Wunused */ + #else /* FOOTERS */ mstate fm = (mstate)msp; -#endif /* FOOTERS */ + #endif /* FOOTERS */ if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); return; + } + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { - size_t psize = chunksize(p); + + size_t psize = chunksize(p); mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { + size_t prevsize = p->prev_foot; if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + if (CALL_MUNMAP((char *)p - prevsize, psize) == 0) fm->footprint -= psize; goto postaction; - } - else { + + } else { + mchunkptr prev = chunk_minus_offset(p, prevsize); psize += prevsize; p = prev; - if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); - } - else if ((next->head & INUSE_BITS) == INUSE_BITS) { + + } else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; set_free_with_pinuse(p, psize, next); goto postaction; + } - } - else + + } else + goto erroraction; + } + } if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { - if (!cinuse(next)) { /* consolidate forward */ + + if (!cinuse(next)) { /* consolidate forward */ if (next == fm->top) { + size_t tsize = fm->topsize += psize; fm->top = p; p->head = tsize | PINUSE_BIT; if (p == fm->dv) { + fm->dv = 0; fm->dvsize = 0; + } - if (should_trim(fm, tsize)) - sys_trim(fm, 0); + + if (should_trim(fm, tsize)) sys_trim(fm, 0); goto postaction; - } - else if (next == fm->dv) { + + } else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; fm->dv = p; set_size_and_pinuse_of_free_chunk(p, dsize); goto postaction; - } - else { + + } else { + size_t nsize = chunksize(next); psize += nsize; unlink_chunk(fm, next, nsize); set_size_and_pinuse_of_free_chunk(p, psize); if (p == fm->dv) { + fm->dvsize = psize; goto postaction; + } + } - } - else + + } else + set_free_with_pinuse(p, psize, next); if (is_small(psize)) { + insert_small_chunk(fm, p, psize); check_free_chunk(fm, p); - } - else { + + } else { + tchunkptr tp = (tchunkptr)p; insert_large_chunk(fm, tp, psize); check_free_chunk(fm, p); - if (--fm->release_checks == 0) - release_unused_segments(fm); + if (--fm->release_checks == 0) release_unused_segments(fm); + } + goto postaction; + } + } + erroraction: USAGE_ERROR_ACTION(fm, p); postaction: POSTACTION(fm); + } + } + } -void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { - void* mem; +void *mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { + + void * mem; size_t req = 0; mstate ms = (mstate)msp; if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); + + USAGE_ERROR_ACTION(ms, ms); return 0; + } + if (n_elements != 0) { + req = n_elements * elem_size; if (((n_elements | elem_size) & ~(size_t)0xffff) && (req / n_elements != elem_size)) - req = MAX_SIZE_T; /* force downstream failure on overflow */ + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = internal_malloc(ms, req); if (mem != 0 && calloc_must_clear(mem2chunk(mem))) __builtin_memset(mem, 0, req); return mem; + } -void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) { - void* mem = 0; +void *mspace_realloc(mspace msp, void *oldmem, size_t bytes) { + + void *mem = 0; if (oldmem == 0) { + mem = mspace_malloc(msp, bytes); - } - else if (bytes >= MAX_REQUEST) { + + } else if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } -#ifdef REALLOC_ZERO_BYTES_FREES + + #ifdef REALLOC_ZERO_BYTES_FREES else if (bytes == 0) { + mspace_free(msp, oldmem); + } -#endif /* REALLOC_ZERO_BYTES_FREES */ + + #endif /* REALLOC_ZERO_BYTES_FREES */ else { - size_t nb = request2size(bytes); + + size_t nb = request2size(bytes); mchunkptr oldp = mem2chunk(oldmem); -#if ! FOOTERS + #if !FOOTERS mstate m = (mstate)msp; -#else /* FOOTERS */ + #else /* FOOTERS */ mstate m = get_mstate_for(oldp); if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); return 0; + } -#endif /* FOOTERS */ + + #endif /* FOOTERS */ if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); POSTACTION(m); if (newp != 0) { + check_inuse_chunk(m, newp); mem = chunk2mem(newp); - } - else { + + } else { + mem = mspace_malloc(m, bytes); if (mem != 0) { + size_t oc = chunksize(oldp) - overhead_for(oldp); - __builtin_memcpy(mem, oldmem, (oc < bytes)? oc : bytes); + __builtin_memcpy(mem, oldmem, (oc < bytes) ? oc : bytes); mspace_free(m, oldmem); + } + } + } + } + return mem; + } -void* mspace_realloc_in_place(mspace msp, void* oldmem, size_t bytes) { - void* mem = 0; +void *mspace_realloc_in_place(mspace msp, void *oldmem, size_t bytes) { + + void *mem = 0; if (oldmem != 0) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; - } - else { - size_t nb = request2size(bytes); + + } else { + + size_t nb = request2size(bytes); mchunkptr oldp = mem2chunk(oldmem); -#if ! FOOTERS + #if !FOOTERS mstate m = (mstate)msp; -#else /* FOOTERS */ + #else /* FOOTERS */ mstate m = get_mstate_for(oldp); - (void)msp; /* placate people compiling -Wunused */ + (void)msp; /* placate people compiling -Wunused */ if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); return 0; + } -#endif /* FOOTERS */ + + #endif /* FOOTERS */ if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); POSTACTION(m); if (newp == oldp) { + check_inuse_chunk(m, newp); mem = oldmem; + } + } + } + } + return mem; + } -void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { +void *mspace_memalign(mspace msp, size_t alignment, size_t bytes) { + mstate ms = (mstate)msp; if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); + + USAGE_ERROR_ACTION(ms, ms); return 0; + } - if (alignment <= MALLOC_ALIGNMENT) - return mspace_malloc(msp, bytes); + + if (alignment <= MALLOC_ALIGNMENT) return mspace_malloc(msp, bytes); return internal_memalign(ms, alignment, bytes); + } -void** mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]) { - size_t sz = elem_size; /* serves as 1-element array */ +void **mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void *chunks[]) { + + size_t sz = elem_size; /* serves as 1-element array */ mstate ms = (mstate)msp; if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); + + USAGE_ERROR_ACTION(ms, ms); return 0; + } + return ialloc(ms, n_elements, &sz, 3, chunks); + } -void** mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]) { +void **mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void *chunks[]) { + mstate ms = (mstate)msp; if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); + + USAGE_ERROR_ACTION(ms, ms); return 0; + } + return ialloc(ms, n_elements, sizes, 0, chunks); + } -size_t mspace_bulk_free(mspace msp, void* array[], size_t nelem) { +size_t mspace_bulk_free(mspace msp, void *array[], size_t nelem) { + return internal_bulk_free((mstate)msp, array, nelem); + } -#if MALLOC_INSPECT_ALL + #if MALLOC_INSPECT_ALL void mspace_inspect_all(mspace msp, - void(*handler)(void *start, - void *end, - size_t used_bytes, - void* callback_arg), - void* arg) { + void (*handler)(void *start, void *end, + size_t used_bytes, void *callback_arg), + void *arg) { + mstate ms = (mstate)msp; if (ok_magic(ms)) { + if (!PREACTION(ms)) { + internal_inspect_all(ms, handler, arg); POSTACTION(ms); + } + + } else { + + USAGE_ERROR_ACTION(ms, ms); + } - else { - USAGE_ERROR_ACTION(ms,ms); - } + } -#endif /* MALLOC_INSPECT_ALL */ + + #endif /* MALLOC_INSPECT_ALL */ int mspace_trim(mspace msp, size_t pad) { - int result = 0; + + int result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { + if (!PREACTION(ms)) { + result = sys_trim(ms, pad); POSTACTION(ms); + } + + } else { + + USAGE_ERROR_ACTION(ms, ms); + } - else { - USAGE_ERROR_ACTION(ms,ms); - } + return result; + } -#if !NO_MALLOC_STATS + #if !NO_MALLOC_STATS void mspace_malloc_stats(mspace msp) { + mstate ms = (mstate)msp; if (ok_magic(ms)) { + internal_malloc_stats(ms); + + } else { + + USAGE_ERROR_ACTION(ms, ms); + } - else { - USAGE_ERROR_ACTION(ms,ms); - } + } -#endif /* NO_MALLOC_STATS */ + + #endif /* NO_MALLOC_STATS */ size_t mspace_footprint(mspace msp) { + size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { + result = ms->footprint; + + } else { + + USAGE_ERROR_ACTION(ms, ms); + } - else { - USAGE_ERROR_ACTION(ms,ms); - } + return result; + } size_t mspace_max_footprint(mspace msp) { + size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { + result = ms->max_footprint; + + } else { + + USAGE_ERROR_ACTION(ms, ms); + } - else { - USAGE_ERROR_ACTION(ms,ms); - } + return result; + } size_t mspace_footprint_limit(mspace msp) { + size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { + size_t maf = ms->footprint_limit; result = (maf == 0) ? MAX_SIZE_T : maf; + + } else { + + USAGE_ERROR_ACTION(ms, ms); + } - else { - USAGE_ERROR_ACTION(ms,ms); - } + return result; + } size_t mspace_set_footprint_limit(mspace msp, size_t bytes) { + size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { - if (bytes == 0) - result = granularity_align(1); /* Use minimal size */ + + if (bytes == 0) result = granularity_align(1); /* Use minimal size */ if (bytes == MAX_SIZE_T) - result = 0; /* disable */ + result = 0; /* disable */ else result = granularity_align(bytes); ms->footprint_limit = result; + + } else { + + USAGE_ERROR_ACTION(ms, ms); + } - else { - USAGE_ERROR_ACTION(ms,ms); - } + return result; + } -#if !NO_MALLINFO + #if !NO_MALLINFO struct mallinfo mspace_mallinfo(mspace msp) { + mstate ms = (mstate)msp; - if (!ok_magic(ms)) { - USAGE_ERROR_ACTION(ms,ms); - } + if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms, ms); } return internal_mallinfo(ms); + } -#endif /* NO_MALLINFO */ -size_t mspace_usable_size(const void* mem) { + #endif /* NO_MALLINFO */ + +size_t mspace_usable_size(const void *mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); - if (is_inuse(p)) - return chunksize(p) - overhead_for(p); + if (is_inuse(p)) return chunksize(p) - overhead_for(p); + } + return 0; + } int mspace_mallopt(int param_number, int value) { + return change_mparam(param_number, value); -} -#endif /* MSPACES */ +} + #endif /* MSPACES */ /* -------------------- Alternative MORECORE functions ------------------- */ @@ -6067,35 +7035,48 @@ int mspace_mallopt(int param_number, int value) { void *osMoreCore(int size) { + void *ptr = 0; static void *sbrk_top = 0; if (size > 0) { + if (size < MINIMUM_MORECORE_SIZE) size = MINIMUM_MORECORE_SIZE; if (CurrentExecutionLevel() == kTaskLevel) ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); if (ptr == 0) { + return (void *) MFAIL; + } + // save ptrs so they can be freed during cleanup our_os_pools[next_os_pool] = ptr; next_os_pool++; ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); sbrk_top = (char *) ptr + size; return ptr; + } + else if (size < 0) { + // we don't currently support shrink behavior return (void *) MFAIL; + } + else { + return sbrk_top; + } + } // cleanup any allocated memory pools @@ -6103,19 +7084,22 @@ int mspace_mallopt(int param_number, int value) { void osCleanupMem(void) { + void **ptr; for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) if (*ptr) { + PoolDeallocate(*ptr); *ptr = 0; + } + } */ - /* ----------------------------------------------------------------------- History: v2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea @@ -6335,4 +7319,5 @@ History: */ -#endif // __GLIBC__ +#endif // __GLIBC__ + diff --git a/qemu_mode/libqasan/hooks.c b/qemu_mode/libqasan/hooks.c index 3bb4cc42..405dddae 100644 --- a/qemu_mode/libqasan/hooks.c +++ b/qemu_mode/libqasan/hooks.c @@ -174,7 +174,9 @@ char *fgets(char *s, int size, FILE *stream) { QASAN_DEBUG("%14p: fgets(%p, %d, %p)\n", rtv, s, size, stream); QASAN_STORE(s, size); +#ifndef __ANDROID__ QASAN_LOAD(stream, sizeof(FILE)); +#endif char *r = __lq_libc_fgets(s, size, stream); QASAN_DEBUG("\t\t = %p\n", r); diff --git a/qemu_mode/libqasan/libqasan.c b/qemu_mode/libqasan/libqasan.c index 11b50270..9fc4ef7a 100644 --- a/qemu_mode/libqasan/libqasan.c +++ b/qemu_mode/libqasan/libqasan.c @@ -72,7 +72,7 @@ void __libqasan_print_maps(void) { QASAN_LOG("QEMU-AddressSanitizer (v%s)\n", QASAN_VERSTR); QASAN_LOG( - "Copyright (C) 2019-2020 Andrea Fioraldi \n"); + "Copyright (C) 2019-2021 Andrea Fioraldi \n"); QASAN_LOG("\n"); if (__qasan_log) __libqasan_print_maps(); diff --git a/qemu_mode/libqasan/malloc.c b/qemu_mode/libqasan/malloc.c index 54c1096a..5a2d2a0c 100644 --- a/qemu_mode/libqasan/malloc.c +++ b/qemu_mode/libqasan/malloc.c @@ -51,9 +51,9 @@ typedef struct { struct chunk_begin { size_t requested_size; - void* aligned_orig; // NULL if not aligned - struct chunk_begin* next; - struct chunk_begin* prev; + void * aligned_orig; // NULL if not aligned + struct chunk_begin *next; + struct chunk_begin *prev; char redzone[REDZONE_SIZE]; }; @@ -68,45 +68,45 @@ struct chunk_struct { #ifdef __GLIBC__ -void* (*__lq_libc_malloc)(size_t); -void (*__lq_libc_free)(void*); -#define backend_malloc __lq_libc_malloc -#define backend_free __lq_libc_free +void *(*__lq_libc_malloc)(size_t); +void (*__lq_libc_free)(void *); + #define backend_malloc __lq_libc_malloc + #define backend_free __lq_libc_free -#define TMP_ZONE_SIZE 4096 + #define TMP_ZONE_SIZE 4096 static int __tmp_alloc_zone_idx; static unsigned char __tmp_alloc_zone[TMP_ZONE_SIZE]; #else // From dlmalloc.c -void* dlmalloc(size_t); -void dlfree(void*); -#define backend_malloc dlmalloc -#define backend_free dlfree +void * dlmalloc(size_t); +void dlfree(void *); + #define backend_malloc dlmalloc + #define backend_free dlfree #endif int __libqasan_malloc_initialized; -static struct chunk_begin* quarantine_top; -static struct chunk_begin* quarantine_end; +static struct chunk_begin *quarantine_top; +static struct chunk_begin *quarantine_end; static size_t quarantine_bytes; #ifdef __BIONIC__ -static pthread_mutex_t quarantine_lock; -#define LOCK_TRY pthread_mutex_trylock -#define LOCK_INIT pthread_mutex_init -#define LOCK_UNLOCK pthread_mutex_unlock +static pthread_mutex_t quarantine_lock; + #define LOCK_TRY pthread_mutex_trylock + #define LOCK_INIT pthread_mutex_init + #define LOCK_UNLOCK pthread_mutex_unlock #else -static pthread_spinlock_t quarantine_lock; -#define LOCK_TRY pthread_spin_trylock -#define LOCK_INIT pthread_spin_init -#define LOCK_UNLOCK pthread_spin_unlock +static pthread_spinlock_t quarantine_lock; + #define LOCK_TRY pthread_spin_trylock + #define LOCK_INIT pthread_spin_init + #define LOCK_UNLOCK pthread_spin_unlock #endif // need qasan disabled -static int quanratine_push(struct chunk_begin* ck) { +static int quanratine_push(struct chunk_begin *ck) { if (ck->requested_size >= QUARANTINE_MAX_BYTES) return 0; @@ -114,7 +114,7 @@ static int quanratine_push(struct chunk_begin* ck) { while (ck->requested_size + quarantine_bytes >= QUARANTINE_MAX_BYTES) { - struct chunk_begin* tmp = quarantine_end; + struct chunk_begin *tmp = quarantine_end; quarantine_end = tmp->prev; quarantine_bytes -= tmp->requested_size; @@ -154,23 +154,23 @@ void __libqasan_init_malloc(void) { } -size_t __libqasan_malloc_usable_size(void* ptr) { +size_t __libqasan_malloc_usable_size(void *ptr) { - char* p = ptr; + char *p = ptr; p -= sizeof(struct chunk_begin); - return ((struct chunk_begin*)p)->requested_size; + return ((struct chunk_begin *)p)->requested_size; } -void* __libqasan_malloc(size_t size) { +void *__libqasan_malloc(size_t size) { if (!__libqasan_malloc_initialized) { - + __libqasan_init_malloc(); #ifdef __GLIBC__ - void* r = &__tmp_alloc_zone[__tmp_alloc_zone_idx]; + void *r = &__tmp_alloc_zone[__tmp_alloc_zone_idx]; if (size & (ALLOC_ALIGN_SIZE - 1)) __tmp_alloc_zone_idx += @@ -185,7 +185,7 @@ void* __libqasan_malloc(size_t size) { int state = QASAN_SWAP(QASAN_DISABLED); // disable qasan for this thread - struct chunk_begin* p = backend_malloc(sizeof(struct chunk_struct) + size); + struct chunk_begin *p = backend_malloc(sizeof(struct chunk_struct) + size); QASAN_SWAP(state); @@ -197,14 +197,14 @@ void* __libqasan_malloc(size_t size) { p->aligned_orig = NULL; p->next = p->prev = NULL; - QASAN_ALLOC(&p[1], (char*)&p[1] + size); + QASAN_ALLOC(&p[1], (char *)&p[1] + size); QASAN_POISON(p->redzone, REDZONE_SIZE, ASAN_HEAP_LEFT_RZ); if (size & (ALLOC_ALIGN_SIZE - 1)) - QASAN_POISON((char*)&p[1] + size, + QASAN_POISON((char *)&p[1] + size, (size & ~(ALLOC_ALIGN_SIZE - 1)) + 8 - size + REDZONE_SIZE, ASAN_HEAP_RIGHT_RZ); else - QASAN_POISON((char*)&p[1] + size, REDZONE_SIZE, ASAN_HEAP_RIGHT_RZ); + QASAN_POISON((char *)&p[1] + size, REDZONE_SIZE, ASAN_HEAP_RIGHT_RZ); __builtin_memset(&p[1], 0xff, size); @@ -212,17 +212,17 @@ void* __libqasan_malloc(size_t size) { } -void __libqasan_free(void* ptr) { +void __libqasan_free(void *ptr) { if (!ptr) return; - + #ifdef __GLIBC__ - if (ptr >= (void*)__tmp_alloc_zone && - ptr < ((void*)__tmp_alloc_zone + TMP_ZONE_SIZE)) + if (ptr >= (void *)__tmp_alloc_zone && + ptr < ((void *)__tmp_alloc_zone + TMP_ZONE_SIZE)) return; #endif - struct chunk_begin* p = ptr; + struct chunk_begin *p = ptr; p -= 1; size_t n = p->requested_size; @@ -249,21 +249,22 @@ void __libqasan_free(void* ptr) { } -void* __libqasan_calloc(size_t nmemb, size_t size) { +void *__libqasan_calloc(size_t nmemb, size_t size) { size *= nmemb; #ifdef __GLIBC__ if (!__libqasan_malloc_initialized) { - void* r = &__tmp_alloc_zone[__tmp_alloc_zone_idx]; + void *r = &__tmp_alloc_zone[__tmp_alloc_zone_idx]; __tmp_alloc_zone_idx += size; return r; } + #endif - char* p = __libqasan_malloc(size); + char *p = __libqasan_malloc(size); if (!p) return NULL; __builtin_memset(p, 0, size); @@ -272,14 +273,14 @@ void* __libqasan_calloc(size_t nmemb, size_t size) { } -void* __libqasan_realloc(void* ptr, size_t size) { +void *__libqasan_realloc(void *ptr, size_t size) { - char* p = __libqasan_malloc(size); + char *p = __libqasan_malloc(size); if (!p) return NULL; if (!ptr) return p; - size_t n = ((struct chunk_begin*)ptr)[-1].requested_size; + size_t n = ((struct chunk_begin *)ptr)[-1].requested_size; if (size < n) n = size; __builtin_memcpy(p, ptr, n); @@ -289,9 +290,9 @@ void* __libqasan_realloc(void* ptr, size_t size) { } -int __libqasan_posix_memalign(void** ptr, size_t align, size_t len) { +int __libqasan_posix_memalign(void **ptr, size_t align, size_t len) { - if ((align % 2) || (align % sizeof(void*))) return EINVAL; + if ((align % 2) || (align % sizeof(void *))) return EINVAL; if (len == 0) { *ptr = NULL; @@ -305,7 +306,7 @@ int __libqasan_posix_memalign(void** ptr, size_t align, size_t len) { int state = QASAN_SWAP(QASAN_DISABLED); // disable qasan for this thread - char* orig = backend_malloc(sizeof(struct chunk_struct) + size); + char *orig = backend_malloc(sizeof(struct chunk_struct) + size); QASAN_SWAP(state); @@ -313,10 +314,10 @@ int __libqasan_posix_memalign(void** ptr, size_t align, size_t len) { QASAN_UNPOISON(orig, sizeof(struct chunk_struct) + size); - char* data = orig + sizeof(struct chunk_begin); + char *data = orig + sizeof(struct chunk_begin); data += align - ((uintptr_t)data % align); - struct chunk_begin* p = (struct chunk_begin*)data - 1; + struct chunk_begin *p = (struct chunk_begin *)data - 1; p->requested_size = len; p->aligned_orig = orig; @@ -339,9 +340,9 @@ int __libqasan_posix_memalign(void** ptr, size_t align, size_t len) { } -void* __libqasan_memalign(size_t align, size_t len) { +void *__libqasan_memalign(size_t align, size_t len) { - void* ret = NULL; + void *ret = NULL; __libqasan_posix_memalign(&ret, align, len); @@ -349,9 +350,9 @@ void* __libqasan_memalign(size_t align, size_t len) { } -void* __libqasan_aligned_alloc(size_t align, size_t len) { +void *__libqasan_aligned_alloc(size_t align, size_t len) { - void* ret = NULL; + void *ret = NULL; if ((len % align)) return NULL; diff --git a/qemu_mode/libqasan/string.c b/qemu_mode/libqasan/string.c index 4be01279..c850463b 100644 --- a/qemu_mode/libqasan/string.c +++ b/qemu_mode/libqasan/string.c @@ -271,7 +271,7 @@ void *__libqasan_memmem(const void *haystack, size_t haystack_len, } - } while (++h <= end); + } while (h++ <= end); return 0; diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl index 47722f64..9a258d5b 160000 --- a/qemu_mode/qemuafl +++ b/qemu_mode/qemuafl @@ -1 +1 @@ -Subproject commit 47722f64e4c1662bad97dc25f3e4cc63959ff5f3 +Subproject commit 9a258d5b7a38c045a6e385fcfcf80a746a60e557 diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 7844eedf..3ce4148d 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1303,7 +1303,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } -#endif /* CMPLOG_SOLVE_ARITHMETIC */ +#endif /* CMPLOG_SOLVE_ARITHMETIC */ return 0; @@ -2670,3 +2670,4 @@ exit_its: return r; } + -- cgit 1.4.1 From 1ba5d1008e749a12ac338f57adbac3b2b4cf9ea9 Mon Sep 17 00:00:00 2001 From: aflpp Date: Sat, 13 Feb 2021 10:53:40 +0100 Subject: fuck you llvm 13 --- src/afl-cc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 0ae401e7..9d88f262 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -554,6 +554,11 @@ static void edit_params(u32 argc, char **argv, char **envp) { } +#if LLVM_MAJOR >= 13 + // fuck you llvm 13 + cc_params[cc_par_cnt++] = "-fno-experimental-new-pass-manager"; +#endif + if (lto_mode && !have_c) { u8 *ld_path = strdup(AFL_REAL_LD); -- cgit 1.4.1 From 385312c65858695b55607ccd376fb5ea8f83a688 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 13 Feb 2021 13:31:17 +0100 Subject: fix issue #732 afl-cmin and afl-showmap should support '-f' --- afl-cmin | 4 ++-- src/afl-showmap.c | 3 +-- test/test-basic.sh | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/afl-cmin b/afl-cmin index 31d7ddad..4ee79a79 100755 --- a/afl-cmin +++ b/afl-cmin @@ -411,8 +411,8 @@ BEGIN { retval = system( AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string) } else { print " Processing "in_count" files (forkserver mode)..." -# print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string" use_stdin); } else { @@ -1169,7 +1168,7 @@ int main(int argc, char **argv_orig, char **envp) { } - stdin_file = + stdin_file = at_file ? strdup(at_file) : alloc_printf("%s/.afl-showmap-temp-%u", use_dir, (u32)getpid()); unlink(stdin_file); atexit(at_exit_handler); diff --git a/test/test-basic.sh b/test/test-basic.sh index 132610c0..b4bb9df2 100755 --- a/test/test-basic.sh +++ b/test/test-basic.sh @@ -7,7 +7,7 @@ AFL_GCC=afl-gcc $ECHO "$BLUE[*] Testing: ${AFL_GCC}, afl-showmap, afl-fuzz, afl-cmin and afl-tmin" test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "i386" && { test -e ../${AFL_GCC} -a -e ../afl-showmap -a -e ../afl-fuzz && { - ../${AFL_GCC} -o test-instr.plain ../test-instr.c > /dev/null 2>&1 + ../${AFL_GCC} -o test-instr.plain -O0 ../test-instr.c > /dev/null 2>&1 AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 test -e test-instr.plain && { $ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded" @@ -39,7 +39,7 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc $ECHO "$RED[!] ${AFL_GCC} failed" echo CUT------------------------------------------------------------------CUT uname -a - ../${AFL_GCC} -o test-instr.plain ../test-instr.c + ../${AFL_GCC} -o test-instr.plain -O0 ../test-instr.c echo CUT------------------------------------------------------------------CUT CODE=1 } @@ -128,7 +128,7 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc $ECHO "$BLUE[*] Testing: ${AFL_GCC}, afl-showmap, afl-fuzz, afl-cmin and afl-tmin" SKIP= test -e ../${AFL_GCC} -a -e ../afl-showmap -a -e ../afl-fuzz && { - ../${AFL_GCC} -o test-instr.plain ../test-instr.c > /dev/null 2>&1 + ../${AFL_GCC} -o test-instr.plain -O0 ../test-instr.c > /dev/null 2>&1 AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 test -e test-instr.plain && { $ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded" -- cgit 1.4.1 From 1d60c39191478334e27ddf4f60cf9b27710502f0 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 13 Feb 2021 13:42:37 +0100 Subject: fix new compiler warning --- src/afl-showmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 2e421065..0d2c5ceb 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1168,7 +1168,7 @@ int main(int argc, char **argv_orig, char **envp) { } - stdin_file = at_file ? strdup(at_file) : + stdin_file = at_file ? strdup(at_file) : (char *) alloc_printf("%s/.afl-showmap-temp-%u", use_dir, (u32)getpid()); unlink(stdin_file); atexit(at_exit_handler); -- cgit 1.4.1 From 9bd1e19d7f004b4da6a610b07e59f99d66bb7ec2 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 13 Feb 2021 22:43:56 +0100 Subject: added AFL_IGNORE_UNKNOWN_ENVS --- docs/Changelog.md | 3 ++- docs/env_variables.md | 5 ++++- include/envs.h | 1 + instrumentation/SanitizerCoveragePCGUARD.so.cc | 2 +- src/afl-cc.c | 1 + src/afl-common.c | 4 ++-- src/afl-fuzz.c | 1 + src/afl-showmap.c | 5 +++-- 8 files changed, 15 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 895ab845..71ef4c2c 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -61,7 +61,8 @@ sending a mail to . - Added a new example harness to compare python, c, and rust bindings - afl-cmin and afl-showmap now support the -f option - changed default: no memory limit for afl-cmin and afl-cmin.bash - - warn on any _AFL and __AFL env vars + - warn on any _AFL and __AFL env vars. + - set AFL_IGNORE_UNKNOWN_ENVS to not warn on unknown AFL_... env vars. - added dummy Makefile to instrumentation/ - Updated utils/afl_frida to be 5% faster, 7% on x86_x64 - Added AFL_KILL_SIGNAL env variable (thanks @v-p-b) diff --git a/docs/env_variables.md b/docs/env_variables.md index 886669ad..f7745247 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -5,6 +5,10 @@ users or for some types of custom fuzzing setups. See [README.md](README.md) for the general instruction manual. + Note that most tools will warn on any unknown AFL environment variables. + This is for warning on typos that can happen. If you want to disable this + check then set the `AFL_IGNORE_UNKNOWN_ENVS` environment variable. + ## 1) Settings for all compilers Starting with afl++ 3.0 there is only one compiler: afl-cc @@ -18,7 +22,6 @@ To select the different instrumentation modes this can be done by `MODE` can be one of `LTO` (afl-clang-lto*), `LLVM` (afl-clang-fast*), `GCC_PLUGIN` (afl-g*-fast) or `GCC` (afl-gcc/afl-g++). - Because (with the exception of the --afl-MODE command line option) the compile-time tools do not accept afl specific command-line options, they make fairly broad use of environmental variables instead: diff --git a/include/envs.h b/include/envs.h index 210b34a6..4313e053 100644 --- a/include/envs.h +++ b/include/envs.h @@ -61,6 +61,7 @@ static char *afl_environment_variables[] = { "AFL_FORKSRV_INIT_TMOUT", "AFL_HARDEN", "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES", + "AFL_IGNORE_UNKNOWN_ENVS", "AFL_IMPORT_FIRST", "AFL_INST_LIBS", "AFL_INST_RATIO", diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index 80c8f917..9b1351b0 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -1138,7 +1138,7 @@ void ModuleSanitizerCoverage::InjectTraceForGep( IRBuilder<> IRB(GEP); for (Use &Idx : GEP->indices()) if (!isa(Idx) && Idx->getType()->isIntegerTy()) - IRB.CreateCall(SanCovTraceGepFunction, + IRB.CreateCall(SanCovTraceGepFunction, {IRB.CreateIntCast(Idx, IntptrTy, true)}); } diff --git a/src/afl-cc.c b/src/afl-cc.c index 9d88f262..d41f79a2 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1587,6 +1587,7 @@ int main(int argc, char **argv, char **envp) { "libtokencap.so)\n" " AFL_PATH: path to instrumenting pass and runtime " "(afl-compiler-rt.*o)\n" + " AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" " AFL_INST_RATIO: percentage of branches to instrument\n" " AFL_QUIET: suppress verbose output\n" " AFL_HARDEN: adds code hardening to catch memory bugs\n" diff --git a/src/afl-common.c b/src/afl-common.c index 1cc7f462..589aac71 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -523,7 +523,7 @@ void check_environment_vars(char **envp) { if (be_quiet) { return; } int index = 0, issue_detected = 0; - char *env, *val; + char *env, *val, *ignore = getenv("AFL_IGNORE_UNKNOWN_ENVS"); while ((env = envp[index++]) != NULL) { if (strncmp(env, "ALF_", 4) == 0 || strncmp(env, "_ALF", 4) == 0 || @@ -582,7 +582,7 @@ void check_environment_vars(char **envp) { } - if (match == 0) { + if (match == 0 && !ignore) { WARNF("Mistyped AFL environment variable: %s", env); issue_detected = 1; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e4139857..e0ac8840 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -198,6 +198,7 @@ static void usage(u8 *argv0, int more_help) { "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" "AFL_HANG_TMOUT: override timeout value (in milliseconds)\n" "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: don't warn about core dump handlers\n" + "AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" "AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n" "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 0d2c5ceb..b40527d3 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1168,8 +1168,9 @@ int main(int argc, char **argv_orig, char **envp) { } - stdin_file = at_file ? strdup(at_file) : (char *) - alloc_printf("%s/.afl-showmap-temp-%u", use_dir, (u32)getpid()); + stdin_file = at_file ? strdup(at_file) + : (char *)alloc_printf("%s/.afl-showmap-temp-%u", + use_dir, (u32)getpid()); unlink(stdin_file); atexit(at_exit_handler); fsrv->out_file = stdin_file; -- cgit 1.4.1 From f31d8b8401f7bebb75a19e27c21143c31ad33448 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Mon, 15 Feb 2021 08:46:19 +0100 Subject: redqueen fix compiler warnings for 32 bit --- src/afl-fuzz-redqueen.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 3ce4148d..527feef5 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1498,11 +1498,12 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, struct cmp_header *h = &afl->shm.cmp_map->headers[key]; struct tainted * t; u32 i, j, idx, taint_len, loggeds; - u32 have_taint = 1, is_n = 0; + u32 have_taint = 1; u8 status = 0, found_one = 0; /* loop cmps are useless, detect and ignore them */ #ifdef WORD_SIZE_64 + u32 is_n = 0; u128 s128_v0 = 0, s128_v1 = 0, orig_s128_v0 = 0, orig_s128_v1 = 0; #endif u64 s_v0, s_v1; @@ -1520,6 +1521,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } +#ifdef WORD_SIZE_64 switch (SHAPE_BYTES(h->shape)) { case 1: @@ -1531,6 +1533,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, is_n = 1; } +#endif for (i = 0; i < loggeds; ++i) { @@ -2606,8 +2609,8 @@ exit_its: } #else - u32 *v = (u64 *)afl->virgin_bits; - u32 *s = (u64 *)virgin_save; + u32 *v = (u32 *)afl->virgin_bits; + u32 *s = (u32 *)virgin_save; u32 i; for (i = 0; i < (afl->shm.map_size >> 2); i++) { -- cgit 1.4.1 From e3a5c31307f323452dc4b5288e0d19a02b596a33 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 15 Feb 2021 13:25:15 +0100 Subject: llvm bug workaround for lto extint --- docs/Changelog.md | 1 + include/envs.h | 1 + instrumentation/cmplog-instructions-pass.cc | 39 ++++++++++++++++++++++++++--- qemu_mode/libqasan/dlmalloc.c | 5 ++++ src/afl-cc.c | 2 ++ src/afl-fuzz-redqueen.c | 1 + 6 files changed, 45 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 71ef4c2c..e2482f8f 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -49,6 +49,7 @@ sending a mail to . CLANG for old afl-clang - fixed a potential crash in the LAF feature - workaround for llvm 13 + - workaround for llvm internal lto bug that lets not bitcast from _ExtInt() - qemuafl - QASan (address sanitizer for Qemu) ported to qemuafl! See qemu_mode/libqasan/README.md diff --git a/include/envs.h b/include/envs.h index 4313e053..36667ebc 100644 --- a/include/envs.h +++ b/include/envs.h @@ -16,6 +16,7 @@ static char *afl_environment_deprecated[] = { static char *afl_environment_variables[] = { + "_AFL_LTO_COMPILE", "AFL_ALIGNED_ALLOC", "AFL_ALLOW_TMP", "AFL_ANALYZE_HEX", diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index b5cc1882..6b071b48 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -113,6 +113,8 @@ bool CmpLogInstructions::hookInstrs(Module &M) { IntegerType *Int64Ty = IntegerType::getInt64Ty(C); IntegerType *Int128Ty = IntegerType::getInt128Ty(C); + char *is_lto = getenv("_AFL_LTO_COMPILE"); + #if LLVM_VERSION_MAJOR < 9 Constant * #else @@ -265,10 +267,20 @@ bool CmpLogInstructions::hookInstrs(Module &M) { unsigned int max_size = Val->getType()->getIntegerBitWidth(), cast_size; unsigned char do_cast = 0; - if (!SI->getNumCases() || max_size < 16 || max_size % 8) { + if (!SI->getNumCases() || max_size < 16) { continue; } + + if (max_size % 8) { + + if (is_lto) { + + continue; // LTO cannot bitcast from _ExtInt() :( + + } else { - // if (!be_quiet) errs() << "skip trivial switch..\n"; - continue; + max_size = (((max_size / 8) + 1) * 8); + do_cast = 1; + + } } @@ -285,6 +297,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } + if (is_lto) { continue; } // LTO cannot bitcast _ExtInt() :( max_size = 128; do_cast = 1; @@ -301,6 +314,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { cast_size = max_size; break; default: + if (is_lto) { continue; } // LTO cannot bitcast _ExtInt() :( cast_size = 128; do_cast = 1; @@ -540,7 +554,22 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (!max_size || max_size % 8 || max_size < 16) { continue; } + if (!max_size || max_size < 16) { continue; } + + if (max_size % 8) { + + if (is_lto) { + + continue; // LTO cannot bitcast from _ExtInt() :( + + } else { + + max_size = (((max_size / 8) + 1) * 8); + do_cast = 1; + + } + + } if (max_size > 128) { @@ -552,6 +581,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } + if (is_lto) { continue; } // LTO cannot bitcast from _ExtInt() :( max_size = 128; do_cast = 1; @@ -568,6 +598,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { cast_size = max_size; break; default: + if (is_lto) { continue; } // LTO cannot bitcast from _ExtInt() :( cast_size = 128; do_cast = 1; diff --git a/qemu_mode/libqasan/dlmalloc.c b/qemu_mode/libqasan/dlmalloc.c index bace0ff6..aff58ad5 100644 --- a/qemu_mode/libqasan/dlmalloc.c +++ b/qemu_mode/libqasan/dlmalloc.c @@ -3917,6 +3917,7 @@ static void internal_malloc_stats(mstate m) { \ } else if (RTCHECK(B == smallbin_at(M, I) || \ \ + \ (ok_address(M, B) && B->fd == P))) { \ \ F->bk = B; \ @@ -4128,6 +4129,7 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ if (R != 0) { \ \ @@ -4144,6 +4146,7 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ @@ -4156,12 +4159,14 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ diff --git a/src/afl-cc.c b/src/afl-cc.c index d41f79a2..959c9a6f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1875,6 +1875,8 @@ int main(int argc, char **argv, char **envp) { edit_params(argc, argv, envp); + if (lto_mode) { setenv("_AFL_LTO_COMPILE", "1", 1); } + if (debug) { DEBUGF("cd '%s';", getthecwd()); diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 527feef5..2b01ecad 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1533,6 +1533,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, is_n = 1; } + #endif for (i = 0; i < loggeds; ++i) { -- cgit 1.4.1 From 512f53984cd53d75900e6648720ef7cc7c571ca1 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 15 Feb 2021 13:51:32 +0100 Subject: fixed scan-build issues --- src/afl-fuzz-redqueen.c | 17 ++++++++++++++++- src/afl-fuzz.c | 15 ++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 7844eedf..deaddc56 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -382,6 +382,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, rng = ranges; ranges = rng->next; ck_free(rng); + rng = NULL; } @@ -455,6 +456,15 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, return 0; checksum_fail: + while (ranges) { + + rng = ranges; + ranges = rng->next; + ck_free(rng); + rng = NULL; + + } + ck_free(backup); ck_free(changed); @@ -503,6 +513,8 @@ static int strntoll(const char *str, size_t sz, char **end, int base, long long ret; const char *beg = str; + if (!str || !sz) { return 1; } + for (; beg && sz && *beg == ' '; beg++, sz--) {}; if (!sz) return 1; @@ -526,6 +538,8 @@ static int strntoull(const char *str, size_t sz, char **end, int base, unsigned long long ret; const char * beg = str; + if (!str || !sz) { return 1; } + for (; beg && sz && *beg == ' '; beg++, sz--) ; @@ -1303,7 +1317,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } -#endif /* CMPLOG_SOLVE_ARITHMETIC */ +#endif /* CMPLOG_SOLVE_ARITHMETIC */ return 0; @@ -2670,3 +2684,4 @@ exit_its: return r; } + diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e4139857..e79671e0 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -552,13 +552,22 @@ int main(int argc, char **argv_orig, char **envp) { case 'F': /* foreign sync dir */ - if (!afl->is_main_node) + if (!optarg) { FATAL("Missing path for -F"); } + if (!afl->is_main_node) { + FATAL( "Option -F can only be specified after the -M option for the " "main fuzzer of a fuzzing campaign"); - if (afl->foreign_sync_cnt >= FOREIGN_SYNCS_MAX) + + } + + if (afl->foreign_sync_cnt >= FOREIGN_SYNCS_MAX) { + FATAL("Maximum %u entried of -F option can be specified", FOREIGN_SYNCS_MAX); + + } + afl->foreign_syncs[afl->foreign_sync_cnt].dir = optarg; while (afl->foreign_syncs[afl->foreign_sync_cnt] .dir[strlen(afl->foreign_syncs[afl->foreign_sync_cnt].dir) - @@ -802,7 +811,7 @@ int main(int argc, char **argv_orig, char **envp) { case 'l': { - afl->cmplog_lvl = atoi(optarg); + if (optarg) { afl->cmplog_lvl = atoi(optarg); } if (afl->cmplog_lvl < 1 || afl->cmplog_lvl > CMPLOG_LVL_MAX) { FATAL( -- cgit 1.4.1 From 6caec2169cef890ba8a62715c2c26cc0608626e3 Mon Sep 17 00:00:00 2001 From: Michael Rodler Date: Mon, 15 Feb 2021 19:14:28 +0100 Subject: Revert "llvm bug workaround for lto extint" This reverts commit e3a5c31307f323452dc4b5288e0d19a02b596a33. --- docs/Changelog.md | 1 - include/envs.h | 1 - instrumentation/cmplog-instructions-pass.cc | 39 +++-------------------------- qemu_mode/libqasan/dlmalloc.c | 5 ---- src/afl-cc.c | 2 -- src/afl-fuzz-redqueen.c | 1 - 6 files changed, 4 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index e2482f8f..71ef4c2c 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -49,7 +49,6 @@ sending a mail to . CLANG for old afl-clang - fixed a potential crash in the LAF feature - workaround for llvm 13 - - workaround for llvm internal lto bug that lets not bitcast from _ExtInt() - qemuafl - QASan (address sanitizer for Qemu) ported to qemuafl! See qemu_mode/libqasan/README.md diff --git a/include/envs.h b/include/envs.h index 36667ebc..4313e053 100644 --- a/include/envs.h +++ b/include/envs.h @@ -16,7 +16,6 @@ static char *afl_environment_deprecated[] = { static char *afl_environment_variables[] = { - "_AFL_LTO_COMPILE", "AFL_ALIGNED_ALLOC", "AFL_ALLOW_TMP", "AFL_ANALYZE_HEX", diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index 9cd99f85..50ade9fd 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -114,8 +114,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { IntegerType *Int64Ty = IntegerType::getInt64Ty(C); IntegerType *Int128Ty = IntegerType::getInt128Ty(C); - char *is_lto = getenv("_AFL_LTO_COMPILE"); - #if LLVM_VERSION_MAJOR < 9 Constant * #else @@ -268,20 +266,10 @@ bool CmpLogInstructions::hookInstrs(Module &M) { unsigned int max_size = Val->getType()->getIntegerBitWidth(), cast_size; unsigned char do_cast = 0; - if (!SI->getNumCases() || max_size < 16) { continue; } - - if (max_size % 8) { - - if (is_lto) { + if (!SI->getNumCases() || max_size < 16 || max_size % 8) { - continue; // LTO cannot bitcast from _ExtInt() :( - - } else { - - max_size = (((max_size / 8) + 1) * 8); - do_cast = 1; - - } + // if (!be_quiet) errs() << "skip trivial switch..\n"; + continue; } @@ -298,7 +286,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (is_lto) { continue; } // LTO cannot bitcast _ExtInt() :( max_size = 128; do_cast = 1; @@ -315,7 +302,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { cast_size = max_size; break; default: - if (is_lto) { continue; } // LTO cannot bitcast _ExtInt() :( cast_size = 128; do_cast = 1; @@ -504,22 +490,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (!max_size || max_size < 16) { continue; } - - if (max_size % 8) { - - if (is_lto) { - - continue; // LTO cannot bitcast from _ExtInt() :( - - } else { - - max_size = (((max_size / 8) + 1) * 8); - do_cast = 1; - - } - - } + if (!max_size || max_size % 8 || max_size < 16) { continue; } if (max_size > 128) { @@ -531,7 +502,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (is_lto) { continue; } // LTO cannot bitcast from _ExtInt() :( max_size = 128; do_cast = 1; @@ -548,7 +518,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { cast_size = max_size; break; default: - if (is_lto) { continue; } // LTO cannot bitcast from _ExtInt() :( cast_size = 128; do_cast = 1; diff --git a/qemu_mode/libqasan/dlmalloc.c b/qemu_mode/libqasan/dlmalloc.c index aff58ad5..bace0ff6 100644 --- a/qemu_mode/libqasan/dlmalloc.c +++ b/qemu_mode/libqasan/dlmalloc.c @@ -3917,7 +3917,6 @@ static void internal_malloc_stats(mstate m) { \ } else if (RTCHECK(B == smallbin_at(M, I) || \ \ - \ (ok_address(M, B) && B->fd == P))) { \ \ F->bk = B; \ @@ -4129,7 +4128,6 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ - \ CORRUPTION_ERROR_ACTION(M); \ if (R != 0) { \ \ @@ -4146,7 +4144,6 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ - \ CORRUPTION_ERROR_ACTION(M); \ \ } \ @@ -4159,14 +4156,12 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ - \ CORRUPTION_ERROR_ACTION(M); \ \ } \ \ } else \ \ - \ CORRUPTION_ERROR_ACTION(M); \ \ } \ diff --git a/src/afl-cc.c b/src/afl-cc.c index 959c9a6f..d41f79a2 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1875,8 +1875,6 @@ int main(int argc, char **argv, char **envp) { edit_params(argc, argv, envp); - if (lto_mode) { setenv("_AFL_LTO_COMPILE", "1", 1); } - if (debug) { DEBUGF("cd '%s';", getthecwd()); diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index cf65d3c1..275af9c8 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1547,7 +1547,6 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, is_n = 1; } - #endif for (i = 0; i < loggeds; ++i) { -- cgit 1.4.1 From 4c47b242eb976b8518ab8884733d02465f02d90a Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Feb 2021 17:40:01 +0100 Subject: fix FPE in colorization --- src/afl-fuzz-redqueen.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 275af9c8..bbe35fe5 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -421,8 +421,9 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, if (taint) { - if (len / positions == 1 && positions > CMPLOG_POSITIONS_MAX && - afl->active_paths / afl->colorize_success > CMPLOG_CORPUS_PERCENT) { + if (afl->colorize_success && + (len / positions == 1 && positions > CMPLOG_POSITIONS_MAX && + afl->active_paths / afl->colorize_success > CMPLOG_CORPUS_PERCENT)) { #ifdef _DEBUG fprintf(stderr, "Colorization unsatisfactory\n"); @@ -1547,6 +1548,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, is_n = 1; } + #endif for (i = 0; i < loggeds; ++i) { -- cgit 1.4.1 From 5dd35f5281afec0955c08fe9f99e3c83222b7764 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Feb 2021 19:10:05 +0100 Subject: fix a rare i2s illegal memory access --- src/afl-fuzz-redqueen.c | 71 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index bbe35fe5..3ac7ba11 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -808,37 +808,82 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // Try to identify transform magic if (pattern != o_pattern && repl == changed_val && attr <= IS_EQUAL) { - u64 *ptr = (u64 *)&buf[idx]; - u64 *o_ptr = (u64 *)&orig_buf[idx]; - u64 b_val, o_b_val, mask; + u64 b_val, o_b_val, mask; + u8 bytes; switch (SHAPE_BYTES(h->shape)) { case 0: case 1: - b_val = (u64)(*ptr % 0x100); + bytes = 1; + break; + case 2: + bytes = 2; + break; + case 3: + case 4: + bytes = 4; + break; + default: + bytes = 8; + + } + + // necessary for preventing heap access overflow + bytes = MIN(bytes, len - idx); + + switch (bytes) { + + case 0: // cannot happen + b_val = o_b_val = mask = 0; // keep the linters happy + break; + case 1: { + + u8 *ptr = (u8 *)&buf[idx]; + u8 *o_ptr = (u8 *)&orig_buf[idx]; + b_val = (u64)(*ptr); o_b_val = (u64)(*o_ptr % 0x100); mask = 0xff; break; + + } + case 2: - case 3: - b_val = (u64)(*ptr % 0x10000); - o_b_val = (u64)(*o_ptr % 0x10000); + case 3: { + + u16 *ptr = (u16 *)&buf[idx]; + u16 *o_ptr = (u16 *)&orig_buf[idx]; + b_val = (u64)(*ptr); + o_b_val = (u64)(*o_ptr); mask = 0xffff; break; + + } + case 4: case 5: case 6: - case 7: - b_val = (u64)(*ptr % 0x100000000); - o_b_val = (u64)(*o_ptr % 0x100000000); + case 7: { + + u32 *ptr = (u32 *)&buf[idx]; + u32 *o_ptr = (u32 *)&orig_buf[idx]; + b_val = (u64)(*ptr); + o_b_val = (u64)(*o_ptr); mask = 0xffffffff; break; - default: - b_val = *ptr; - o_b_val = *o_ptr; + + } + + default: { + + u64 *ptr = (u64 *)&buf[idx]; + u64 *o_ptr = (u64 *)&orig_buf[idx]; + b_val = (u64)(*ptr); + o_b_val = (u64)(*o_ptr); mask = 0xffffffffffffffff; + } + } // test for arithmetic, eg. "if ((user_val - 0x1111) == 0x1234) ..." -- cgit 1.4.1 From 89cf94f0e6afd4d360f7f139f16a6730a07b478d Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 19 Feb 2021 20:33:12 +0100 Subject: suggested env vars for lazy ppl --- include/common.h | 1 + qemu_mode/libqasan/dlmalloc.c | 5 ++ src/afl-common.c | 116 ++++++++++++++++++++++++++++++++++++++++++ src/afl-fuzz-state.c | 2 + 4 files changed, 124 insertions(+) (limited to 'src') diff --git a/include/common.h b/include/common.h index bb8831f2..cd728536 100644 --- a/include/common.h +++ b/include/common.h @@ -39,6 +39,7 @@ #define STRINGIFY_VAL_SIZE_MAX (16) void detect_file_args(char **argv, u8 *prog_in, bool *use_stdin); +void print_suggested_envs(char *mispelled_env); void check_environment_vars(char **env); char **argv_cpy_dup(int argc, char **argv); diff --git a/qemu_mode/libqasan/dlmalloc.c b/qemu_mode/libqasan/dlmalloc.c index bace0ff6..aff58ad5 100644 --- a/qemu_mode/libqasan/dlmalloc.c +++ b/qemu_mode/libqasan/dlmalloc.c @@ -3917,6 +3917,7 @@ static void internal_malloc_stats(mstate m) { \ } else if (RTCHECK(B == smallbin_at(M, I) || \ \ + \ (ok_address(M, B) && B->fd == P))) { \ \ F->bk = B; \ @@ -4128,6 +4129,7 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ if (R != 0) { \ \ @@ -4144,6 +4146,7 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ @@ -4156,12 +4159,14 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ diff --git a/src/afl-common.c b/src/afl-common.c index 589aac71..0b38b222 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -518,6 +518,120 @@ int parse_afl_kill_signal_env(u8 *afl_kill_signal_env, int default_signal) { } +#define HELPER_MIN3(a, b, c) \ + ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c))) + +// from +// https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C +static int string_distance_levenshtein(char *s1, char *s2) { + + unsigned int s1len, s2len, x, y, lastdiag, olddiag; + s1len = strlen(s1); + s2len = strlen(s2); + unsigned int column[s1len + 1]; + for (y = 1; y <= s1len; y++) + column[y] = y; + for (x = 1; x <= s2len; x++) { + + column[0] = x; + for (y = 1, lastdiag = x - 1; y <= s1len; y++) { + + olddiag = column[y]; + column[y] = HELPER_MIN3(column[y] + 1, column[y - 1] + 1, + lastdiag + (s1[y - 1] == s2[x - 1] ? 0 : 1)); + lastdiag = olddiag; + + } + + } + + return column[s1len]; + +} + +#undef HELPER_MIN3 + +void print_suggested_envs(char *mispelled_env) { + + size_t env_name_len = + strcspn(mispelled_env, "=") - 4; // remove the AFL_prefix + char *env_name = ck_alloc(env_name_len + 1); + memcpy(env_name, mispelled_env + 4, env_name_len); + + char *seen = ck_alloc(sizeof(afl_environment_variables) / sizeof(char *)); + + int j; + for (j = 0; afl_environment_variables[j] != NULL; ++j) { + + char *afl_env = afl_environment_variables[j] + 4; + + int distance = string_distance_levenshtein(afl_env, env_name); + if (distance <= 3 && seen[j] == 0) { + + SAYF("Did you mean %s?\n", afl_environment_variables[j]); + seen[j] = 1; + + } + + size_t afl_env_len = strlen(afl_env); + char * reduced = ck_alloc(afl_env_len + 1); + + size_t start = 0; + while (start < afl_env_len) { + + size_t end = start + strcspn(afl_env + start, "_") + 1; + memcpy(reduced, afl_env, start); + if (end < afl_env_len) + memcpy(reduced + start, afl_env + end, afl_env_len - end); + reduced[afl_env_len - end + start] = 0; + + int distance = string_distance_levenshtein(reduced, env_name); + if (distance <= 3 && seen[j] == 0) { + + SAYF("Did you mean %s?\n", afl_environment_variables[j]); + seen[j] = 1; + + } + + start = end; + + }; + + } + + char * reduced = ck_alloc(env_name_len + 1); + size_t start = 0; + while (start < env_name_len) { + + size_t end = start + strcspn(env_name + start, "_") + 1; + memcpy(reduced, env_name, start); + if (end < env_name_len) + memcpy(reduced + start, env_name + end, env_name_len - end); + reduced[env_name_len - end + start] = 0; + + for (j = 0; afl_environment_variables[j] != NULL; ++j) { + + int distance = string_distance_levenshtein( + afl_environment_variables[j] + 4, reduced); + if (distance <= 3 && seen[j] == 0) { + + SAYF("Did you mean %s?\n", afl_environment_variables[j]); + seen[j] = 1; + + } + + } + + start = end; + + }; + + ck_free(env_name); + ck_free(reduced); + ck_free(seen); + +} + void check_environment_vars(char **envp) { if (be_quiet) { return; } @@ -587,6 +701,8 @@ void check_environment_vars(char **envp) { WARNF("Mistyped AFL environment variable: %s", env); issue_detected = 1; + print_suggested_envs(env); + } } diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 5040e3ef..3d36e712 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -486,6 +486,8 @@ void read_afl_environment(afl_state_t *afl, char **envp) { WARNF("Mistyped AFL environment variable: %s", env); issue_detected = 1; + print_suggested_envs(env); + } } -- cgit 1.4.1 From 62767a42dc2101a67eeb723ca94f5bf853525d60 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 19 Feb 2021 20:40:38 +0100 Subject: improved env suggestions --- src/afl-common.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/afl-common.c b/src/afl-common.c index 0b38b222..531753c2 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -551,6 +551,8 @@ static int string_distance_levenshtein(char *s1, char *s2) { #undef HELPER_MIN3 +#define ENV_SIMILARITY_TRESHOLD 3 + void print_suggested_envs(char *mispelled_env) { size_t env_name_len = @@ -559,20 +561,28 @@ void print_suggested_envs(char *mispelled_env) { memcpy(env_name, mispelled_env + 4, env_name_len); char *seen = ck_alloc(sizeof(afl_environment_variables) / sizeof(char *)); + int found = 0; int j; for (j = 0; afl_environment_variables[j] != NULL; ++j) { char *afl_env = afl_environment_variables[j] + 4; - int distance = string_distance_levenshtein(afl_env, env_name); - if (distance <= 3 && seen[j] == 0) { + if (distance < ENV_SIMILARITY_TRESHOLD && seen[j] == 0) { SAYF("Did you mean %s?\n", afl_environment_variables[j]); seen[j] = 1; + found = 1; } + + } + + if (found) goto cleanup; + + for (j = 0; afl_environment_variables[j] != NULL; ++j) { + char *afl_env = afl_environment_variables[j] + 4; size_t afl_env_len = strlen(afl_env); char * reduced = ck_alloc(afl_env_len + 1); @@ -586,10 +596,11 @@ void print_suggested_envs(char *mispelled_env) { reduced[afl_env_len - end + start] = 0; int distance = string_distance_levenshtein(reduced, env_name); - if (distance <= 3 && seen[j] == 0) { + if (distance < ENV_SIMILARITY_TRESHOLD && seen[j] == 0) { SAYF("Did you mean %s?\n", afl_environment_variables[j]); seen[j] = 1; + found = 1; } @@ -599,6 +610,8 @@ void print_suggested_envs(char *mispelled_env) { } + if (found) goto cleanup; + char * reduced = ck_alloc(env_name_len + 1); size_t start = 0; while (start < env_name_len) { @@ -613,7 +626,7 @@ void print_suggested_envs(char *mispelled_env) { int distance = string_distance_levenshtein( afl_environment_variables[j] + 4, reduced); - if (distance <= 3 && seen[j] == 0) { + if (distance < ENV_SIMILARITY_TRESHOLD && seen[j] == 0) { SAYF("Did you mean %s?\n", afl_environment_variables[j]); seen[j] = 1; @@ -626,8 +639,10 @@ void print_suggested_envs(char *mispelled_env) { }; - ck_free(env_name); ck_free(reduced); + +cleanup: + ck_free(env_name); ck_free(seen); } -- cgit 1.4.1 From 100aac4dd39012750036b2fd71eed5b21959f693 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 20 Feb 2021 14:15:38 +0100 Subject: -t help --- qemu_mode/qemuafl | 2 +- src/afl-fuzz.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl index e36a30eb..213f3b27 160000 --- a/qemu_mode/qemuafl +++ b/qemu_mode/qemuafl @@ -1 +1 @@ -Subproject commit e36a30ebca57ca433a5d6e20b1a32975aabb761b +Subproject commit 213f3b27dd099ef352181c48cd75c0f20a73e3f0 diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 8eb3625b..e3e9007d 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -103,7 +103,8 @@ static void usage(u8 *argv0, int more_help) { " quad -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " "or @@)\n" - " -t msec - timeout for each run (auto-scaled, 50-%u ms)\n" + " -t msec - timeout for each run (auto-scaled, 50-... ms, default %u ms)\n" + " add a '+' to skip over seeds running longer.\n" " -m megs - memory limit for child process (%u MB, 0 = no limit " "[default])\n" " -Q - use binary-only instrumentation (QEMU mode)\n" -- cgit 1.4.1 From b957218a3aad95af02a4da8207c7dabb893d4dc8 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sun, 21 Feb 2021 11:29:54 +0100 Subject: more attuned colorize repace --- TODO.md | 5 +---- src/afl-fuzz-redqueen.c | 31 ++++++++++++++++++++++++------- 2 files changed, 25 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/TODO.md b/TODO.md index 890a481a..4615c456 100644 --- a/TODO.md +++ b/TODO.md @@ -6,16 +6,13 @@ - CPU affinity for many cores? There seems to be an issue > 96 cores - afl-plot to support multiple plot_data - afl_custom_fuzz_splice_optin() + - afl_custom_splice() - intel-pt tracer ## Further down the road afl-fuzz: - setting min_len/max_len/start_offset/end_offset limits for mutation output - - add __sanitizer_cov_trace_cmp* support via shmem - -llvm_mode: - - add __sanitizer_cov_trace_cmp* support qemu_mode: - non colliding instrumentation diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 3ac7ba11..d77baf25 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -205,14 +205,31 @@ static void type_replace(afl_state_t *afl, u8 *buf, u32 len) { case '\t': c = ' '; break; - /* - case '\r': - case '\n': - // nothing ... - break; - */ + case '\r': + c = '\n'; + break; + case '\n': + c = '\r'; + break; + case 0: + c = 1; + break; + case 1: + c = 0; + break; + case 0xff: + c = 0; + break; default: - c = (buf[i] ^ 0xff); + if (buf[i] < 32) { + + c = (buf[i] ^ 0x1f); + + } else { + + c = (buf[i] ^ 0x7f); // we keep the highest bit + + } } -- cgit 1.4.1 From 974aab6cf6aa4ae34ee73bdceed1bd44a212fc5e Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sun, 21 Feb 2021 17:53:09 +0100 Subject: cmplog config.h -> -l option --- include/afl-fuzz.h | 1 + include/config.h | 9 ++---- src/afl-common.c | 8 ++--- src/afl-fuzz-redqueen.c | 83 +++++++++++++++++++++++++------------------------ src/afl-fuzz.c | 46 ++++++++++++++++++++------- 5 files changed, 85 insertions(+), 62 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 1d5ec1f0..10d94fed 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -649,6 +649,7 @@ typedef struct afl_state { u32 cmplog_max_filesize; u32 cmplog_lvl; u32 colorize_success; + u8 cmplog_enable_arith, cmplog_enable_transform; struct afl_pass_stat *pass_stats; struct cmp_map * orig_cmp_map; diff --git a/include/config.h b/include/config.h index 9f7db04d..535ce0d3 100644 --- a/include/config.h +++ b/include/config.h @@ -42,13 +42,8 @@ * */ -/* Enable arithmetic compare solving for both branches */ -#define CMPLOG_SOLVE_ARITHMETIC - -/* Enable transform following (XOR/ADD/SUB manipulations, hex en/decoding) */ -#define CMPLOG_SOLVE_TRANSFORM - -/* if TRANSFORM is enabled, this additionally enables base64 en/decoding */ +/* if TRANSFORM is enabled with '-l T', this additionally enables base64 + encoding/decoding */ // #define CMPLOG_SOLVE_TRANSFORM_BASE64 /* If a redqueen pass finds more than one solution, try to combine them? */ diff --git a/src/afl-common.c b/src/afl-common.c index 531753c2..ce63c262 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -561,13 +561,13 @@ void print_suggested_envs(char *mispelled_env) { memcpy(env_name, mispelled_env + 4, env_name_len); char *seen = ck_alloc(sizeof(afl_environment_variables) / sizeof(char *)); - int found = 0; + int found = 0; int j; for (j = 0; afl_environment_variables[j] != NULL; ++j) { char *afl_env = afl_environment_variables[j] + 4; - int distance = string_distance_levenshtein(afl_env, env_name); + int distance = string_distance_levenshtein(afl_env, env_name); if (distance < ENV_SIMILARITY_TRESHOLD && seen[j] == 0) { SAYF("Did you mean %s?\n", afl_environment_variables[j]); @@ -575,14 +575,14 @@ void print_suggested_envs(char *mispelled_env) { found = 1; } - + } if (found) goto cleanup; for (j = 0; afl_environment_variables[j] != NULL; ++j) { - char *afl_env = afl_environment_variables[j] + 4; + char * afl_env = afl_environment_variables[j] + 4; size_t afl_env_len = strlen(afl_env); char * reduced = ck_alloc(afl_env_len + 1); diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index d77baf25..1ab5f996 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -30,7 +30,6 @@ //#define _DEBUG //#define CMPLOG_INTROSPECTION -#define CMPLOG_COMBINE // CMP attribute enum enum { @@ -523,7 +522,7 @@ static u8 its_fuzz(afl_state_t *afl, u8 *buf, u32 len, u8 *status) { } -#ifdef CMPLOG_SOLVE_TRANSFORM +//#ifdef CMPLOG_SOLVE_TRANSFORM static int strntoll(const char *str, size_t sz, char **end, int base, long long *out) { @@ -608,7 +607,7 @@ static int is_hex(const char *str) { } - #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 +#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 // tests 4 bytes at location static int is_base64(const char *str) { @@ -721,10 +720,10 @@ static void to_base64(u8 *src, u8 *dst, u32 dst_len) { } - #endif - #endif +//#endif + static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, u64 pattern, u64 repl, u64 o_pattern, u64 changed_val, u8 attr, u32 idx, u32 taint_len, @@ -748,9 +747,9 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // o_pattern, pattern, repl, changed_val, idx, taint_len, // h->shape + 1, attr); -#ifdef CMPLOG_SOLVE_TRANSFORM + //#ifdef CMPLOG_SOLVE_TRANSFORM // reverse atoi()/strnu?toll() is expensive, so we only to it in lvl 3 - if (lvl & LVL3) { + if (afl->cmplog_enable_transform && (lvl & LVL3)) { u8 * endptr; u8 use_num = 0, use_unum = 0; @@ -771,11 +770,11 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - #ifdef _DEBUG +#ifdef _DEBUG if (idx == 0) fprintf(stderr, "ASCII is=%u use_num=%u use_unum=%u idx=%u %llx==%llx\n", afl->queue_cur->is_ascii, use_num, use_unum, idx, num, pattern); - #endif +#endif // num is likely not pattern as atoi("AAA") will be zero... if (use_num && ((u64)num == pattern || !num)) { @@ -1060,7 +1059,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } -#endif + //#endif // we only allow this for ascii2integer (above) so leave if this is the case if (unlikely(pattern == o_pattern)) { return 0; } @@ -1215,8 +1214,12 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // 16 = modified float, 32 = modified integer (modified = wont match // in original buffer) -#ifdef CMPLOG_SOLVE_ARITHMETIC - if (lvl < LVL3 || attr == IS_TRANSFORM) { return 0; } + //#ifdef CMPLOG_SOLVE_ARITHMETIC + if (!afl->cmplog_enable_arith || lvl < LVL3 || attr == IS_TRANSFORM) { + + return 0; + + } if (!(attr & (IS_GREATER | IS_LESSER)) || SHAPE_BYTES(h->shape) < 4) { @@ -1321,11 +1324,11 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, double *f = (double *)&repl; float g = (float)*f; repl_new = 0; - #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) memcpy((char *)&repl_new, (char *)&g, 4); - #else +#else memcpy(((char *)&repl_new) + 4, (char *)&g, 4); - #endif +#endif changed_val = repl_new; h->shape = 3; // modify shape @@ -1380,7 +1383,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } -#endif /* CMPLOG_SOLVE_ARITHMETIC */ + //#endif /* CMPLOG_SOLVE_ARITHMETIC return 0; @@ -1539,7 +1542,7 @@ static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) { for (k = 0; k < size; ++k) { #else - u32 off = 16 - size; + u32 off = 16 - size; for (k = 16 - size; k < 16; ++k) { #endif @@ -1857,9 +1860,9 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, #ifndef CMPLOG_COMBINE (void)(cbuf); #endif -#ifndef CMPLOG_SOLVE_TRANSFORM - (void)(changed_val); -#endif + //#ifndef CMPLOG_SOLVE_TRANSFORM + // (void)(changed_val); + //#endif u8 save[40]; u32 saved_idx = idx, pre, from = 0, to = 0, i, j; @@ -1939,16 +1942,16 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } -#ifdef CMPLOG_SOLVE_TRANSFORM + //#ifdef CMPLOG_SOLVE_TRANSFORM if (*status == 1) return 0; - if (lvl & LVL3) { + if (afl->cmplog_enable_transform && (lvl & LVL3)) { u32 toupper = 0, tolower = 0, xor = 0, arith = 0, tohex = 0, fromhex = 0; - #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 +#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 u32 tob64 = 0, fromb64 = 0; - #endif +#endif u32 from_0 = 0, from_x = 0, from_X = 0, from_slash = 0, from_up = 0; u32 to_0 = 0, to_x = 0, to_slash = 0, to_up = 0; u8 xor_val[32], arith_val[32], tmp[48]; @@ -2044,7 +2047,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 +#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 if (i % 3 == 2 && i < 24) { if (is_base64(repl + ((i / 3) << 2))) tob64 += 3; @@ -2057,7 +2060,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - #endif +#endif if ((o_pattern[i] ^ orig_buf[idx + i]) == xor_val[i] && xor_val[i]) { @@ -2085,20 +2088,20 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - #ifdef _DEBUG +#ifdef _DEBUG fprintf(stderr, "RTN idx=%u loop=%u xor=%u arith=%u tolower=%u toupper=%u " "tohex=%u fromhex=%u to_0=%u to_slash=%u to_x=%u " "from_0=%u from_slash=%u from_x=%u\n", idx, i, xor, arith, tolower, toupper, tohex, fromhex, to_0, to_slash, to_x, from_0, from_slash, from_x); - #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 + #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 fprintf(stderr, "RTN idx=%u loop=%u tob64=%u from64=%u\n", tob64, fromb64); - #endif #endif +#endif - #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 +#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 // input is base64 and converted to binary? convert repl to base64! if ((i % 4) == 3 && i < 24 && fromb64 > i) { @@ -2121,7 +2124,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - #endif +#endif // input is converted to hex? convert repl to binary! if (i < 16 && tohex > i) { @@ -2250,16 +2253,16 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } - #ifdef CMPLOG_COMBINE +#ifdef CMPLOG_COMBINE if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i + 1); } - #endif +#endif if ((i >= 7 && (i >= xor&&i >= arith &&i >= tolower &&i >= toupper &&i > tohex &&i > (fromhex + from_0 + from_x + from_slash + 1) - #ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 +#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64 && i > tob64 + 3 && i > fromb64 + 4 - #endif +#endif )) || repl[i] != changed_val[i] || *status == 1) { @@ -2273,7 +2276,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } -#endif + //#endif return 0; @@ -2606,9 +2609,9 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { } else if ((lvl & LVL1) -#ifdef CMPLOG_SOLVE_TRANSFORM - || (lvl & LVL3) -#endif + //#ifdef CMPLOG_SOLVE_TRANSFORM + || ((lvl & LVL3) && afl->cmplog_enable_transform) + //#endif ) { if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, cbuf, len, lvl, taint))) { @@ -2689,7 +2692,7 @@ exit_its: #else u32 *v = (u32 *)afl->virgin_bits; u32 *s = (u32 *)virgin_save; - u32 i; + u32 i; for (i = 0; i < (afl->shm.map_size >> 2); i++) { v[i] &= s[i]; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e3e9007d..e2db029d 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -103,7 +103,8 @@ static void usage(u8 *argv0, int more_help) { " quad -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " "or @@)\n" - " -t msec - timeout for each run (auto-scaled, 50-... ms, default %u ms)\n" + " -t msec - timeout for each run (auto-scaled, 50-... ms, default " + "%u ms)\n" " add a '+' to skip over seeds running longer.\n" " -m megs - memory limit for child process (%u MB, 0 = no limit " "[default])\n" @@ -123,10 +124,10 @@ static void usage(u8 *argv0, int more_help) { " -c program - enable CmpLog by specifying a binary compiled for " "it.\n" " if using QEMU, just use -c 0.\n" - " -l cmplog_level - set the complexity/intensivity of CmpLog.\n" - " Values: 1 (basic), 2 (larger files) and 3 " - "(transform)\n\n" - + " -l cmplog_opts - CmpLog configuration values (e.g. \"2AT\"):\n" + " 1=small files (default), 2=larger files, 3=all " + "files,\n" + " A=arithmetic solving, T=tranformational solving.\n\n" "Fuzzing behavior settings:\n" " -Z - sequential queue selection instead of weighted " "random\n" @@ -813,13 +814,36 @@ int main(int argc, char **argv_orig, char **envp) { case 'l': { - if (optarg) { afl->cmplog_lvl = atoi(optarg); } - if (afl->cmplog_lvl < 1 || afl->cmplog_lvl > CMPLOG_LVL_MAX) { + if (!optarg) { FATAL("missing parameter for 'l'"); } + char *c = optarg; + while (*c) { - FATAL( - "Bad complog level value, accepted values are 1 (default), 2 and " - "%u.", - CMPLOG_LVL_MAX); + switch (*c) { + + case '0': + case '1': + afl->cmplog_lvl = 1; + break; + case '2': + afl->cmplog_lvl = 2; + break; + case '3': + afl->cmplog_lvl = 3; + break; + case 'a': + case 'A': + afl->cmplog_enable_arith = 1; + break; + case 't': + case 'T': + afl->cmplog_enable_transform = 1; + break; + default: + FATAL("Unknown option value '%c' in -l %s", *c, optarg); + + } + + ++c; } -- cgit 1.4.1 From ac9cfd89dae0ce99df500793b3f5cbed2fd2e4f7 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sun, 21 Feb 2021 23:27:07 +0100 Subject: how to compare afl++ --- README.md | 7 +++++++ src/afl-cc.c | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/README.md b/README.md index d37826b5..59b1c143 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ If you want to use afl++ for your academic work, check the [papers page](https://aflplus.plus/papers/) on the website. To cite our work, look at the [Cite](#cite) section. + For comparisons use the fuzzbench `aflplusplus` setup, or use `afl-clang-fast` + with `AFL_LLVM_CMPLOG=1`. ## Major changes in afl++ 3.0 @@ -1170,6 +1172,11 @@ Thank you! If you use AFLplusplus in scientific work, consider citing [our paper](https://www.usenix.org/conference/woot20/presentation/fioraldi) presented at WOOT'20: +If you use AFLpluplus to compare to your work, please use either `afl-clang-lto` +or `afl-clang-fast` with `AFL_LLVM_CMPLOG=1` for building targets and +`afl-fuzz` with the command line option `-l 2` for fuzzing. +The most effective setup is the `aflplusplus` default fuzzer on Google's fuzzbench. + + Andrea Fioraldi, Dominik Maier, Heiko Eißfeldt, and Marc Heuse. “AFL++: Combining incremental steps of fuzzing research”. In 14th USENIX Workshop on Offensive Technologies (WOOT 20). USENIX Association, Aug. 2020. Bibtex: diff --git a/src/afl-cc.c b/src/afl-cc.c index d41f79a2..a5e54691 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1699,7 +1699,9 @@ int main(int argc, char **argv, char **envp) { "Do not be overwhelmed :) afl-cc uses good defaults if no options are " "selected.\n" "Read the documentation for FEATURES though, all are good but few are " - "defaults.\n\n"); + "defaults.\n"); + "Recommended is afl-clang-lto with AFL_LLVM_CMPLOG or afl-clang-fast with\n" + "AFL_LLVM_CMPLOG and AFL_LLVM_DICT2FILE.\n\n" exit(1); -- cgit 1.4.1 From 8ad78f5b65d09375dddfa679ccce76bec0862c28 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sun, 21 Feb 2021 23:42:08 +0100 Subject: fix --- src/afl-cc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index a5e54691..2eb8c575 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1699,9 +1699,9 @@ int main(int argc, char **argv, char **envp) { "Do not be overwhelmed :) afl-cc uses good defaults if no options are " "selected.\n" "Read the documentation for FEATURES though, all are good but few are " - "defaults.\n"); + "defaults.\n" "Recommended is afl-clang-lto with AFL_LLVM_CMPLOG or afl-clang-fast with\n" - "AFL_LLVM_CMPLOG and AFL_LLVM_DICT2FILE.\n\n" + "AFL_LLVM_CMPLOG and AFL_LLVM_DICT2FILE.\n\n"); exit(1); -- cgit 1.4.1 From 2785c8b197a1e7f109fa4dfb47fdd82eca0ad008 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 22 Feb 2021 12:34:37 +0100 Subject: crash fix --- src/afl-fuzz-init.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 702e732d..e372c803 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -1060,13 +1060,22 @@ void perform_dry_run(afl_state_t *afl) { p->perf_score = 0; u32 i = 0; - while (unlikely(afl->queue_buf[i]->disabled)) { + while (unlikely(i < afl->queued_paths && afl->queue_buf[i] && + afl->queue_buf[i]->disabled)) { ++i; } - afl->queue = afl->queue_buf[i]; + if (i < afl->queued_paths && afl->queue_buf[i]) { + + afl->queue = afl->queue_buf[i]; + + } else { + + afl->queue = afl->queue_buf[0]; + + } afl->max_depth = 0; for (i = 0; i < afl->queued_paths; i++) { -- cgit 1.4.1 From a252943236b12c080248747bee06c9c5084b871e Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 22 Feb 2021 12:59:01 +0100 Subject: another fix for disabled entries --- docs/Changelog.md | 1 + src/afl-fuzz.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 477498d0..a006fccb 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -32,6 +32,7 @@ sending a mail to . after every queue entry as this can take very long time otherwise - better detection if a target needs a large shared map - fix for `-Z` + - fixed a few crashes - switched to an even faster RNG - added hghwng's patch for faster trace map analysis - afl-cc diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e2db029d..24d77cc9 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1770,12 +1770,15 @@ int main(int argc, char **argv_orig, char **envp) { if (unlikely(afl->old_seed_selection)) { afl->current_entry = 0; - while (unlikely(afl->queue_buf[afl->current_entry]->disabled)) { + while (unlikely(afl->current_entry < afl->queued_paths && + afl->queue_buf[afl->current_entry]->disabled)) { ++afl->current_entry; } + if (afl->current_entry >= afl->queued_paths) { afl->current_entry = 0; } + afl->queue_cur = afl->queue_buf[afl->current_entry]; if (unlikely(seek_to)) { -- cgit 1.4.1 From 70fe872940b9815698b4317bdde33da1dae27923 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 22 Feb 2021 16:39:38 +0100 Subject: ensure a valid seed exists --- instrumentation/afl-compiler-rt.o.c | 2 ++ src/afl-cc.c | 3 ++- src/afl-fuzz.c | 7 ++++++- 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 324d541d..e4aeadfa 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1675,6 +1675,7 @@ 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; + __cmplog_rtn_hook(get_llvm_stdstring(stdstring), cstring); } @@ -1684,6 +1685,7 @@ 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)) return; + __cmplog_rtn_hook(get_llvm_stdstring(stdstring1), get_llvm_stdstring(stdstring2)); diff --git a/src/afl-cc.c b/src/afl-cc.c index 2eb8c575..5f471355 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1700,7 +1700,8 @@ int main(int argc, char **argv, char **envp) { "selected.\n" "Read the documentation for FEATURES though, all are good but few are " "defaults.\n" - "Recommended is afl-clang-lto with AFL_LLVM_CMPLOG or afl-clang-fast with\n" + "Recommended is afl-clang-lto with AFL_LLVM_CMPLOG or afl-clang-fast " + "with\n" "AFL_LLVM_CMPLOG and AFL_LLVM_DICT2FILE.\n\n"); exit(1); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 24d77cc9..9137dc23 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1707,7 +1707,12 @@ int main(int argc, char **argv_orig, char **envp) { cull_queue(afl); - if (!afl->pending_not_fuzzed) { + // ensure we have at least one seed that is not disabled. + u32 entry, valid_seeds = 0; + for (entry = 0; entry < afl->queued_paths; ++entry) + if (!afl->queue_buf[entry]->disabled) { ++valid_seeds; } + + if (!afl->pending_not_fuzzed || !valid_seeds) { FATAL("We need at least one valid input seed that does not crash!"); -- cgit 1.4.1 From fb2a6b6941ffb6cf575d2a772c6e0d47b49835ee Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 22 Feb 2021 16:56:35 +0100 Subject: minimum sync time --- docs/Changelog.md | 1 + include/afl-fuzz.h | 1 + include/config.h | 5 +++++ src/afl-fuzz-run.c | 2 ++ src/afl-fuzz.c | 15 ++++++++++++--- 5 files changed, 21 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index a006fccb..d8587334 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -35,6 +35,7 @@ sending a mail to . - fixed a few crashes - switched to an even faster RNG - added hghwng's patch for faster trace map analysis + - added minimum SYNC_TIME to include/config.h (30 minutes default) - afl-cc - allow instrumenting LLVMFuzzerTestOneInput - fixed endless loop for allow/blocklist lines starting with a diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 10d94fed..e191543a 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -570,6 +570,7 @@ typedef struct afl_state { blocks_eff_total, /* Blocks subject to effector maps */ blocks_eff_select, /* Blocks selected as fuzzable */ start_time, /* Unix start time (ms) */ + last_sync_time, /* Time of last sync */ last_path_time, /* Time for most recent path (ms) */ last_crash_time, /* Time for most recent crash (ms) */ last_hang_time; /* Time for most recent hang (ms) */ diff --git a/include/config.h b/include/config.h index 535ce0d3..083cad23 100644 --- a/include/config.h +++ b/include/config.h @@ -280,6 +280,11 @@ #define SYNC_INTERVAL 8 +/* Sync time (minimum time between syncing in ms, time is halfed for -M main + nodes): */ + +#define SYNC_TIME 18000000LLU /* 18000000 = 30 minutes */ + /* Output directory reuse grace period (minutes): */ #define OUTPUT_GRACE 25 diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 97cb7415..0b84a542 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -707,6 +707,8 @@ void sync_fuzzers(afl_state_t *afl) { if (afl->foreign_sync_cnt) read_foreign_testcases(afl, 0); + afl->last_sync_time = get_cur_time(); + } /* Trim all new test cases to save cycles when doing deterministic checks. The diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 9137dc23..f83aac9e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1986,15 +1986,24 @@ int main(int argc, char **argv_orig, char **envp) { if (unlikely(afl->is_main_node)) { - if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) { + if (unlikely(get_cur_time() > + (SYNC_TIME >> 1) + afl->last_sync_time)) { - sync_fuzzers(afl); + if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) { + + sync_fuzzers(afl); + + } } } else { - if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); } + if (unlikely(get_cur_time() > SYNC_TIME + afl->last_sync_time)) { + + if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); } + + } } -- cgit 1.4.1 From 7674dac1a16a9963e22eb628fdbd409befe19979 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 22 Feb 2021 18:17:35 +0100 Subject: auto mode for CTX + NGRAM --- src/afl-cc.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 5f471355..783cf23e 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1421,6 +1421,14 @@ int main(int argc, char **argv, char **envp) { } + if (instrument_opt_mode && instrument_mode == INSTRUMENT_DEFAULT && + (compiler_mode == LLVM || compiler_mode == UNSET)) { + + instrument_mode = INSTRUMENT_CLASSIC; + compiler_mode = LLVM; + + } + if (!compiler_mode) { // lto is not a default because outside of afl-cc RANLIB and AR have to -- cgit 1.4.1 From 745bc083d144a31981b3686d68bc34777758b359 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 22 Feb 2021 18:22:09 +0100 Subject: fix error msg --- src/afl-cc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 783cf23e..c3910e6d 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1802,8 +1802,8 @@ int main(int argc, char **argv, char **envp) { if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC && instrument_mode != INSTRUMENT_CFG) FATAL( - "CTX and NGRAM instrumentation options can only be used with CFG " - "(recommended) and CLASSIC instrumentation modes!"); + "CTX and NGRAM instrumentation options can only be used with LLVM and " + "CFG or CLASSIC instrumentation modes!"); if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO")) FATAL( -- cgit 1.4.1 From cc7c651dc97f5567af33164d396b390d36b46049 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 24 Feb 2021 18:07:08 +0100 Subject: tidied up env suggestions --- docs/Changelog.md | 3 ++- src/afl-common.c | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index d8587334..1260531e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -36,6 +36,7 @@ sending a mail to . - switched to an even faster RNG - added hghwng's patch for faster trace map analysis - added minimum SYNC_TIME to include/config.h (30 minutes default) + - printing suggestions for mistyped `AFL_` env variables - afl-cc - allow instrumenting LLVMFuzzerTestOneInput - fixed endless loop for allow/blocklist lines starting with a @@ -68,7 +69,7 @@ sending a mail to . - set AFL_IGNORE_UNKNOWN_ENVS to not warn on unknown AFL_... env vars. - added dummy Makefile to instrumentation/ - Updated utils/afl_frida to be 5% faster, 7% on x86_x64 - - Added AFL_KILL_SIGNAL env variable (thanks @v-p-b) + - Added `AFL_KILL_SIGNAL` env variable (thanks @v-p-b) - @Edznux added a nice documentation on how to use rpc.statsd with afl++ in docs/rpc_statsd.md, thanks! diff --git a/src/afl-common.c b/src/afl-common.c index ce63c262..1628e517 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -529,6 +529,8 @@ static int string_distance_levenshtein(char *s1, char *s2) { s1len = strlen(s1); s2len = strlen(s2); unsigned int column[s1len + 1]; + column[s1len] = 1; + for (y = 1; y <= s1len; y++) column[y] = y; for (x = 1; x <= s2len; x++) { @@ -608,6 +610,8 @@ void print_suggested_envs(char *mispelled_env) { }; + ck_free(reduced); + } if (found) goto cleanup; -- cgit 1.4.1 From 48a1a29baa49b633855f49536d72783d5a489b82 Mon Sep 17 00:00:00 2001 From: hexcoder Date: Wed, 24 Feb 2021 20:34:33 +0100 Subject: typos --- src/afl-fuzz.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index f83aac9e..d4e3958e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -127,7 +127,7 @@ static void usage(u8 *argv0, int more_help) { " -l cmplog_opts - CmpLog configuration values (e.g. \"2AT\"):\n" " 1=small files (default), 2=larger files, 3=all " "files,\n" - " A=arithmetic solving, T=tranformational solving.\n\n" + " A=arithmetic solving, T=transformational solving.\n\n" "Fuzzing behavior settings:\n" " -Z - sequential queue selection instead of weighted " "random\n" @@ -139,8 +139,8 @@ static void usage(u8 *argv0, int more_help) { "Testing settings:\n" " -s seed - use a fixed seed for the RNG\n" - " -V seconds - fuzz for a specific time then terminate\n" - " -E execs - fuzz for a approx. no of total executions then " + " -V seconds - fuzz for a specified time then terminate\n" + " -E execs - fuzz for an approx. no. of total executions then " "terminate\n" " Note: not precise and can have several more " "executions.\n\n" -- cgit 1.4.1 From 047f3436e95b40d541bcc5b688be0052ef5e798e Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 24 Feb 2021 21:29:00 +0100 Subject: edges in plot file --- include/afl-fuzz.h | 4 ++-- src/afl-fuzz-init.c | 2 +- src/afl-fuzz-stats.c | 21 +++++++++++---------- src/afl-fuzz.c | 8 ++++---- 4 files changed, 18 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index d9dbad5a..3531d672 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1072,8 +1072,8 @@ void destroy_extras(afl_state_t *); void load_stats_file(afl_state_t *); void write_setup_file(afl_state_t *, u32, char **); -void write_stats_file(afl_state_t *, double, double, double); -void maybe_update_plot_file(afl_state_t *, double, double); +void write_stats_file(afl_state_t *, u32, double, double, double); +void maybe_update_plot_file(afl_state_t *, u32, double, double); void show_stats(afl_state_t *); void show_init_stats(afl_state_t *); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index e372c803..ab743f4b 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2026,7 +2026,7 @@ void setup_dirs_fds(afl_state_t *afl) { fprintf(afl->fsrv.plot_file, "# unix_time, cycles_done, cur_path, paths_total, " "pending_total, pending_favs, map_size, unique_crashes, " - "unique_hangs, max_depth, execs_per_sec\n"); + "unique_hangs, max_depth, execs_per_sec, edges_found\n"); fflush(afl->fsrv.plot_file); /* ignore errors */ diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 66efeb20..192fdd62 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -185,15 +185,14 @@ void load_stats_file(afl_state_t *afl) { /* Update stats file for unattended monitoring. */ -void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, - double eps) { +void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, + double stability, double eps) { #ifndef __HAIKU__ struct rusage rus; #endif u64 cur_time = get_cur_time(); - u32 t_bytes = count_non_255_bytes(afl, afl->virgin_bits); u8 fn[PATH_MAX]; FILE *f; @@ -353,7 +352,8 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, /* Update the plot file if there is a reason to. */ -void maybe_update_plot_file(afl_state_t *afl, double bitmap_cvg, double eps) { +void maybe_update_plot_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, + double eps) { if (unlikely(afl->plot_prev_qp == afl->queued_paths && afl->plot_prev_pf == afl->pending_favored && @@ -384,16 +384,16 @@ void maybe_update_plot_file(afl_state_t *afl, double bitmap_cvg, double eps) { /* Fields in the file: unix_time, afl->cycles_done, cur_path, paths_total, paths_not_fuzzed, - favored_not_fuzzed, afl->unique_crashes, afl->unique_hangs, afl->max_depth, - execs_per_sec */ + favored_not_fuzzed, unique_crashes, unique_hangs, max_depth, + execs_per_sec, edges_found */ fprintf( afl->fsrv.plot_file, - "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu\n", + "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu, %u\n", get_cur_time() / 1000, afl->queue_cycle - 1, afl->current_entry, afl->queued_paths, afl->pending_not_fuzzed, afl->pending_favored, bitmap_cvg, afl->unique_crashes, afl->unique_hangs, afl->max_depth, eps, - afl->plot_prev_ed); /* ignore errors */ + afl->plot_prev_ed, t_bytes); /* ignore errors */ fflush(afl->fsrv.plot_file); @@ -532,7 +532,8 @@ void show_stats(afl_state_t *afl) { if (cur_ms - afl->stats_last_stats_ms > STATS_UPDATE_SEC * 1000) { afl->stats_last_stats_ms = cur_ms; - write_stats_file(afl, t_byte_ratio, stab_ratio, afl->stats_avg_exec); + write_stats_file(afl, t_bytes, t_byte_ratio, stab_ratio, + afl->stats_avg_exec); save_auto(afl); write_bitmap(afl); @@ -555,7 +556,7 @@ void show_stats(afl_state_t *afl) { if (cur_ms - afl->stats_last_plot_ms > PLOT_UPDATE_SEC * 1000) { afl->stats_last_plot_ms = cur_ms; - maybe_update_plot_file(afl, t_byte_ratio, afl->stats_avg_exec); + maybe_update_plot_file(afl, t_bytes, t_byte_ratio, afl->stats_avg_exec); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index f83aac9e..f029ef83 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1724,8 +1724,8 @@ int main(int argc, char **argv_orig, char **envp) { afl->start_time = get_cur_time(); if (afl->in_place_resume || afl->afl_env.afl_autoresume) load_stats_file(afl); - write_stats_file(afl, 0, 0, 0); - maybe_update_plot_file(afl, 0, 0); + write_stats_file(afl, 0, 0, 0, 0); + maybe_update_plot_file(afl, 0, 0, 0); save_auto(afl); if (afl->stop_soon) { goto stop_fuzzing; } @@ -2018,12 +2018,12 @@ int main(int argc, char **argv_orig, char **envp) { } write_bitmap(afl); - maybe_update_plot_file(afl, 0, 0); + maybe_update_plot_file(afl, 0, 0, 0); save_auto(afl); stop_fuzzing: - write_stats_file(afl, 0, 0, 0); + write_stats_file(afl, 0, 0, 0, 0); afl->force_ui_update = 1; // ensure the screen is reprinted show_stats(afl); // print the screen one last time -- cgit 1.4.1 From 2f7e57f6aa4f09d0b6f55b6592fbd70d2c2c1064 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 25 Feb 2021 10:04:41 +0100 Subject: helper_min3 func --- src/afl-common.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/afl-common.c b/src/afl-common.c index 1628e517..078ffb9d 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -518,8 +518,12 @@ int parse_afl_kill_signal_env(u8 *afl_kill_signal_env, int default_signal) { } -#define HELPER_MIN3(a, b, c) \ - ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c))) +static inline unsigned int helper_min3(unsigned int a, unsigned int b, + unsigned int c) { + + return a < b ? (a < c ? a : c) : (b < c ? b : c); + +} // from // https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C @@ -539,7 +543,7 @@ static int string_distance_levenshtein(char *s1, char *s2) { for (y = 1, lastdiag = x - 1; y <= s1len; y++) { olddiag = column[y]; - column[y] = HELPER_MIN3(column[y] + 1, column[y - 1] + 1, + column[y] = helper_min3(column[y] + 1, column[y - 1] + 1, lastdiag + (s1[y - 1] == s2[x - 1] ? 0 : 1)); lastdiag = olddiag; @@ -551,8 +555,6 @@ static int string_distance_levenshtein(char *s1, char *s2) { } -#undef HELPER_MIN3 - #define ENV_SIMILARITY_TRESHOLD 3 void print_suggested_envs(char *mispelled_env) { -- cgit 1.4.1 From 6c9777de13ebd9a8c1cd20c2124aff7e4e31d579 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 25 Feb 2021 10:42:39 +0100 Subject: edges in afl-plot --- afl-plot | 11 +++++++++-- src/afl-fuzz-init.c | 2 +- src/afl-fuzz-stats.c | 3 ++- 3 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/afl-plot b/afl-plot index 0faed0ec..ba100d3e 100755 --- a/afl-plot +++ b/afl-plot @@ -99,7 +99,7 @@ if [ ! -d "$outputdir" ]; then fi -rm -f "$outputdir/high_freq.png" "$outputdir/low_freq.png" "$outputdir/exec_speed.png" +rm -f "$outputdir/high_freq.png" "$outputdir/low_freq.png" "$outputdir/exec_speed.png" "$outputdir/edges.png" mv -f "$outputdir/index.html" "$outputdir/index.html.orig" 2>/dev/null echo "[*] Generating plots..." @@ -152,6 +152,12 @@ set ytics auto plot '$inputdir/plot_data' using 1:11 with filledcurve x1 title '' linecolor rgb '#0090ff' fillstyle transparent solid 0.2 noborder, \\ '$inputdir/plot_data' using 1:11 with lines title ' execs/sec' linecolor rgb '#0090ff' linewidth 3 smooth bezier; +set terminal png truecolor enhanced size 1000,300 butt +set output '$outputdir/edges.png' + +set ytics auto +plot '$inputdir/plot_data' using 1:13 with lines title ' edges' linecolor rgb '#0090ff' linewidth 3 + _EOF_ ) | gnuplot @@ -172,6 +178,7 @@ cat >"$outputdir/index.html" <<_EOF_ Generated on:`date`

      +

      @@ -183,7 +190,7 @@ _EOF_ # sensitive, this seems like a reasonable trade-off. chmod 755 "$outputdir" -chmod 644 "$outputdir/high_freq.png" "$outputdir/low_freq.png" "$outputdir/exec_speed.png" "$outputdir/index.html" +chmod 644 "$outputdir/high_freq.png" "$outputdir/low_freq.png" "$outputdir/exec_speed.png" "$outputdir/edges.png" "$outputdir/index.html" echo "[+] All done - enjoy your charts!" diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index ab743f4b..d85a83e0 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2026,7 +2026,7 @@ void setup_dirs_fds(afl_state_t *afl) { fprintf(afl->fsrv.plot_file, "# unix_time, cycles_done, cur_path, paths_total, " "pending_total, pending_favs, map_size, unique_crashes, " - "unique_hangs, max_depth, execs_per_sec, edges_found\n"); + "unique_hangs, max_depth, execs_per_sec, total_execs, edges_found\n"); fflush(afl->fsrv.plot_file); /* ignore errors */ diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 192fdd62..42c71b05 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -355,7 +355,8 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, void maybe_update_plot_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, double eps) { - if (unlikely(afl->plot_prev_qp == afl->queued_paths && + if (unlikely(afl->stop_soon) || + unlikely(afl->plot_prev_qp == afl->queued_paths && afl->plot_prev_pf == afl->pending_favored && afl->plot_prev_pnf == afl->pending_not_fuzzed && afl->plot_prev_ce == afl->current_entry && -- cgit 1.4.1 From ee0ca07f3c9f7c5971440f5dca70a2ee6f37584d Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Thu, 25 Feb 2021 12:19:46 +0100 Subject: changing the -t ...+ meaning to "auto-calculate buth this is the max" --- README.md | 7 ++++++- docs/Changelog.md | 21 +++++++++++++-------- src/afl-fuzz-init.c | 33 ++++++++++++--------------------- src/afl-fuzz-stats.c | 16 ++++++++-------- src/afl-fuzz.c | 33 +++++++++++++++++++++++++++++---- 5 files changed, 68 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/README.md b/README.md index 0539752c..119426f6 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,12 @@ For comparisons use the fuzzbench `aflplusplus` setup, or use `afl-clang-fast` with `AFL_LLVM_CMPLOG=1`. -## Major changes in afl++ 3.0 +## Major changes in afl++ 3.0 + 3.1 + +With afl++ 3.1 we introduced the following changes from previous behaviours: + * The '+' feature of the '-t' option now means to auto-calculate the timeout + with the value given being the maximum timeout. The original meaning of + "skipping timeouts instead of abort" is now inherent to the -t option. With afl++ 3.0 we introduced changes that break some previous afl and afl++ behaviours and defaults: diff --git a/docs/Changelog.md b/docs/Changelog.md index 9a61fac3..c4347baf 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -16,26 +16,31 @@ sending a mail to . to be placed in the source code. Check out instrumentation/README.instrument_list.md - afl-fuzz - - Making AFL_MAP_SIZE (mostly) obsolete - afl-fuzz now learns on start - the target map size + - Making AFL_MAP_SIZE (mostly) obsolete - afl-fuzz now learns on + start the target map size - upgraded cmplog/redqueen: solving for floating point, solving transformations (e.g. toupper, tolower, to/from hex, xor, arithmetics, etc.). This is costly hence new command line option - `-l` that sets the intensity (values 1 to 3). Recommended is 1 or 2. - - added `AFL_CMPLOG_ONLY_NEW` to not use cmplog on initial testcases from - `-i` or resumes (as these have most likely already been done) + `-l` that sets the intensity (values 1 to 3). Recommended is 2. + - added `AFL_CMPLOG_ONLY_NEW` to not use cmplog on initial seeds + from `-i` or resumes (these have most likely already been done) - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) - on restarts (`-i`)/autoresume (AFL_AUTORESUME) the stats are now reloaded and used, thanks to Vimal Joseph for this patch! - - if deterministic mode is active (`-D`, or `-M` without `-d`) then we sync - after every queue entry as this can take very long time otherwise + - changed the meaning of '+' of the '-t' option, it now means to + auto-calculate the timeout with the value given being the max + timeout. The original meaning of skipping timeouts instead of + abort is now inherent to the -t option. + - if deterministic mode is active (`-D`, or `-M` without `-d`) then + we sync after every queue entry as this can take very long time + otherwise + - added minimum SYNC_TIME to include/config.h (30 minutes default) - better detection if a target needs a large shared map - fix for `-Z` - fixed a few crashes - switched to an even faster RNG - added hghwng's patch for faster trace map analysis - - added minimum SYNC_TIME to include/config.h (30 minutes default) - printing suggestions for mistyped `AFL_` env variables - afl-cc - allow instrumenting LLVMFuzzerTestOneInput diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index d85a83e0..3dbc4c65 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -882,32 +882,23 @@ void perform_dry_run(afl_state_t *afl) { if (afl->timeout_given) { - /* The -t nn+ syntax in the command line sets afl->timeout_given to - '2' and instructs afl-fuzz to tolerate but skip queue entries that - time out. */ + /* if we have a timeout but a timeout value was given then always + skip. The '+' meaning has been changed! */ + WARNF("Test case results in a timeout (skipping)"); + ++cal_failures; + q->cal_failed = CAL_CHANCES; + q->disabled = 1; + q->perf_score = 0; - if (afl->timeout_given > 1) { + if (!q->was_fuzzed) { - WARNF("Test case results in a timeout (skipping)"); - q->cal_failed = CAL_CHANCES; - ++cal_failures; - break; + q->was_fuzzed = 1; + --afl->pending_not_fuzzed; + --afl->active_paths; } - SAYF("\n" cLRD "[-] " cRST - "The program took more than %u ms to process one of the initial " - "test cases.\n" - " Usually, the right thing to do is to relax the -t option - " - "or to delete it\n" - " altogether and allow the fuzzer to auto-calibrate. That " - "said, if you know\n" - " what you are doing and want to simply skip the unruly test " - "cases, append\n" - " '+' at the end of the value passed to -t ('-t %u+').\n", - afl->fsrv.exec_tmout, afl->fsrv.exec_tmout); - - FATAL("Test case '%s' results in a timeout", fn); + break; } else { diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 42c71b05..bd856088 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -388,13 +388,13 @@ void maybe_update_plot_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, favored_not_fuzzed, unique_crashes, unique_hangs, max_depth, execs_per_sec, edges_found */ - fprintf( - afl->fsrv.plot_file, - "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu, %u\n", - get_cur_time() / 1000, afl->queue_cycle - 1, afl->current_entry, - afl->queued_paths, afl->pending_not_fuzzed, afl->pending_favored, - bitmap_cvg, afl->unique_crashes, afl->unique_hangs, afl->max_depth, eps, - afl->plot_prev_ed, t_bytes); /* ignore errors */ + fprintf(afl->fsrv.plot_file, + "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu, " + "%u\n", + get_cur_time() / 1000, afl->queue_cycle - 1, afl->current_entry, + afl->queued_paths, afl->pending_not_fuzzed, afl->pending_favored, + bitmap_cvg, afl->unique_crashes, afl->unique_hangs, afl->max_depth, + eps, afl->plot_prev_ed, t_bytes); /* ignore errors */ fflush(afl->fsrv.plot_file); @@ -1219,7 +1219,7 @@ void show_init_stats(afl_state_t *afl) { stringify_int(IB(0), min_us), stringify_int(IB(1), max_us), stringify_int(IB(2), avg_us)); - if (!afl->timeout_given) { + if (afl->timeout_given != 1) { /* Figure out the appropriate timeout. The basic idea is: 5x average or 1x max, rounded up to EXEC_TM_ROUND ms and capped at 1 second. diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 5810e9a9..a02eadb2 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -103,9 +103,10 @@ static void usage(u8 *argv0, int more_help) { " quad -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " "or @@)\n" - " -t msec - timeout for each run (auto-scaled, 50-... ms, default " - "%u ms)\n" - " add a '+' to skip over seeds running longer.\n" + " -t msec - timeout for each run (auto-scaled, default %u ms). " + "Add a '+'\n" + " to auto-calculate the timeout, the value being the " + "maximum.\n" " -m megs - memory limit for child process (%u MB, 0 = no limit " "[default])\n" " -Q - use binary-only instrumentation (QEMU mode)\n" @@ -1453,7 +1454,7 @@ int main(int argc, char **argv_orig, char **envp) { } - if (!afl->timeout_given) { find_timeout(afl); } + if (!afl->timeout_given) { find_timeout(afl); } // only for resumes! if ((afl->tmp_dir = afl->afl_env.afl_tmpdir) != NULL && !afl->in_place_resume) { @@ -1718,6 +1719,30 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->timeout_given == 2) { // -t ...+ option + + if (valid_seeds == 1) { + + WARNF( + "Only one valid seed is present, auto-calculating the timeout is " + "disabled!"); + afl->timeout_given = 1; + + } else { + + u64 max_ms = 0; + + for (entry = 0; entry < afl->queued_paths; ++entry) + if (!afl->queue_buf[entry]->disabled) + if (afl->queue_buf[entry]->exec_us > max_ms) + max_ms = afl->queue_buf[entry]->exec_us; + + afl->fsrv.exec_tmout = max_ms; + + } + + } + show_init_stats(afl); if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl); -- cgit 1.4.1 From 79f1a44a01775ab28ad39f21f09e084fcd773c98 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 27 Feb 2021 18:14:50 +0100 Subject: fix qasan search path --- qemu_mode/qemuafl | 2 +- src/afl-common.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl index 213f3b27..e36a30eb 160000 --- a/qemu_mode/qemuafl +++ b/qemu_mode/qemuafl @@ -1 +1 @@ -Subproject commit 213f3b27dd099ef352181c48cd75c0f20a73e3f0 +Subproject commit e36a30ebca57ca433a5d6e20b1a32975aabb761b diff --git a/src/afl-common.c b/src/afl-common.c index 078ffb9d..c341bb97 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -372,11 +372,11 @@ u8 *get_libqasan_path(u8 *own_loc) { } - if (!access(BIN_PATH "/libqasan.so", X_OK)) { + if (!access(AFL_PATH "/libqasan.so", X_OK)) { if (cp) { ck_free(cp); } - return ck_strdup(BIN_PATH "/libqasan.so"); + return ck_strdup(AFL_PATH "/libqasan.so"); } -- cgit 1.4.1 From f81ef4abf41184a24d24828841c82b98b9216ddc Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sun, 28 Feb 2021 00:12:21 +0100 Subject: fix afl-common compile --- src/afl-common.c | 4 ++++ utils/afl_network_proxy/GNUmakefile | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-common.c b/src/afl-common.c index c341bb97..fa4aec7f 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -47,6 +47,10 @@ u8 be_quiet = 0; u8 *doc_path = ""; u8 last_intr = 0; +#ifndef AFL_PATH + #define AFL_PATH "/usr/local/lib/afl/" +#endif + void detect_file_args(char **argv, u8 *prog_in, bool *use_stdin) { u32 i = 0; diff --git a/utils/afl_network_proxy/GNUmakefile b/utils/afl_network_proxy/GNUmakefile index 25a3df82..0b55dc2c 100644 --- a/utils/afl_network_proxy/GNUmakefile +++ b/utils/afl_network_proxy/GNUmakefile @@ -1,5 +1,6 @@ PREFIX ?= /usr/local BIN_PATH = $(PREFIX)/bin +HELPER_PATH = $(PREFIX)/lib/afl DOC_PATH = $(PREFIX)/share/doc/afl PROGRAMS = afl-network-client afl-network-server @@ -31,7 +32,7 @@ afl-network-client: afl-network-client.c $(CC) $(CFLAGS) -I../../include -o afl-network-client afl-network-client.c $(LDFLAGS) afl-network-server: afl-network-server.c - $(CC) $(CFLAGS) -I../../include -o afl-network-server afl-network-server.c ../../src/afl-forkserver.c ../../src/afl-sharedmem.c ../../src/afl-common.c -DBIN_PATH=\"$(BIN_PATH)\" $(LDFLAGS) + $(CC) $(CFLAGS) -I../../include -o afl-network-server afl-network-server.c ../../src/afl-forkserver.c ../../src/afl-sharedmem.c ../../src/afl-common.c -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" $(LDFLAGS) clean: rm -f $(PROGRAMS) *~ core -- cgit 1.4.1 From 4619a1395b9a414e5e11148d79fde3a7fa348e87 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 1 Mar 2021 09:57:57 +0100 Subject: ensure proper aligning for skim patch --- instrumentation/afl-compiler-rt.o.c | 8 ++++++-- instrumentation/afl-llvm-lto-instrumentation.so.cc | 4 +--- src/afl-common.c | 2 +- src/afl-forkserver.c | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index e4aeadfa..ecb94cab 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -244,8 +244,12 @@ static void __afl_map_shm(void) { if (__afl_final_loc) { - if (__afl_final_loc % 32) - __afl_final_loc = (((__afl_final_loc + 31) >> 5) << 5); + if (__afl_final_loc % 64) { + + __afl_final_loc = (((__afl_final_loc + 63) >> 6) << 6); + + } + __afl_map_size = __afl_final_loc; if (__afl_final_loc > MAP_SIZE) { diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc index f5c24e41..137bae2c 100644 --- a/instrumentation/afl-llvm-lto-instrumentation.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc @@ -924,9 +924,7 @@ bool AFLLTOPass::runOnModule(Module &M) { if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) { - uint32_t write_loc = afl_global_id; - - if (afl_global_id % 32) write_loc = (((afl_global_id + 32) >> 4) << 4); + uint32_t write_loc = (((afl_global_id + 63) >> 6) << 6); GlobalVariable *AFLFinalLoc = new GlobalVariable( M, Int32Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_final_loc"); diff --git a/src/afl-common.c b/src/afl-common.c index fa4aec7f..a306fe5e 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -1135,7 +1135,7 @@ u32 get_map_size(void) { } - if (map_size % 32) { map_size = (((map_size >> 5) + 1) << 5); } + if (map_size % 64) { map_size = (((map_size >> 6) + 1) << 6); } } diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 9ee59822..fd5edc98 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -656,11 +656,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if (!fsrv->map_size) { fsrv->map_size = MAP_SIZE; } - if (unlikely(tmp_map_size % 32)) { + if (unlikely(tmp_map_size % 64)) { // should not happen WARNF("Target reported non-aligned map size of %u", tmp_map_size); - tmp_map_size = (((tmp_map_size + 31) >> 5) << 5); + tmp_map_size = (((tmp_map_size + 63) >> 6) << 6); } -- cgit 1.4.1 From 7259075b71924e7ab68546aca04fa5ecfe2d93d6 Mon Sep 17 00:00:00 2001 From: aflpp Date: Mon, 1 Mar 2021 19:13:29 +0100 Subject: len for cmplog rtn --- src/afl-fuzz-redqueen.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 1ab5f996..9bfbf95b 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1853,7 +1853,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, - u8 *o_pattern, u8 *changed_val, u32 idx, + u8 *o_pattern, u8 *changed_val, u8 plen, u32 idx, u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, u8 lvl, u8 *status) { @@ -1866,7 +1866,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, u8 save[40]; u32 saved_idx = idx, pre, from = 0, to = 0, i, j; - u32 its_len = MIN((u32)32, len - idx); + u32 its_len = MIN((u32)plen, len - idx); its_len = MIN(its_len, taint_len); u32 saved_its_len = its_len; @@ -2365,9 +2365,9 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, status = 0; - if (unlikely(rtn_extend_encoding(afl, o->v0, o->v1, orig_o->v0, - orig_o->v1, idx, taint_len, orig_buf, - buf, cbuf, len, lvl, &status))) { + if (unlikely(rtn_extend_encoding( + afl, o->v0, o->v1, orig_o->v0, orig_o->v1, SHAPE_BYTES(h->shape), + idx, taint_len, orig_buf, buf, cbuf, len, lvl, &status))) { return 1; @@ -2382,9 +2382,9 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, status = 0; - if (unlikely(rtn_extend_encoding(afl, o->v1, o->v0, orig_o->v1, - orig_o->v0, idx, taint_len, orig_buf, - buf, cbuf, len, lvl, &status))) { + if (unlikely(rtn_extend_encoding( + afl, o->v1, o->v0, orig_o->v1, orig_o->v0, SHAPE_BYTES(h->shape), + idx, taint_len, orig_buf, buf, cbuf, len, lvl, &status))) { return 1; -- cgit 1.4.1 From 108e588e888df5c2679600ea49846a565bac23f9 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 2 Mar 2021 17:46:43 +0100 Subject: add de-unicoded dictionary entries --- docs/Changelog.md | 2 + include/afl-fuzz.h | 1 + src/afl-fuzz-extras.c | 149 ++++++++++++++++++++++++++++++++++++++++++-------- src/afl-fuzz.c | 7 ++- 4 files changed, 134 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 01240b2a..376f5f06 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,6 +9,8 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . ### Version ++3.11a (dev) + - afl-fuzz + - add non-unicode variants from unicode-looking dictionary entries - afl-cc - fixed for a crash that can occur with ASAN + CMPLOG together plus better support for unicode (thanks to @stbergmann for reporting!) diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 3531d672..5003b563 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1062,6 +1062,7 @@ u8 has_new_bits_unclassified(afl_state_t *, u8 *); void load_extras_file(afl_state_t *, u8 *, u32 *, u32 *, u32); void load_extras(afl_state_t *, u8 *); void dedup_extras(afl_state_t *); +void deunicode_extras(afl_state_t *); void add_extra(afl_state_t *afl, u8 *mem, u32 len); void maybe_add_auto(afl_state_t *, u8 *, u32); void save_auto(afl_state_t *); diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 7ecad233..52100fa1 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -387,6 +387,130 @@ static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) { } +/* add an extra/dict/token - no checks performed, no sorting */ + +static void add_extra_nocheck(afl_state_t *afl, u8 *mem, u32 len) { + + afl->extras = afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); + + if (unlikely(!afl->extras)) { PFATAL("alloc"); } + + afl->extras[afl->extras_cnt].data = ck_alloc(len); + afl->extras[afl->extras_cnt].len = len; + memcpy(afl->extras[afl->extras_cnt].data, mem, len); + afl->extras_cnt++; + + /* We only want to print this once */ + + if (afl->extras_cnt == afl->max_det_extras + 1) { + + WARNF("More than %u tokens - will use them probabilistically.", + afl->max_det_extras); + + } + +} + +/* Sometimes strings in input is transformed to unicode internally, so for + fuzzing we should attempt to de-unicode if it looks like simple unicode */ + +void deunicode_extras(afl_state_t *afl) { + + if (!afl->extras_cnt) return; + + u32 i, j, orig_cnt = afl->extras_cnt; + u8 buf[64]; + + for (i = 0; i < orig_cnt; ++i) { + + if (afl->extras[i].len < 6 || afl->extras[i].len > 64 || + afl->extras[i].len % 2) { + + continue; + + } + + u32 k = 0, z1 = 0, z2 = 0, z3 = 0, z4 = 0, half = afl->extras[i].len >> 1; + u32 quarter = half >> 1; + + for (j = 0; j < afl->extras[i].len; ++j) { + + switch (j % 4) { + + case 2: + if (!afl->extras[i].data[j]) { ++z3; } + // fall through + case 0: + if (!afl->extras[i].data[j]) { ++z1; } + break; + case 3: + if (!afl->extras[i].data[j]) { ++z4; } + // fall through + case 1: + if (!afl->extras[i].data[j]) { ++z2; } + break; + + } + + } + + if ((z1 < half && z2 < half) || z1 + z2 == afl->extras[i].len) { continue; } + + // also maybe 32 bit unicode? + if (afl->extras[i].len % 4 == 0 && afl->extras[i].len >= 12 && + (z3 == quarter || z4 == quarter) && z1 + z2 == quarter * 3) { + + for (j = 0; j < afl->extras[i].len; ++j) { + + if (z4 < quarter) { + + if (j % 4 == 3) { buf[k++] = afl->extras[i].data[j]; } + + } else if (z3 < quarter) { + + if (j % 4 == 2) { buf[k++] = afl->extras[i].data[j]; } + + } else if (z2 < half) { + + if (j % 4 == 1) { buf[k++] = afl->extras[i].data[j]; } + + } else { + + if (j % 4 == 0) { buf[k++] = afl->extras[i].data[j]; } + + } + + } + + add_extra_nocheck(afl, buf, k); + k = 0; + + } + + for (j = 0; j < afl->extras[i].len; ++j) { + + if (z1 < half) { + + if (j % 2 == 0) { buf[k++] = afl->extras[i].data[j]; } + + } else { + + if (j % 2 == 1) { buf[k++] = afl->extras[i].data[j]; } + + } + + } + + add_extra_nocheck(afl, buf, k); + + } + + qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), + compare_extras_len); + +} + /* Removes duplicates from the loaded extras. This can happen if multiple files are loaded */ @@ -396,9 +520,9 @@ void dedup_extras(afl_state_t *afl) { u32 i, j, orig_cnt = afl->extras_cnt; - for (i = 0; i < afl->extras_cnt - 1; i++) { + for (i = 0; i < afl->extras_cnt - 1; ++i) { - for (j = i + 1; j < afl->extras_cnt; j++) { + for (j = i + 1; j < afl->extras_cnt; ++j) { restart_dedup: @@ -462,30 +586,11 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { } - afl->extras = afl_realloc((void **)&afl->extras, - (afl->extras_cnt + 1) * sizeof(struct extra_data)); - - if (unlikely(!afl->extras)) { PFATAL("alloc"); } - - afl->extras[afl->extras_cnt].data = ck_alloc(len); - afl->extras[afl->extras_cnt].len = len; - - memcpy(afl->extras[afl->extras_cnt].data, mem, len); - - afl->extras_cnt++; + add_extra_nocheck(afl, mem, len); qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), compare_extras_len); - /* We only want to print this once */ - - if (afl->extras_cnt == afl->max_det_extras + 1) { - - WARNF("More than %u tokens - will use them probabilistically.", - afl->max_det_extras); - - } - } /* Maybe add automatic extra. */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index a02eadb2..90f77919 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1449,9 +1449,6 @@ int main(int argc, char **argv_orig, char **envp) { } - dedup_extras(afl); - OKF("Loaded a total of %u extras.", afl->extras_cnt); - } if (!afl->timeout_given) { find_timeout(afl); } // only for resumes! @@ -1681,6 +1678,10 @@ int main(int argc, char **argv_orig, char **envp) { } + deunicode_extras(afl); + dedup_extras(afl); + if (afl->extras_cnt) { OKF("Loaded a total of %u extras.", afl->extras_cnt); } + // after we have the correct bitmap size we can read the bitmap -B option // and set the virgin maps if (afl->in_bitmap) { -- cgit 1.4.1 From f0c7967fbf4d6d3906693896f511b6679573c02b Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 3 Mar 2021 08:58:09 +0100 Subject: add new tutorial --- README.md | 1 + src/afl-fuzz.c | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/README.md b/README.md index 800c2121..992dcd86 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,7 @@ Here are some good writeups to show how to effectively use AFL++: * [https://securitylab.github.com/research/fuzzing-software-2](https://securitylab.github.com/research/fuzzing-software-2) * [https://securitylab.github.com/research/fuzzing-sockets-FTP](https://securitylab.github.com/research/fuzzing-sockets-FTP) * [https://securitylab.github.com/research/fuzzing-sockets-FreeRDP](https://securitylab.github.com/research/fuzzing-sockets-FreeRDP) + * [https://securitylab.github.com/research/fuzzing-apache-1](https://securitylab.github.com/research/fuzzing-apache-1) If you are interested in fuzzing structured data (where you define what the structure is), these links have you covered: diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 90f77919..09aff4fb 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1437,20 +1437,8 @@ int main(int argc, char **argv_orig, char **envp) { // read_foreign_testcases(afl, 1); for the moment dont do this OKF("Loaded a total of %u seeds.", afl->queued_paths); - load_auto(afl); - pivot_inputs(afl); - if (extras_dir_cnt) { - - for (i = 0; i < extras_dir_cnt; i++) { - - load_extras(afl, extras_dir[i]); - - } - - } - if (!afl->timeout_given) { find_timeout(afl); } // only for resumes! if ((afl->tmp_dir = afl->afl_env.afl_tmpdir) != NULL && @@ -1678,6 +1666,18 @@ int main(int argc, char **argv_orig, char **envp) { } + load_auto(afl); + + if (extras_dir_cnt) { + + for (i = 0; i < extras_dir_cnt; i++) { + + load_extras(afl, extras_dir[i]); + + } + + } + deunicode_extras(afl); dedup_extras(afl); if (afl->extras_cnt) { OKF("Loaded a total of %u extras.", afl->extras_cnt); } -- cgit 1.4.1 From 1e76079e93f5b4a9729367dd982d632013669bc5 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 4 Mar 2021 11:32:32 +0100 Subject: llvm mode CALLER mode --- instrumentation/README.ctx.md | 22 +++++++++++++++++++--- instrumentation/afl-llvm-pass.so.cc | 18 ++++++++++-------- src/afl-cc.c | 37 +++++++++++++++++++++++++------------ 3 files changed, 54 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/instrumentation/README.ctx.md b/instrumentation/README.ctx.md index caf2c09a..ffcce0a9 100644 --- a/instrumentation/README.ctx.md +++ b/instrumentation/README.ctx.md @@ -4,14 +4,19 @@ This is an LLVM-based implementation of the context sensitive branch coverage. -Basically every function gets its own ID and that ID is combined with the -edges of the called functions. +Basically every function gets its own ID and, every time that an edge is logged, +all the IDs in the callstack are hashed and combined with the edge transition +hash to augment the classic edge coverage with the information about the +calling context. So if both function A and function B call a function C, the coverage collected in C will be different. In math the coverage is collected as follows: -`map[current_location_ID ^ previous_location_ID >> 1 ^ previous_callee_ID] += 1` +`map[current_location_ID ^ previous_location_ID >> 1 ^ hash_callstack_IDs] += 1` + +The callstack hash is produced XOR-ing the function IDs to avoid explosion with +recusrsive functions. ## Usage @@ -20,3 +25,14 @@ Set the `AFL_LLVM_INSTRUMENT=CTX` or `AFL_LLVM_CTX=1` environment variable. It is highly recommended to increase the MAP_SIZE_POW2 definition in config.h to at least 18 and maybe up to 20 for this as otherwise too many map collisions occur. + +## Caller Branch Coverage + +If the context sensitive coverage introduces too may collisions becoming +decremental, the user can choose to augment edge coverage with just the +called function ID, instead of the entire callstack hash. + +In math the coverage is collected as follows: +`map[current_location_ID ^ previous_location_ID >> 1 ^ previous_callee_ID] += 1` + +Set the `AFL_LLVM_INSTRUMENT=CALLER` or `AFL_LLVM_CALLER=1` environment variable. diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index 87267e35..d06d3201 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -84,7 +84,7 @@ class AFLCoverage : public ModulePass { uint32_t ngram_size = 0; uint32_t map_size = MAP_SIZE; uint32_t function_minimum_size = 1; - char * ctx_str = NULL, *skip_nozero = NULL; + char * ctx_str = NULL, *caller_str = NULL, *skip_nozero = NULL; }; @@ -187,6 +187,7 @@ bool AFLCoverage::runOnModule(Module &M) { char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); ctx_str = getenv("AFL_LLVM_CTX"); + caller_str = getenv("AFL_LLVM_CALLER"); #ifdef AFL_HAVE_VECTOR_INTRINSICS /* Decide previous location vector size (must be a power of two) */ @@ -240,7 +241,7 @@ bool AFLCoverage::runOnModule(Module &M) { GlobalVariable *AFLPrevLoc; GlobalVariable *AFLContext = NULL; - if (ctx_str) + if (ctx_str || caller_str) #if defined(__ANDROID__) || defined(__HAIKU__) AFLContext = new GlobalVariable( M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx"); @@ -318,7 +319,7 @@ bool AFLCoverage::runOnModule(Module &M) { IRBuilder<> IRB(&(*IP)); // Context sensitive coverage - if (ctx_str && &BB == &F.getEntryBlock()) { + if ((ctx_str || caller_str) && &BB == &F.getEntryBlock()) { // load the context ID of the previous function and write to to a local // variable on the stack @@ -354,8 +355,9 @@ bool AFLCoverage::runOnModule(Module &M) { // if yes we store a context ID for this function in the global var if (has_calls) { - Value *NewCtx = IRB.CreateXor( - PrevCtx, ConstantInt::get(Int32Ty, AFL_R(map_size))); + Value *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size)); + if (ctx_str) + NewCtx = IRB.CreateXor(PrevCtx, NewCtx); StoreInst * StoreCtx = IRB.CreateStore(NewCtx, AFLContext); StoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); @@ -412,7 +414,7 @@ bool AFLCoverage::runOnModule(Module &M) { // in CTX mode we have to restore the original context for the caller - // she might be calling other functions which need the correct CTX - if (ctx_str && has_calls) { + if ((ctx_str || caller_str) && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa(Inst) || isa(Inst)) { @@ -459,7 +461,7 @@ bool AFLCoverage::runOnModule(Module &M) { #endif PrevLocTrans = PrevLoc; - if (ctx_str) + if (ctx_str || caller_str) PrevLocTrans = IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty); else @@ -546,7 +548,7 @@ bool AFLCoverage::runOnModule(Module &M) { // in CTX mode we have to restore the original context for the caller - // she might be calling other functions which need the correct CTX. // Currently this is only needed for the Ubuntu clang-6.0 bug - if (ctx_str && has_calls) { + if ((ctx_str || caller_str) && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa(Inst) || isa(Inst)) { diff --git a/src/afl-cc.c b/src/afl-cc.c index c3910e6d..9cf02059 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -73,7 +73,8 @@ enum { INSTRUMENT_GCC = 6, INSTRUMENT_CLANG = 7, INSTRUMENT_OPT_CTX = 8, - INSTRUMENT_OPT_NGRAM = 16 + INSTRUMENT_OPT_NGRAM = 16, + INSTRUMENT_OPT_CALLER = 32, }; @@ -1273,7 +1274,8 @@ int main(int argc, char **argv, char **envp) { } if (getenv("AFL_LLVM_CTX")) instrument_opt_mode |= INSTRUMENT_OPT_CTX; - + if (getenv("AFL_LLVM_CALLER")) instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + if (getenv("AFL_LLVM_NGRAM_SIZE")) { instrument_opt_mode |= INSTRUMENT_OPT_NGRAM; @@ -1387,6 +1389,13 @@ int main(int argc, char **argv, char **envp) { setenv("AFL_LLVM_CTX", "1", 1); } + + if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) { + + instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + setenv("AFL_LLVM_CALLER", "1", 1); + + } if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) { @@ -1420,6 +1429,11 @@ int main(int argc, char **argv, char **envp) { } } + + if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) && + (instrument_opt_mode & INSTRUMENT_OPT_CALLER)) { + FATAL("you cannot set CTX and CALLER together"); + } if (instrument_opt_mode && instrument_mode == INSTRUMENT_DEFAULT && (compiler_mode == LLVM || compiler_mode == UNSET)) { @@ -1770,7 +1784,7 @@ int main(int argc, char **argv, char **envp) { } if (instrument_opt_mode && compiler_mode != LLVM) - FATAL("CTX and NGRAM can only be used in LLVM mode"); + FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode"); if (!instrument_opt_mode) { @@ -1780,15 +1794,14 @@ int main(int argc, char **argv, char **envp) { } else { - if (instrument_opt_mode == INSTRUMENT_OPT_CTX) - - ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]); - else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM) - ptr = alloc_printf("%s + NGRAM-%u", - instrument_mode_string[instrument_mode], ngram_size); - else - ptr = alloc_printf("%s + CTX + NGRAM-%u", - instrument_mode_string[instrument_mode], ngram_size); + char *ptr2 = alloc_printf(" + NGRAM-%u", ngram_size); + ptr = alloc_printf("%s%s%s%s", instrument_mode_string[instrument_mode], + (instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "", + (instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "", + (instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "" + ); + + ck_free(ptr2); } -- cgit 1.4.1 From 8f538e77ed75256466e8b97d43d0c32948cb9931 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 4 Mar 2021 11:33:51 +0100 Subject: code format --- instrumentation/afl-llvm-pass.so.cc | 5 ++--- src/afl-cc.c | 16 +++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index d06d3201..33898aec 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -356,9 +356,8 @@ bool AFLCoverage::runOnModule(Module &M) { if (has_calls) { Value *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size)); - if (ctx_str) - NewCtx = IRB.CreateXor(PrevCtx, NewCtx); - StoreInst * StoreCtx = IRB.CreateStore(NewCtx, AFLContext); + if (ctx_str) NewCtx = IRB.CreateXor(PrevCtx, NewCtx); + StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext); StoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); diff --git a/src/afl-cc.c b/src/afl-cc.c index 9cf02059..0c689286 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1275,7 +1275,7 @@ int main(int argc, char **argv, char **envp) { if (getenv("AFL_LLVM_CTX")) instrument_opt_mode |= INSTRUMENT_OPT_CTX; if (getenv("AFL_LLVM_CALLER")) instrument_opt_mode |= INSTRUMENT_OPT_CALLER; - + if (getenv("AFL_LLVM_NGRAM_SIZE")) { instrument_opt_mode |= INSTRUMENT_OPT_NGRAM; @@ -1389,7 +1389,7 @@ int main(int argc, char **argv, char **envp) { setenv("AFL_LLVM_CTX", "1", 1); } - + if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) { instrument_opt_mode |= INSTRUMENT_OPT_CALLER; @@ -1429,10 +1429,12 @@ int main(int argc, char **argv, char **envp) { } } - + if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) && (instrument_opt_mode & INSTRUMENT_OPT_CALLER)) { + FATAL("you cannot set CTX and CALLER together"); + } if (instrument_opt_mode && instrument_mode == INSTRUMENT_DEFAULT && @@ -1795,12 +1797,12 @@ int main(int argc, char **argv, char **envp) { } else { char *ptr2 = alloc_printf(" + NGRAM-%u", ngram_size); - ptr = alloc_printf("%s%s%s%s", instrument_mode_string[instrument_mode], + ptr = alloc_printf( + "%s%s%s%s", instrument_mode_string[instrument_mode], (instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "", (instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "", - (instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "" - ); - + (instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : ""); + ck_free(ptr2); } -- cgit 1.4.1 From 96c526cb78512737a980726dd32c95593edb8cd1 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Thu, 4 Mar 2021 14:04:40 +0100 Subject: fix caller/ctx change, support dlopen in afl-compiler-rt --- docs/Changelog.md | 10 ++++ instrumentation/LLVMInsTrim.so.cc | 29 ++++----- instrumentation/afl-compiler-rt.o.c | 114 +++++++++++++++++++++++++++++++----- src/afl-cc.c | 31 ++++++---- 4 files changed, 144 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 1be41267..6fe3517a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,16 @@ sending a mail to . - afl-cc - fixed a crash that can occur with ASAN + CMPLOG together plus better support for unicode (thanks to @stbergmann for reporting!) + - handle erroneous setups in which multiple afl-compiler-rt are + compiled into the target. This now also supports dlopen instrumented + libs loaded before the forkserver. + - Renamed CTX to CALLER, added correct/real CTX implemenation to CLASSIC + - qemu_mode + - added AFL_QEMU_EXCLUDE_RANGES env by @realmadsci, thanks! + - if no new/updated checkout is wanted, build with: + NO_CHECKOUT=1 ./build_qemu_support.sh + - we no longer perform a "git drop" + ### Version ++3.10c (release) - Mac OS ARM64 support diff --git a/instrumentation/LLVMInsTrim.so.cc b/instrumentation/LLVMInsTrim.so.cc index 948f8f3a..f0de6536 100644 --- a/instrumentation/LLVMInsTrim.so.cc +++ b/instrumentation/LLVMInsTrim.so.cc @@ -135,7 +135,7 @@ struct InsTrim : public ModulePass { unsigned int PrevLocSize = 0; char * ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); - char *ctx_str = getenv("AFL_LLVM_CTX"); + char *caller_str = getenv("AFL_LLVM_CALLER"); #ifdef AFL_HAVE_VECTOR_INTRINSICS unsigned int ngram_size = 0; @@ -197,9 +197,9 @@ struct InsTrim : public ModulePass { GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); GlobalVariable *AFLPrevLoc; GlobalVariable *AFLContext = NULL; - LoadInst * PrevCtx = NULL; // for CTX sensitive coverage + LoadInst * PrevCaller = NULL; // for CALLER sensitive coverage - if (ctx_str) + if (caller_str) #if defined(__ANDROID__) || defined(__HAIKU__) AFLContext = new GlobalVariable( M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx"); @@ -398,11 +398,11 @@ struct InsTrim : public ModulePass { unsigned int cur_loc; // Context sensitive coverage - if (ctx_str && &BB == &F.getEntryBlock()) { + if (caller_str && &BB == &F.getEntryBlock()) { - PrevCtx = IRB.CreateLoad(AFLContext); - PrevCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); + PrevCaller = IRB.CreateLoad(AFLContext); + PrevCaller->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); // does the function have calls? and is any of the calls larger than // one basic block? @@ -441,7 +441,7 @@ struct InsTrim : public ModulePass { } - } // END of ctx_str + } // END of caller_str if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; } @@ -485,9 +485,9 @@ struct InsTrim : public ModulePass { #endif PrevLocTrans = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty()); - if (ctx_str) + if (caller_str) PrevLocTrans = - IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty); + IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCaller), Int32Ty); /* Load SHM pointer */ LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); @@ -535,16 +535,17 @@ struct InsTrim : public ModulePass { IRB.CreateStore(Incr, MapPtrIdx) ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - if (ctx_str && has_calls) { + if (caller_str && has_calls) { - // in CTX mode we have to restore the original context for the + // in CALLER mode we have to restore the original context for the // caller - she might be calling other functions which need the - // correct CTX + // correct CALLER Instruction *Inst = BB.getTerminator(); if (isa(Inst) || isa(Inst)) { IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + StoreInst * RestoreCtx = + Post_IRB.CreateStore(PrevCaller, AFLContext); RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index c9577a55..e3aa787f 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -123,6 +123,17 @@ static u8 is_persistent; static u8 _is_sancov; +/* Debug? */ + +static u32 __afl_debug; + +/* Already initialized markers */ + +static u32 __afl_already_initialized_shm; +static u32 __afl_already_initialized_forkserver; +static u32 __afl_already_initialized_first; +static u32 __afl_already_initialized_second; + /* Dummy pipe for area_is_valid() */ static int __afl_dummy_fd[2] = {2, 2}; @@ -176,7 +187,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"); @@ -222,7 +233,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"); @@ -242,7 +253,6 @@ static void __afl_map_shm_fuzz() { static void __afl_map_shm(void) { - static u32 __afl_already_initialized_shm = 0; if (__afl_already_initialized_shm) return; __afl_already_initialized_shm = 1; @@ -303,7 +313,7 @@ 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, " @@ -359,17 +369,18 @@ static void __afl_map_shm(void) { } + close(shm_fd); + if (shm_base == MAP_FAILED) { - close(shm_fd); shm_fd = -1; - 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); @@ -476,7 +487,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 ? "" : id_str); @@ -541,6 +552,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) { @@ -569,7 +632,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); @@ -746,7 +809,6 @@ static void __afl_start_snapshots(void) { static void __afl_start_forkserver(void) { - static u32 __afl_already_initialized_forkserver = 0; if (__afl_already_initialized_forkserver) return; __afl_already_initialized_forkserver = 1; @@ -800,7 +862,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); @@ -1035,7 +1097,7 @@ 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"); @@ -1079,10 +1141,11 @@ __attribute__((constructor(CTOR_PRIO))) void __afl_auto_early(void) { __attribute__((constructor(1))) void __afl_auto_second(void) { - static u32 __afl_already_initialized_second = 0; 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; @@ -1114,7 +1177,6 @@ __attribute__((constructor(1))) void __afl_auto_second(void) { __attribute__((constructor(0))) void __afl_auto_first(void) { - static u32 __afl_already_initialized_first = 0; if (__afl_already_initialized_first) return; __afl_already_initialized_first = 1; @@ -1198,7 +1260,7 @@ 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", @@ -1235,6 +1297,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 diff --git a/src/afl-cc.c b/src/afl-cc.c index 0c689286..ab794877 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -89,7 +89,7 @@ char instrument_mode_string[18][18] = { "GCC", "CLANG", "CTX", - "", + "CALLER", "", "", "", @@ -1514,12 +1514,13 @@ int main(int argc, char **argv, char **envp) { " CLASSIC %s no yes module yes yes " "yes\n" " - NORMAL\n" + " - CALLER\n" " - CTX\n" " - NGRAM-{2-16}\n" " INSTRIM no yes module yes yes " " yes\n" " - NORMAL\n" - " - CTX\n" + " - CALLER\n" " - NGRAM-{2-16}\n" " [GCC_PLUGIN] gcc plugin: %s%s\n" " CLASSIC DEFAULT no yes no no no " @@ -1566,7 +1567,10 @@ int main(int argc, char **argv, char **envp) { NATIVE_MSG " CLASSIC: decision target instrumentation (README.llvm.md)\n" - " CTX: CLASSIC + callee context (instrumentation/README.ctx.md)\n" + " CALLER: CLASSIC + single callee context " + "(instrumentation/README.ctx.md)\n" + " CTX: CLASSIC + full callee context " + "(instrumentation/README.ctx.md)\n" " NGRAM-x: CLASSIC + previous path " "((instrumentation/README.ngram.md)\n" " INSTRIM: Dominator tree (for LLVM <= 6.0) " @@ -1660,15 +1664,17 @@ int main(int argc, char **argv, char **envp) { " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen " "mutator)\n" " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" - " CLASSIC, INSTRIM, PCGUARD, LTO, GCC, CLANG, CTX, NGRAM-2 ... " - "NGRAM-16\n" + " CLASSIC, INSTRIM, PCGUARD, LTO, GCC, CLANG, CALLER, CTX, " + "NGRAM-2 ..-16\n" " You can also use the old environment variables instead:\n" " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n" " AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n" " AFL_LLVM_INSTRIM_LOOPHEAD: optimize loop tracing for speed " "(option to INSTRIM)\n" - " AFL_LLVM_CTX: use context sensitive coverage (for CLASSIC and " - "INSTRIM)\n" + " AFL_LLVM_CALLER: use single context sensitive coverage (for " + "CLASSIC)\n" + " AFL_LLVM_CTX: use full context sensitive coverage (for " + "CLASSIC)\n" " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " "CLASSIC & INSTRIM)\n"); @@ -1814,11 +1820,14 @@ int main(int argc, char **argv, char **envp) { "(requires LLVM 11 or higher)"); #endif - if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC && - instrument_mode != INSTRUMENT_CFG) + if (instrument_opt_mode && instrument_mode == INSTRUMENT_CFG && + instrument_opt_mode & INSTRUMENT_OPT_CTX) + FATAL("CFG instrumentation mode supports NGRAM and CALLER, but not CTX."); + else if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC) + // we will drop CFG/INSTRIM in the future so do not advertise FATAL( - "CTX and NGRAM instrumentation options can only be used with LLVM and " - "CFG or CLASSIC instrumentation modes!"); + "CALLER, CTX and NGRAM instrumentation options can only be used with " + "the LLVM CLASSIC instrumentation mode."); if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO")) FATAL( -- cgit 1.4.1 From 0aa93afeb82c610fd39b5dd4a3dd7482b9f86b1e Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 4 Mar 2021 14:50:26 +0100 Subject: vectorial top-k CTX first implementation --- instrumentation/LLVMInsTrim.so.cc | 2 +- instrumentation/afl-compiler-rt.o.c | 4 +- instrumentation/afl-llvm-pass.so.cc | 154 ++++++++++++++++++++++++---- instrumentation/llvm-alternative-coverage.h | 21 ++++ instrumentation/llvm-ngram-coverage.h | 18 ---- src/afl-cc.c | 2 +- 6 files changed, 162 insertions(+), 39 deletions(-) create mode 100644 instrumentation/llvm-alternative-coverage.h delete mode 100644 instrumentation/llvm-ngram-coverage.h (limited to 'src') diff --git a/instrumentation/LLVMInsTrim.so.cc b/instrumentation/LLVMInsTrim.so.cc index 948f8f3a..83f11411 100644 --- a/instrumentation/LLVMInsTrim.so.cc +++ b/instrumentation/LLVMInsTrim.so.cc @@ -38,7 +38,7 @@ typedef long double max_align_t; #include "MarkNodes.h" #include "afl-llvm-common.h" -#include "llvm-ngram-coverage.h" +#include "llvm-alternative-coverage.h" #include "config.h" #include "debug.h" diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index c9577a55..d583e246 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 #include @@ -97,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 diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index 33898aec..c58e9d95 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -62,7 +62,7 @@ typedef long double max_align_t; #endif #include "afl-llvm-common.h" -#include "llvm-ngram-coverage.h" +#include "llvm-alternative-coverage.h" using namespace llvm; @@ -82,6 +82,7 @@ class AFLCoverage : public ModulePass { protected: uint32_t ngram_size = 0; + uint32_t ctx_k = 0; uint32_t map_size = MAP_SIZE; uint32_t function_minimum_size = 1; char * ctx_str = NULL, *caller_str = NULL, *skip_nozero = NULL; @@ -183,12 +184,17 @@ bool AFLCoverage::runOnModule(Module &M) { skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); unsigned PrevLocSize = 0; + unsigned PrevCallerSize = 0; char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); + char *ctx_k_str = getenv("AFL_LLVM_CTX_K"); + if (!ctx_k_str) ctx_k_str = getenv("AFL_CTX_K"); ctx_str = getenv("AFL_LLVM_CTX"); caller_str = getenv("AFL_LLVM_CALLER"); + bool instrument_ctx = ctx_str || caller_str; + #ifdef AFL_HAVE_VECTOR_INTRINSICS /* Decide previous location vector size (must be a power of two) */ VectorType *PrevLocTy = NULL; @@ -205,6 +211,25 @@ bool AFLCoverage::runOnModule(Module &M) { if (ngram_size) PrevLocSize = ngram_size - 1; else + PrevLocSize = 1; + + /* Decide K-ctx vector size (must be a power of two) */ + VectorType *PrevCallerTy = NULL; + + if (ctx_k_str) + if (sscanf(ctx_k_str, "%u", &ctx_k) != 1 || ctx_k < 2 || + ctx_k > CTX_MAX_K) + FATAL("Bad value of AFL_CTX_K (must be between 2 and CTX_MAX_K (%u))", CTX_MAX_K); + + if (ctx_k == 1) { + ctx_k = 0; + instrument_ctx = true; + caller_str = ctx_k_str; // Enable CALLER instead + } + if (ctx_k) { + PrevCallerSize = ctx_k; + instrument_ctx = true; + } #else if (ngram_size_str) #ifndef LLVM_VERSION_PATCH @@ -218,8 +243,20 @@ bool AFLCoverage::runOnModule(Module &M) { "%d.%d.%d!", LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH); #endif + if (ctx_k_str) + #ifndef LLVM_VERSION_PATCH + FATAL( + "Sorry, K-CTX branch coverage is not supported with llvm version " + "%d.%d.%d!", + LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0); + #else + FATAL( + "Sorry, K-CTX branch coverage is not supported with llvm version " + "%d.%d.%d!", + LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH); + #endif + PrevLocSize = 1; #endif - PrevLocSize = 1; #ifdef AFL_HAVE_VECTOR_INTRINSICS int PrevLocVecSize = PowerOf2Ceil(PrevLocSize); @@ -232,6 +269,17 @@ bool AFLCoverage::runOnModule(Module &M) { ); #endif +#ifdef AFL_HAVE_VECTOR_INTRINSICS + int PrevCallerVecSize = PowerOf2Ceil(PrevCallerSize); + if (ctx_k) + PrevCallerTy = VectorType::get(IntLocTy, PrevCallerVecSize + #if LLVM_VERSION_MAJOR >= 12 + , + false + #endif + ); +#endif + /* Get globals for the SHM region and the previous location. Note that __afl_prev_loc is thread-local. */ @@ -239,6 +287,7 @@ bool AFLCoverage::runOnModule(Module &M) { new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); GlobalVariable *AFLPrevLoc; + GlobalVariable *AFLPrevCaller; GlobalVariable *AFLContext = NULL; if (ctx_str || caller_str) @@ -275,6 +324,30 @@ bool AFLCoverage::runOnModule(Module &M) { GlobalVariable::GeneralDynamicTLSModel, 0, false); #endif +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) + #if defined(__ANDROID__) || defined(__HAIKU__) + AFLPrevCaller = new GlobalVariable( + M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage, + /* Initializer */ nullptr, "__afl_prev_caller"); + #else + AFLPrevCaller = new GlobalVariable( + M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage, + /* Initializer */ nullptr, "__afl_prev_caller", + /* InsertBefore */ nullptr, GlobalVariable::GeneralDynamicTLSModel, + /* AddressSpace */ 0, /* IsExternallyInitialized */ false); + #endif + else +#endif +#if defined(__ANDROID__) || defined(__HAIKU__) + AFLPrevCaller = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller"); +#else + AFLPrevCaller = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller", 0, + GlobalVariable::GeneralDynamicTLSModel, 0, false); +#endif + #ifdef AFL_HAVE_VECTOR_INTRINSICS /* Create the vector shuffle mask for updating the previous block history. Note that the first element of the vector will store cur_loc, so just set @@ -289,13 +362,24 @@ bool AFLCoverage::runOnModule(Module &M) { PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize)); Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle); + + SmallVector PrevCallerShuffle = {UndefValue::get(Int32Ty)}; + + for (unsigned I = 0; I < PrevCallerSize - 1; ++I) + PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, I)); + + for (int I = PrevCallerSize; I < PrevCallerVecSize; ++I) + PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, PrevCallerSize)); + + Constant *PrevCallerShuffleMask = ConstantVector::get(PrevCallerShuffle); #endif // other constants we need ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); ConstantInt *One = ConstantInt::get(Int8Ty, 1); - LoadInst *PrevCtx = NULL; // CTX sensitive coverage + Value *PrevCtx = NULL; // CTX sensitive coverage + LoadInst *PrevCaller = NULL; // K-CTX coverage /* Instrument all the things! */ @@ -319,12 +403,21 @@ bool AFLCoverage::runOnModule(Module &M) { IRBuilder<> IRB(&(*IP)); // Context sensitive coverage - if ((ctx_str || caller_str) && &BB == &F.getEntryBlock()) { + if (instrument_ctx && &BB == &F.getEntryBlock()) { - // load the context ID of the previous function and write to to a local - // variable on the stack - PrevCtx = IRB.CreateLoad(AFLContext); - PrevCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) { + PrevCaller = IRB.CreateLoad(AFLPrevCaller); + PrevCaller->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + PrevCtx = IRB.CreateZExt(IRB.CreateXorReduce(PrevCaller), IRB.getInt32Ty()); + } else +#endif + { + // load the context ID of the previous function and write to to a local variable on the stack + LoadInst* PrevCtxLoad = IRB.CreateLoad(AFLContext); + PrevCtxLoad->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + PrevCtx = PrevCtxLoad; + } // does the function have calls? and is any of the calls larger than one // basic block? @@ -356,10 +449,22 @@ bool AFLCoverage::runOnModule(Module &M) { if (has_calls) { Value *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size)); - if (ctx_str) NewCtx = IRB.CreateXor(PrevCtx, NewCtx); - StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext); - StoreCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) { + Value *ShuffledPrevCaller = IRB.CreateShuffleVector( + PrevCaller, UndefValue::get(PrevCallerTy), PrevCallerShuffleMask); + Value *UpdatedPrevCaller = IRB.CreateInsertElement(ShuffledPrevCaller, NewCtx, (uint64_t)0); + + StoreInst * Store = IRB.CreateStore(UpdatedPrevCaller, AFLPrevCaller); + Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + } else +#endif + { + if (ctx_str) NewCtx = IRB.CreateXor(PrevCtx, NewCtx); + StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext); + StoreCtx->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + } } @@ -413,16 +518,22 @@ bool AFLCoverage::runOnModule(Module &M) { // in CTX mode we have to restore the original context for the caller - // she might be calling other functions which need the correct CTX - if ((ctx_str || caller_str) && has_calls) { + if (instrument_ctx && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa(Inst) || isa(Inst)) { IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + + StoreInst * RestoreCtx; +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) + RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller); + else +#endif + RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - } } @@ -460,7 +571,7 @@ bool AFLCoverage::runOnModule(Module &M) { #endif PrevLocTrans = PrevLoc; - if (ctx_str || caller_str) + if (instrument_ctx) PrevLocTrans = IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty); else @@ -547,13 +658,20 @@ bool AFLCoverage::runOnModule(Module &M) { // in CTX mode we have to restore the original context for the caller - // she might be calling other functions which need the correct CTX. // Currently this is only needed for the Ubuntu clang-6.0 bug - if ((ctx_str || caller_str) && has_calls) { + if (instrument_ctx && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa(Inst) || isa(Inst)) { IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + + StoreInst * RestoreCtx; +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) + RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller); + else +#endif + RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); diff --git a/instrumentation/llvm-alternative-coverage.h b/instrumentation/llvm-alternative-coverage.h new file mode 100644 index 00000000..0d7b3957 --- /dev/null +++ b/instrumentation/llvm-alternative-coverage.h @@ -0,0 +1,21 @@ +#ifndef AFL_NGRAM_CONFIG_H +#define AFL_NGRAM_CONFIG_H + +#include "types.h" + +#if (MAP_SIZE_POW2 <= 16) +typedef u16 PREV_LOC_T; +#elif (MAP_SIZE_POW2 <= 32) +typedef u32 PREV_LOC_T; +#else +typedef u64 PREV_LOC_T; +#endif + +/* Maximum ngram size */ +#define NGRAM_SIZE_MAX 16U + +/* Maximum K for top-K context sensitivity */ +#define CTX_MAX_K 32U + +#endif + diff --git a/instrumentation/llvm-ngram-coverage.h b/instrumentation/llvm-ngram-coverage.h deleted file mode 100644 index 666839c8..00000000 --- a/instrumentation/llvm-ngram-coverage.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef AFL_NGRAM_CONFIG_H -#define AFL_NGRAM_CONFIG_H - -#include "types.h" - -#if (MAP_SIZE_POW2 <= 16) -typedef u16 PREV_LOC_T; -#elif (MAP_SIZE_POW2 <= 32) -typedef u32 PREV_LOC_T; -#else -typedef u64 PREV_LOC_T; -#endif - -/* Maximum ngram size */ -#define NGRAM_SIZE_MAX 16U - -#endif - diff --git a/src/afl-cc.c b/src/afl-cc.c index 0c689286..3c96beac 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -22,7 +22,7 @@ #include "types.h" #include "debug.h" #include "alloc-inl.h" -#include "llvm-ngram-coverage.h" +#include "llvm-alternative-coverage.h" #include #include -- cgit 1.4.1 From b6dc529bc38469a69ca5f43e12e9cb921fdc3a08 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Thu, 4 Mar 2021 14:55:57 +0100 Subject: no ASAN odr violations by default --- src/afl-analyze.c | 1 + src/afl-forkserver.c | 1 + src/afl-showmap.c | 1 + src/afl-tmin.c | 1 + 4 files changed, 4 insertions(+) (limited to 'src') diff --git a/src/afl-analyze.c b/src/afl-analyze.c index 20aef2da..d46ecb8d 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -785,6 +785,7 @@ static void set_up_environment(void) { "abort_on_error=1:" "detect_leaks=0:" "allocator_may_return_null=1:" + "detect_odr_violation=0:" "symbolize=0:" "handle_segv=0:" "handle_sigbus=0:" diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index fd5edc98..a129c152 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -492,6 +492,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "malloc_context_size=0:" "symbolize=0:" "allocator_may_return_null=1:" + "detect_odr_violation=0:" "handle_segv=0:" "handle_sigbus=0:" "handle_abort=0:" diff --git a/src/afl-showmap.c b/src/afl-showmap.c index b40527d3..0fc76193 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -563,6 +563,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { "detect_leaks=0:" "allocator_may_return_null=1:" "symbolize=0:" + "detect_odr_violation=0:" "handle_segv=0:" "handle_sigbus=0:" "handle_abort=0:" diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 15336959..6d04c652 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -717,6 +717,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { "detect_leaks=0:" "allocator_may_return_null=1:" "symbolize=0:" + "detect_odr_violation=0:" "handle_segv=0:" "handle_sigbus=0:" "handle_abort=0:" -- cgit 1.4.1 From af9aeb89d43919fb5e538778e5df9bf3ffaba0aa Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 4 Mar 2021 15:26:15 +0100 Subject: afl-cc code for k-ctx --- src/afl-cc.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 3c96beac..97f32b2b 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -50,7 +50,7 @@ static u8 **cc_params; /* Parameters passed to the real CC */ static u32 cc_par_cnt = 1; /* Param count, including argv0 */ static u8 clang_mode; /* Invoked as afl-clang*? */ static u8 llvm_fullpath[PATH_MAX]; -static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode; +static u8 instrument_mode, instrument_opt_mode, ngram_size, ctx_k, lto_mode; static u8 compiler_mode, plusplus_mode, have_instr_env = 0; static u8 have_gcc, have_llvm, have_gcc_plugin, have_lto, have_instr_list = 0; static u8 * lto_flag = AFL_CLANG_FLTO, *argvnull; @@ -75,6 +75,7 @@ enum { INSTRUMENT_OPT_CTX = 8, INSTRUMENT_OPT_NGRAM = 16, INSTRUMENT_OPT_CALLER = 32, + INSTRUMENT_OPT_CTX_K = 64, }; @@ -1282,11 +1283,21 @@ int main(int argc, char **argv, char **envp) { ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE")); if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) FATAL( - "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX " + "K-CTX instrumentation mode must be between 2 and NGRAM_SIZE_MAX " "(%u)", NGRAM_SIZE_MAX); } + + if (getenv("AFL_LLVM_CTX_K")) { + + instrument_opt_mode |= INSTRUMENT_OPT_CTX_K; + ctx_k = atoi(getenv("AFL_LLVM_CTX_K")); + if (ctx_k < 1 || ctx_k > CTX_MAX_K) + FATAL( + "NGRAM instrumentation mode must be between 1 and CTX_MAX_K (%u)", CTX_MAX_K); + + } if (getenv("AFL_LLVM_INSTRUMENT")) { @@ -1382,6 +1393,32 @@ int main(int argc, char **argv, char **envp) { compiler_mode = CLANG; } + + if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0) { + + u8 *ptr3 = ptr2 + strlen("ctx-"); + while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) + ptr3++; + + if (!*ptr3) { + + if ((ptr3 = getenv("AFL_LLVM_CTX_K")) == NULL) + FATAL( + "you must set the K-CTX K with (e.g. for value 2) " + "AFL_LLVM_INSTRUMENT=ctx-2"); + + } + + ctx_k = atoi(ptr3); + if (ctx_k < 1 || ctx_k > CTX_MAX_K) + FATAL( + "K-CTX instrumentation option must be between 1 and CTX_MAX_K (%u)", + CTX_MAX_K); + instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K); + u8 *ptr4 = alloc_printf("%u", ctx_k); + setenv("AFL_LLVM_CTX_K", ptr4, 1); + + } if (strncasecmp(ptr2, "ctx", strlen("ctx")) == 0) { @@ -1437,6 +1474,20 @@ int main(int argc, char **argv, char **envp) { } + if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) && + (instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { + + FATAL("you cannot set CTX and K-CTX together"); + + } + + if ((instrument_opt_mode & INSTRUMENT_OPT_CALLER) && + (instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { + + FATAL("you cannot set CALLER and K-CTX together"); + + } + if (instrument_opt_mode && instrument_mode == INSTRUMENT_DEFAULT && (compiler_mode == LLVM || compiler_mode == UNSET)) { @@ -1797,13 +1848,18 @@ int main(int argc, char **argv, char **envp) { } else { char *ptr2 = alloc_printf(" + NGRAM-%u", ngram_size); + char *ptr3 = alloc_printf(" + K-CTX-%u", ctx_k); + ptr = alloc_printf( - "%s%s%s%s", instrument_mode_string[instrument_mode], + "%s%s%s%s%s", instrument_mode_string[instrument_mode], (instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "", (instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "", - (instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : ""); + (instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "", + (instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : "" + ); ck_free(ptr2); + ck_free(ptr3); } -- cgit 1.4.1 From a2f40aa285faa75e78ac1ffffe8d79e2ac1a40da Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Thu, 4 Mar 2021 22:10:32 +0100 Subject: disable corpus introspection, potentially creates huge data --- src/afl-fuzz-queue.c | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index ad3e3b8e..835aba40 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -198,34 +198,35 @@ void create_alias_table(afl_state_t *afl) { while (nS) afl->alias_probability[S[--nS]] = 1; -#ifdef INTROSPECTION - u8 fn[PATH_MAX]; - snprintf(fn, PATH_MAX, "%s/introspection_corpus.txt", afl->out_dir); - FILE *f = fopen(fn, "a"); - if (f) { + /* + #ifdef INTROSPECTION + u8 fn[PATH_MAX]; + snprintf(fn, PATH_MAX, "%s/introspection_corpus.txt", afl->out_dir); + FILE *f = fopen(fn, "a"); + if (f) { + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + fprintf( + f, + "entry=%u name=%s favored=%s variable=%s disabled=%s len=%u " + "exec_us=%u " + "bitmap_size=%u bitsmap_size=%u tops=%u weight=%f perf_score=%f\n", + i, q->fname, q->favored ? "true" : "false", + q->var_behavior ? "true" : "false", q->disabled ? "true" : "false", + q->len, (u32)q->exec_us, q->bitmap_size, q->bitsmap_size, q->tc_ref, + q->weight, q->perf_score); - for (i = 0; i < n; i++) { + } - struct queue_entry *q = afl->queue_buf[i]; - fprintf( - f, - "entry=%u name=%s favored=%s variable=%s disabled=%s len=%u " - "exec_us=%u " - "bitmap_size=%u bitsmap_size=%u tops=%u weight=%f perf_score=%f\n", - i, q->fname, q->favored ? "true" : "false", - q->var_behavior ? "true" : "false", q->disabled ? "true" : "false", - q->len, (u32)q->exec_us, q->bitmap_size, q->bitsmap_size, q->tc_ref, - q->weight, q->perf_score); + fprintf(f, "\n"); + fclose(f); } - fprintf(f, "\n"); - fclose(f); - - } - -#endif - + #endif + */ /* fprintf(stderr, " entry alias probability perf_score weight filename\n"); for (u32 i = 0; i < n; ++i) fprintf(stderr, " %5u %5u %11u -- cgit 1.4.1 From f848562732337c2d3c71ace4667b3130574f0fe4 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Fri, 5 Mar 2021 10:15:38 +0100 Subject: point explicitly to AFL_MAP_SIZE on problems --- src/afl-forkserver.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index a129c152..6f08f9f4 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -909,10 +909,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, } else if (!fsrv->mem_limit) { SAYF("\n" cLRD "[-] " cRST - "Hmm, looks like the target binary terminated before we could" - " complete a handshake with the injected code.\n" - "If the target was compiled with afl-clang-lto and AFL_LLVM_MAP_ADDR" - " then recompiling without this parameter.\n" + "Hmm, looks like the target binary terminated before we could complete" + " a\n" + "handshake with the injected code.\n" + "Most likely the target has a huge coverage map, retry with setting" + " the\n" + "environment variable AFL_MAP_SIZE=4194304\n" "Otherwise there is a horrible bug in the fuzzer.\n" "Poke for troubleshooting tips.\n"); @@ -928,6 +930,10 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "explanations:\n\n" "%s" + + " - Most likely the target has a huge coverage map, retry with setting the\n" + " environment variable AFL_MAP_SIZE=4194304\n\n" + " - The current memory limit (%s) is too restrictive, causing an " "OOM\n" " fault in the dynamic linker. This can be fixed with the -m " -- cgit 1.4.1 From c429021de10cf01878bd2150cc68c6b403db9335 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 5 Mar 2021 15:27:10 +0100 Subject: fix typos and format --- instrumentation/afl-llvm-pass.so.cc | 99 ++++++++++++++++++++++++------------- src/afl-cc.c | 16 +++--- 2 files changed, 72 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index fbf55f81..f4717345 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -212,24 +212,30 @@ bool AFLCoverage::runOnModule(Module &M) { PrevLocSize = ngram_size - 1; else PrevLocSize = 1; - + /* Decide K-ctx vector size (must be a power of two) */ VectorType *PrevCallerTy = NULL; if (ctx_k_str) - if (sscanf(ctx_k_str, "%u", &ctx_k) != 1 || ctx_k < 2 || - ctx_k > CTX_MAX_K) - FATAL("Bad value of AFL_CTX_K (must be between 2 and CTX_MAX_K (%u))", CTX_MAX_K); + if (sscanf(ctx_k_str, "%u", &ctx_k) != 1 || ctx_k < 2 || ctx_k > CTX_MAX_K) + FATAL("Bad value of AFL_CTX_K (must be between 2 and CTX_MAX_K (%u))", + CTX_MAX_K); if (ctx_k == 1) { + ctx_k = 0; instrument_ctx = true; - caller_str = ctx_k_str; // Enable CALLER instead + caller_str = ctx_k_str; // Enable CALLER instead + } + if (ctx_k) { + PrevCallerSize = ctx_k; instrument_ctx = true; + } + #else if (ngram_size_str) #ifndef LLVM_VERSION_PATCH @@ -274,8 +280,8 @@ bool AFLCoverage::runOnModule(Module &M) { if (ctx_k) PrevCallerTy = VectorType::get(IntLocTy, PrevCallerVecSize #if LLVM_VERSION_MAJOR >= 12 - , - false + , + false #endif ); #endif @@ -340,12 +346,13 @@ bool AFLCoverage::runOnModule(Module &M) { else #endif #if defined(__ANDROID__) || defined(__HAIKU__) - AFLPrevCaller = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller"); + AFLPrevCaller = + new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, + "__afl_prev_caller"); #else AFLPrevCaller = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller", 0, - GlobalVariable::GeneralDynamicTLSModel, 0, false); + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller", + 0, GlobalVariable::GeneralDynamicTLSModel, 0, false); #endif #ifdef AFL_HAVE_VECTOR_INTRINSICS @@ -362,11 +369,12 @@ bool AFLCoverage::runOnModule(Module &M) { PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize)); Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle); - - Constant *PrevCallerShuffleMask = NULL; + + Constant * PrevCallerShuffleMask = NULL; SmallVector PrevCallerShuffle = {UndefValue::get(Int32Ty)}; if (ctx_k) { + for (unsigned I = 0; I < PrevCallerSize - 1; ++I) PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, I)); @@ -374,15 +382,17 @@ bool AFLCoverage::runOnModule(Module &M) { PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, PrevCallerSize)); PrevCallerShuffleMask = ConstantVector::get(PrevCallerShuffle); + } + #endif // other constants we need ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); ConstantInt *One = ConstantInt::get(Int8Ty, 1); - Value *PrevCtx = NULL; // CTX sensitive coverage - LoadInst *PrevCaller = NULL; // K-CTX coverage + Value * PrevCtx = NULL; // CTX sensitive coverage + LoadInst *PrevCaller = NULL; // K-CTX coverage /* Instrument all the things! */ @@ -410,16 +420,25 @@ bool AFLCoverage::runOnModule(Module &M) { #ifdef AFL_HAVE_VECTOR_INTRINSICS if (ctx_k) { + PrevCaller = IRB.CreateLoad(AFLPrevCaller); - PrevCaller->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - PrevCtx = IRB.CreateZExt(IRB.CreateXorReduce(PrevCaller), IRB.getInt32Ty()); - } else + PrevCaller->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + PrevCtx = + IRB.CreateZExt(IRB.CreateXorReduce(PrevCaller), IRB.getInt32Ty()); + + } else + #endif { - // load the context ID of the previous function and write to to a local variable on the stack - LoadInst* PrevCtxLoad = IRB.CreateLoad(AFLContext); - PrevCtxLoad->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + // load the context ID of the previous function and write to to a + // local variable on the stack + LoadInst *PrevCtxLoad = IRB.CreateLoad(AFLContext); + PrevCtxLoad->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); PrevCtx = PrevCtxLoad; + } // does the function have calls? and is any of the calls larger than one @@ -454,19 +473,28 @@ bool AFLCoverage::runOnModule(Module &M) { Value *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size)); #ifdef AFL_HAVE_VECTOR_INTRINSICS if (ctx_k) { + Value *ShuffledPrevCaller = IRB.CreateShuffleVector( - PrevCaller, UndefValue::get(PrevCallerTy), PrevCallerShuffleMask); - Value *UpdatedPrevCaller = IRB.CreateInsertElement(ShuffledPrevCaller, NewCtx, (uint64_t)0); + PrevCaller, UndefValue::get(PrevCallerTy), + PrevCallerShuffleMask); + Value *UpdatedPrevCaller = IRB.CreateInsertElement( + ShuffledPrevCaller, NewCtx, (uint64_t)0); + + StoreInst *Store = + IRB.CreateStore(UpdatedPrevCaller, AFLPrevCaller); + Store->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } else - StoreInst * Store = IRB.CreateStore(UpdatedPrevCaller, AFLPrevCaller); - Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - } else #endif { + if (ctx_str) NewCtx = IRB.CreateXor(PrevCtx, NewCtx); StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext); StoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + } } @@ -528,15 +556,16 @@ bool AFLCoverage::runOnModule(Module &M) { IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx; -#ifdef AFL_HAVE_VECTOR_INTRINSICS + StoreInst *RestoreCtx; + #ifdef AFL_HAVE_VECTOR_INTRINSICS if (ctx_k) RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller); - else -#endif - RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + else + #endif + RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + } } @@ -667,14 +696,14 @@ bool AFLCoverage::runOnModule(Module &M) { if (isa(Inst) || isa(Inst)) { IRBuilder<> Post_IRB(Inst); - - StoreInst * RestoreCtx; + + StoreInst *RestoreCtx; #ifdef AFL_HAVE_VECTOR_INTRINSICS if (ctx_k) RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller); - else + else #endif - RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); diff --git a/src/afl-cc.c b/src/afl-cc.c index 97f32b2b..e4ea66e4 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1283,19 +1283,19 @@ int main(int argc, char **argv, char **envp) { ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE")); if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) FATAL( - "K-CTX instrumentation mode must be between 2 and NGRAM_SIZE_MAX " + "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX " "(%u)", NGRAM_SIZE_MAX); } - + if (getenv("AFL_LLVM_CTX_K")) { instrument_opt_mode |= INSTRUMENT_OPT_CTX_K; ctx_k = atoi(getenv("AFL_LLVM_CTX_K")); if (ctx_k < 1 || ctx_k > CTX_MAX_K) - FATAL( - "NGRAM instrumentation mode must be between 1 and CTX_MAX_K (%u)", CTX_MAX_K); + FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)", + CTX_MAX_K); } @@ -1393,7 +1393,7 @@ int main(int argc, char **argv, char **envp) { compiler_mode = CLANG; } - + if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0) { u8 *ptr3 = ptr2 + strlen("ctx-"); @@ -1412,7 +1412,8 @@ int main(int argc, char **argv, char **envp) { ctx_k = atoi(ptr3); if (ctx_k < 1 || ctx_k > CTX_MAX_K) FATAL( - "K-CTX instrumentation option must be between 1 and CTX_MAX_K (%u)", + "K-CTX instrumentation option must be between 1 and CTX_MAX_K " + "(%u)", CTX_MAX_K); instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K); u8 *ptr4 = alloc_printf("%u", ctx_k); @@ -1855,8 +1856,7 @@ int main(int argc, char **argv, char **envp) { (instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "", (instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "", (instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "", - (instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : "" - ); + (instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : ""); ck_free(ptr2); ck_free(ptr3); -- cgit 1.4.1 From 74a6044b3fba496c1255f9aedbf5b7253ae29f0e Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 9 Mar 2021 14:11:52 +0100 Subject: fix sanitizer settings --- docs/Changelog.md | 1 + src/afl-forkserver.c | 17 +++++++++-------- src/afl-fuzz-init.c | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index ab0e2da2..b47b03ba 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,6 +10,7 @@ sending a mail to . ### Version ++3.11a (dev) - afl-fuzz: + - fix sanitizer settings (bug since 3.10c) - add non-unicode variants from unicode-looking dictionary entries - Rust custom mutator API improvements - afl-cc: diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 6f08f9f4..82ec3069 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -481,11 +481,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* This should improve performance a bit, since it stops the linker from doing extra work post-fork(). */ - if (!getenv("LD_BIND_LAZY")) { setenv("LD_BIND_NOW", "1", 0); } + if (!getenv("LD_BIND_LAZY")) { setenv("LD_BIND_NOW", "1", 1); } /* Set sane defaults for ASAN if nothing else specified. */ - if (fsrv->debug == true && !getenv("ASAN_OPTIONS")) + if (!getenv("ASAN_OPTIONS")) setenv("ASAN_OPTIONS", "abort_on_error=1:" "detect_leaks=0:" @@ -498,11 +498,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "handle_abort=0:" "handle_sigfpe=0:" "handle_sigill=0", - 0); + 1); /* Set sane defaults for UBSAN if nothing else specified. */ - if (fsrv->debug == true && !getenv("UBSAN_OPTIONS")) + if (!getenv("UBSAN_OPTIONS")) setenv("UBSAN_OPTIONS", "halt_on_error=1:" "abort_on_error=1:" @@ -514,7 +514,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "handle_abort=0:" "handle_sigfpe=0:" "handle_sigill=0", - 0); + 1); /* Envs for QASan */ setenv("QASAN_MAX_CALL_STACK", "0", 0); @@ -523,7 +523,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* MSAN is tricky, because it doesn't support abort_on_error=1 at this point. So, we do this in a very hacky way. */ - if (fsrv->debug == true && !getenv("MSAN_OPTIONS")) + if (!getenv("MSAN_OPTIONS")) setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" "symbolize=0:" @@ -536,7 +536,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "handle_abort=0:" "handle_sigfpe=0:" "handle_sigill=0", - 0); + 1); fsrv->init_child_func(fsrv, argv); @@ -931,7 +931,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "%s" - " - Most likely the target has a huge coverage map, retry with setting the\n" + " - Most likely the target has a huge coverage map, retry with " + "setting the\n" " environment variable AFL_MAP_SIZE=4194304\n\n" " - The current memory limit (%s) is too restrictive, causing an " diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 3dbc4c65..2d5f32a7 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2457,7 +2457,7 @@ void check_asan_opts(afl_state_t *afl) { } - if (!strstr(x, "symbolize=0")) { + if (!afl->debug && !strstr(x, "symbolize=0")) { FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); -- cgit 1.4.1 From 47f2650a32470172a32c4ebd446003cb8a4b80e8 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 9 Mar 2021 16:53:56 +0100 Subject: add AFL_NOOPT --- README.md | 17 +++++++++++++---- docs/env_variables.md | 11 +++++++++++ include/envs.h | 4 +++- src/afl-cc.c | 21 +++++++++++++++++++-- src/afl-common.c | 1 + 5 files changed, 47 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/README.md b/README.md index 6b11fee4..d4e39566 100644 --- a/README.md +++ b/README.md @@ -399,10 +399,19 @@ How to do this is described below. Then build the target. (Usually with `make`) -**NOTE**: sometimes configure and build systems are fickle and do not like -stderr output (and think this means a test failure) - which is something -afl++ like to do to show statistics. It is recommended to disable them via -`export AFL_QUIET=1`. +**NOTES** + +1. sometimes configure and build systems are fickle and do not like + stderr output (and think this means a test failure) - which is something + afl++ like to do to show statistics. It is recommended to disable them via + `export AFL_QUIET=1`. + +2. sometimes configure and build systems error on warnings - these should be + disabled (e.g. `--disable-werror` for some configure scripts` + +3. in case the configure/build system complains about afl++'s compiler and + aborts then set `export AFL_NOOPT=1` which will then just behave like the + real compiler. This option has to be unset again before building the target! ##### configure diff --git a/docs/env_variables.md b/docs/env_variables.md index f6ed12d0..a20f1e42 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -26,6 +26,17 @@ Because (with the exception of the --afl-MODE command line option) the compile-time tools do not accept afl specific command-line options, they make fairly broad use of environmental variables instead: + - Some build/configure scripts break with afl++ compilers. To be able to + pass them, do: +``` + export CC=afl-cc + export CXX=afl-c++ + export AFL_NOOPT=1 + ./configure --disable-shared --disabler-werror + unset AFL_NOOPT + make +``` + - Most afl tools do not print any output if stdout/stderr are redirected. If you want to get the output into a file then set the `AFL_DEBUG` environment variable. diff --git a/include/envs.h b/include/envs.h index e8595ef7..37748a56 100644 --- a/include/envs.h +++ b/include/envs.h @@ -119,10 +119,12 @@ static char *afl_environment_variables[] = { "AFL_NO_PYTHON", "AFL_UNTRACER_FILE", "AFL_LLVM_USE_TRACE_PC", - "AFL_NO_X86", // not really an env but we dont want to warn on it "AFL_MAP_SIZE", "AFL_MAPSIZE", "AFL_MAX_DET_EXTRAS", + "AFL_NO_X86", // not really an env but we dont want to warn on it + "AFL_NOOPT", + "AFL_PASSTHROUGH", "AFL_PATH", "AFL_PERFORMANCE_FILE", "AFL_PRELOAD", diff --git a/src/afl-cc.c b/src/afl-cc.c index bb136fb9..8f9bb397 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1025,7 +1025,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { int main(int argc, char **argv, char **envp) { - int i; + int i, passthrough = 0; char *callname = argv[0], *ptr = NULL; if (getenv("AFL_DEBUG")) { @@ -1045,6 +1045,13 @@ int main(int argc, char **argv, char **envp) { } + if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) { + + passthrough = 1; + if (!debug) { be_quiet = 1; } + + } + if ((ptr = strrchr(callname, '/')) != NULL) callname = ptr + 1; argvnull = (u8 *)argv[0]; check_environment_vars(envp); @@ -1665,6 +1672,7 @@ int main(int argc, char **argv, char **envp) { " AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" " AFL_NO_BUILTIN: no builtins for string compare functions (for " "libtokencap.so)\n" + " AFL_NOOP: behave lik a normal compiler (to pass configure tests)\n" " AFL_PATH: path to instrumenting pass and runtime " "(afl-compiler-rt.*o)\n" " AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" @@ -1977,7 +1985,16 @@ int main(int argc, char **argv, char **envp) { } - execvp(cc_params[0], (char **)cc_params); + if (passthrough) { + + argv[0] = cc_params[0]; + execvp(cc_params[0], (char **)argv); + + } else { + + execvp(cc_params[0], (char **)cc_params); + + } FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]); diff --git a/src/afl-common.c b/src/afl-common.c index a306fe5e..68f82a5e 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -682,6 +682,7 @@ void check_environment_vars(char **envp) { env[strlen(afl_environment_variables[i])] == '=') { match = 1; + if ((val = getenv(afl_environment_variables[i])) && !*val) { WARNF( -- cgit 1.4.1 From 0868ea8348ebc682c370d763324b49536ee4c1f7 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 9 Mar 2021 17:15:19 +0100 Subject: fix compiler rt on -c --- docs/Changelog.md | 8 +++++++- src/afl-cc.c | 12 ++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index b47b03ba..c475911d 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,9 @@ sending a mail to . - add non-unicode variants from unicode-looking dictionary entries - Rust custom mutator API improvements - afl-cc: + - added AFL_NOOPT that will just pass everything to the normal + gcc/clang compiler without any changes - to pass weird configure + scripts - fixed a crash that can occur with ASAN + CMPLOG together plus better support for unicode (thanks to @stbergmann for reporting!) - fixed a crash in LAF transform for empty strings @@ -21,7 +24,10 @@ sending a mail to . compiled into the target. This now also supports dlopen() instrumented libs loaded before the forkserver and even after the forkserver is started (then with collisions though) - - Renamed CTX to CALLER, added correct/real CTX implementation to CLASSIC + - the compiler rt was added also in object building (-c) which + should have been fixed years ago but somewhere got lost :( + - Renamed CTX to CALLER, added correct/real CTX implementation to + CLASSIC - qemu_mode: - added AFL_QEMU_EXCLUDE_RANGES env by @realmadsci, thanks! - if no new/updated checkout is wanted, build with: diff --git a/src/afl-cc.c b/src/afl-cc.c index 8f9bb397..a517124f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -940,7 +940,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (preprocessor_only) { + // prevent unnecessary build errors + cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; + + if (preprocessor_only || have_c) { /* In the preprocessor_only case (-E), we are not actually compiling at all but requesting the compiler to output preprocessed sources only. @@ -1001,18 +1004,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if !defined(__APPLE__) && !defined(__sun) - if (!shared_linking && !have_c) + if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif #if defined(USEMMAP) && !defined(__HAIKU__) - if (!have_c) cc_params[cc_par_cnt++] = "-lrt"; + cc_params[cc_par_cnt++] = "-lrt"; #endif - // prevent unnecessary build errors - cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; - } #endif -- cgit 1.4.1 From 791c5c171d9e4e7391a9c3760a4a8eb4ce2b4058 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 9 Mar 2021 18:44:42 +0100 Subject: fix ctx-1 --- include/envs.h | 1 + instrumentation/afl-llvm-pass.so.cc | 4 ++-- src/afl-cc.c | 29 +++++++++++++++++++++++++---- 3 files changed, 28 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/include/envs.h b/include/envs.h index 37748a56..4d4d6b0e 100644 --- a/include/envs.h +++ b/include/envs.h @@ -80,6 +80,7 @@ static char *afl_environment_variables[] = { "AFL_LLVM_BLOCKLIST", "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM", + "AFL_LLVM_CALLER", "AFL_LLVM_CTX", "AFL_LLVM_CTX_K", "AFL_LLVM_DICT2FILE", diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index f4717345..0f773aba 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -217,8 +217,8 @@ bool AFLCoverage::runOnModule(Module &M) { VectorType *PrevCallerTy = NULL; if (ctx_k_str) - if (sscanf(ctx_k_str, "%u", &ctx_k) != 1 || ctx_k < 2 || ctx_k > CTX_MAX_K) - FATAL("Bad value of AFL_CTX_K (must be between 2 and CTX_MAX_K (%u))", + if (sscanf(ctx_k_str, "%u", &ctx_k) != 1 || ctx_k < 1 || ctx_k > CTX_MAX_K) + FATAL("Bad value of AFL_CTX_K (must be between 1 and CTX_MAX_K (%u))", CTX_MAX_K); if (ctx_k == 1) { diff --git a/src/afl-cc.c b/src/afl-cc.c index a517124f..b9e0c101 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1298,11 +1298,21 @@ int main(int argc, char **argv, char **envp) { if (getenv("AFL_LLVM_CTX_K")) { - instrument_opt_mode |= INSTRUMENT_OPT_CTX_K; ctx_k = atoi(getenv("AFL_LLVM_CTX_K")); if (ctx_k < 1 || ctx_k > CTX_MAX_K) FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)", CTX_MAX_K); + if (ctx_k == 1) { + + setenv("AFL_LLVM_CALLER", "1", 1); + unsetenv("AFL_LLVM_CTX_K"); + instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + + } else { + + instrument_opt_mode |= INSTRUMENT_OPT_CTX_K; + + } } @@ -1422,9 +1432,20 @@ int main(int argc, char **argv, char **envp) { "K-CTX instrumentation option must be between 1 and CTX_MAX_K " "(%u)", CTX_MAX_K); - instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K); - u8 *ptr4 = alloc_printf("%u", ctx_k); - setenv("AFL_LLVM_CTX_K", ptr4, 1); + + if (ctx_k == 1) { + + instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + setenv("AFL_LLVM_CALLER", "1", 1); + unsetenv("AFL_LLVM_CTX_K"); + + } else { + + instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K); + u8 *ptr4 = alloc_printf("%u", ctx_k); + setenv("AFL_LLVM_CTX_K", ptr4, 1); + + } } -- cgit 1.4.1 From 5fab0fa51f14d8770b48501e93cde38d97175e4e Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Tue, 9 Mar 2021 18:31:15 +0000 Subject: setting general schedule priority for performance on Mac. --- src/afl-fuzz.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 09aff4fb..9c822d43 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1403,6 +1403,14 @@ int main(int argc, char **argv_orig, char **envp) { set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY); #endif + #ifdef __APPLE__ + if (pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0) != 0) { + + WARNF("general thread priority settings failed"); + + } + #endif + init_count_class16(); if (afl->is_main_node && check_main_node_exists(afl) == 1) { -- cgit 1.4.1 From f21a5c42c150fa48099144b937d5ec48fdc7048b Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 10 Mar 2021 00:55:26 +0100 Subject: tiny typo fixed --- src/afl-cc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index b9e0c101..49de08e7 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1693,7 +1693,7 @@ int main(int argc, char **argv, char **envp) { " AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" " AFL_NO_BUILTIN: no builtins for string compare functions (for " "libtokencap.so)\n" - " AFL_NOOP: behave lik a normal compiler (to pass configure tests)\n" + " AFL_NOOP: behave like a normal compiler (to pass configure tests)\n" " AFL_PATH: path to instrumenting pass and runtime " "(afl-compiler-rt.*o)\n" " AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" -- cgit 1.4.1 From 851231c846ab4c9fe121f78a5677fa8820e843f3 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 10 Mar 2021 01:15:38 +0100 Subject: fixed scan-build warnings --- src/afl-cc.c | 3 ++- src/afl-fuzz-init.c | 13 +++++++------ src/afl-fuzz.c | 1 + 3 files changed, 10 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 49de08e7..44654de0 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1693,7 +1693,8 @@ int main(int argc, char **argv, char **envp) { " AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" " AFL_NO_BUILTIN: no builtins for string compare functions (for " "libtokencap.so)\n" - " AFL_NOOP: behave like a normal compiler (to pass configure tests)\n" + " AFL_NOOP: behave like a normal compiler (to pass configure " + "tests)\n" " AFL_PATH: path to instrumenting pass and runtime " "(afl-compiler-rt.*o)\n" " AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 2d5f32a7..ca2f75f1 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -828,7 +828,7 @@ void perform_dry_run(afl_state_t *afl) { for (idx = 0; idx < afl->queued_paths; idx++) { q = afl->queue_buf[idx]; - if (unlikely(q->disabled)) { continue; } + if (unlikely(!q || q->disabled)) { continue; } u8 res; s32 fd; @@ -1069,7 +1069,7 @@ void perform_dry_run(afl_state_t *afl) { } afl->max_depth = 0; - for (i = 0; i < afl->queued_paths; i++) { + for (i = 0; i < afl->queued_paths && likely(afl->queue_buf[i]); i++) { if (!afl->queue_buf[i]->disabled && afl->queue_buf[i]->depth > afl->max_depth) @@ -1136,10 +1136,11 @@ void perform_dry_run(afl_state_t *afl) { for (idx = 0; idx < afl->queued_paths; idx++) { q = afl->queue_buf[idx]; - if (q->disabled || q->cal_failed || !q->exec_cksum) { continue; } + if (!q || q->disabled || q->cal_failed || !q->exec_cksum) { continue; } u32 done = 0; - for (i = idx + 1; i < afl->queued_paths && !done; i++) { + for (i = idx + 1; + i < afl->queued_paths && !done && likely(afl->queue_buf[i]); i++) { struct queue_entry *p = afl->queue_buf[i]; if (p->disabled || p->cal_failed || !p->exec_cksum) { continue; } @@ -1191,7 +1192,7 @@ void perform_dry_run(afl_state_t *afl) { for (idx = 0; idx < afl->queued_paths; idx++) { - if (!afl->queue_buf[idx]->disabled && + if (afl->queue_buf[idx] && !afl->queue_buf[idx]->disabled && afl->queue_buf[idx]->depth > afl->max_depth) afl->max_depth = afl->queue_buf[idx]->depth; @@ -1247,7 +1248,7 @@ void pivot_inputs(afl_state_t *afl) { ACTF("Creating hard links for all input files..."); - for (i = 0; i < afl->queued_paths; i++) { + for (i = 0; i < afl->queued_paths && likely(afl->queue_buf[i]); i++) { q = afl->queue_buf[i]; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 9c822d43..065010fa 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1409,6 +1409,7 @@ int main(int argc, char **argv_orig, char **envp) { WARNF("general thread priority settings failed"); } + #endif init_count_class16(); -- cgit 1.4.1 From a0c30116733dd08e8d74a879c0e99be140b7eebb Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 10 Mar 2021 11:08:03 +0100 Subject: change map_size tests --- instrumentation/afl-llvm-common.cc | 6 ++++-- src/afl-forkserver.c | 8 ++++---- src/afl-fuzz.c | 30 +++++++++++++++++++++++++----- 3 files changed, 33 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc index aa54f4f7..0fd3a011 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -62,13 +62,15 @@ bool isIgnoreFunction(const llvm::Function *F) { "sancov.", "__ubsan_", "ign.", - "__afl_", + "__afl", "_fini", - "__libc_csu", + "__libc_", "__asan", "__msan", "__cmplog", "__sancov", + "__cxx_", + "_GLOBAL", "msan.", "LLVMFuzzerM", "LLVMFuzzerC", diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 82ec3069..68995388 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -821,7 +821,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, " - The target binary requires a large map and crashes before " "reporting.\n" - " Set a high value (e.g. AFL_MAP_SIZE=1024000) or use " + " Set a high value (e.g. AFL_MAP_SIZE=8000000) or use " "AFL_DEBUG=1 to see the\n" " message from the target binary\n\n" @@ -848,7 +848,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, " - The target binary requires a large map and crashes before " "reporting.\n" - " Set a high value (e.g. AFL_MAP_SIZE=1024000) or use " + " Set a high value (e.g. AFL_MAP_SIZE=8000000) or use " "AFL_DEBUG=1 to see the\n" " message from the target binary\n\n" @@ -914,7 +914,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "handshake with the injected code.\n" "Most likely the target has a huge coverage map, retry with setting" " the\n" - "environment variable AFL_MAP_SIZE=4194304\n" + "environment variable AFL_MAP_SIZE=8000000\n" "Otherwise there is a horrible bug in the fuzzer.\n" "Poke for troubleshooting tips.\n"); @@ -933,7 +933,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, " - Most likely the target has a huge coverage map, retry with " "setting the\n" - " environment variable AFL_MAP_SIZE=4194304\n\n" + " environment variable AFL_MAP_SIZE=8000000\n\n" " - The current memory limit (%s) is too restrictive, causing an " "OOM\n" diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 065010fa..8364c1c2 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1569,13 +1569,21 @@ int main(int argc, char **argv_orig, char **envp) { if (!afl->non_instrumented_mode && !afl->fsrv.qemu_mode && !afl->unicorn_mode) { - afl->fsrv.map_size = 4194304; // dummy temporary value - setenv("AFL_MAP_SIZE", "4194304", 1); + u32 set_env = 0; + if (!getenv("AFL_MAP_SIZE")) { + + afl->fsrv.map_size = 8000000; // dummy temporary value + setenv("AFL_MAP_SIZE", "8000000", 1); + set_env = 1; + + } + + u32 prev_map_size = afl->fsrv.map_size; u32 new_map_size = afl_fsrv_get_mapsize( &afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); - if (new_map_size && new_map_size != 4194304) { + if (new_map_size && new_map_size != prev_map_size) { // only reinitialize when it makes sense if (map_size < new_map_size || @@ -1607,6 +1615,7 @@ int main(int argc, char **argv_orig, char **envp) { } map_size = new_map_size; + if (set_env) { unsetenv("AFL_MAP_SIZE"); } } @@ -1624,13 +1633,22 @@ int main(int argc, char **argv_orig, char **envp) { afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; afl->cmplog_fsrv.init_child_func = cmplog_exec_child; - afl->cmplog_fsrv.map_size = 4194304; + u32 set_env = 0; + if (!getenv("AFL_MAP_SIZE")) { + + afl->fsrv.map_size = 8000000; // dummy temporary value + setenv("AFL_MAP_SIZE", "8000000", 1); + set_env = 1; + + } + + u32 prev_map_size = afl->fsrv.map_size; u32 new_map_size = afl_fsrv_get_mapsize(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); - if (new_map_size && new_map_size != 4194304) { + if (new_map_size && new_map_size != prev_map_size) { // only reinitialize when it needs to be larger if (map_size < new_map_size) { @@ -1667,6 +1685,8 @@ int main(int argc, char **argv_orig, char **envp) { } + if (set_env) { unsetenv("AFL_MAP_SIZE"); } + } afl->cmplog_fsrv.map_size = map_size; -- cgit 1.4.1 From a10a627622fe9785301fcb033cd1eee69d3f3056 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 10 Mar 2021 19:22:57 +0100 Subject: fix auto map discovery --- src/afl-common.c | 4 +- src/afl-fuzz.c | 181 ++++++++++++++++++++++++++----------------------------- 2 files changed, 86 insertions(+), 99 deletions(-) (limited to 'src') diff --git a/src/afl-common.c b/src/afl-common.c index 68f82a5e..9f6eb564 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -1123,7 +1123,7 @@ u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms) { /* Reads the map size from ENV */ u32 get_map_size(void) { - uint32_t map_size = (MAP_SIZE << 2); // needed for target ctors :( + uint32_t map_size = 8000000; // a very large default map char * ptr; if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) { @@ -1131,7 +1131,7 @@ u32 get_map_size(void) { map_size = atoi(ptr); if (!map_size || map_size > (1 << 29)) { - FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 32U, + FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 64U, 1U << 29); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 8364c1c2..cd049fd5 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -351,7 +351,7 @@ int main(int argc, char **argv_orig, char **envp) { exit_1 = !!afl->afl_env.afl_bench_just_one; SAYF(cCYA "afl-fuzz" VERSION cRST - " based on afl by Michal Zalewski and a big online community\n"); + " based on afl by Michal Zalewski and a large online community\n"); doc_path = access(DOC_PATH, F_OK) != 0 ? (u8 *)"docs" : (u8 *)DOC_PATH; @@ -1562,62 +1562,63 @@ int main(int argc, char **argv_orig, char **envp) { } - afl->argv = use_argv; - afl->fsrv.trace_bits = - afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); - - if (!afl->non_instrumented_mode && !afl->fsrv.qemu_mode && - !afl->unicorn_mode) { - - u32 set_env = 0; - if (!getenv("AFL_MAP_SIZE")) { - - afl->fsrv.map_size = 8000000; // dummy temporary value - setenv("AFL_MAP_SIZE", "8000000", 1); - set_env = 1; - - } - - u32 prev_map_size = afl->fsrv.map_size; + if (afl->non_instrumented_mode || afl->fsrv.qemu_mode || afl->unicorn_mode) { - u32 new_map_size = afl_fsrv_get_mapsize( - &afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); + map_size = afl->fsrv.map_size = MAP_SIZE; + afl->virgin_bits = ck_realloc(afl->virgin_bits, map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, map_size); + afl->top_rated = ck_realloc(afl->top_rated, map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, map_size); + afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, map_size); + afl->first_trace = ck_realloc(afl->first_trace, map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, map_size); - if (new_map_size && new_map_size != prev_map_size) { - - // only reinitialize when it makes sense - if (map_size < new_map_size || - (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { + } - OKF("Re-initializing maps to %u bytes", new_map_size); + afl->argv = use_argv; + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); - afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); - afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); - afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); - afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); - afl->top_rated = - ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); - afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); - afl->clean_trace_custom = - ck_realloc(afl->clean_trace_custom, new_map_size); - afl->first_trace = ck_realloc(afl->first_trace, new_map_size); - afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); + if (afl->fsrv.map_size <= 8000000) { - afl_fsrv_kill(&afl->fsrv); - afl_shm_deinit(&afl->shm); - afl->fsrv.map_size = new_map_size; - afl->fsrv.trace_bits = - afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); - setenv("AFL_NO_AUTODICT", "1", 1); // loaded already - afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child); + afl->fsrv.map_size = 8000000; // dummy temporary value + setenv("AFL_MAP_SIZE", "8000000", 1); - } + } - map_size = new_map_size; - if (set_env) { unsetenv("AFL_MAP_SIZE"); } + u32 new_map_size = afl_fsrv_get_mapsize( + &afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); + + // only reinitialize when it makes sense + if (map_size < new_map_size || + (new_map_size > map_size && new_map_size - map_size > MAP_SIZE && + (!afl->cmplog_binary || new_map_size == MAP_SIZE))) { + + OKF("Re-initializing maps to %u bytes", new_map_size); + + afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); + afl->top_rated = ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); + afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, new_map_size); + afl->first_trace = ck_realloc(afl->first_trace, new_map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); + + afl_fsrv_kill(&afl->fsrv); + afl_shm_deinit(&afl->shm); + afl->fsrv.map_size = new_map_size; + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + map_size = new_map_size; - } + } else { afl->fsrv.map_size = map_size; @@ -1633,64 +1634,50 @@ int main(int argc, char **argv_orig, char **envp) { afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; afl->cmplog_fsrv.init_child_func = cmplog_exec_child; - u32 set_env = 0; - if (!getenv("AFL_MAP_SIZE")) { - - afl->fsrv.map_size = 8000000; // dummy temporary value - setenv("AFL_MAP_SIZE", "8000000", 1); - set_env = 1; - - } - - u32 prev_map_size = afl->fsrv.map_size; - u32 new_map_size = afl_fsrv_get_mapsize(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); - if (new_map_size && new_map_size != prev_map_size) { - - // only reinitialize when it needs to be larger - if (map_size < new_map_size) { - - OKF("Re-initializing maps to %u bytes due cmplog", new_map_size); + // only reinitialize when necessary + if (map_size < new_map_size || + (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { + + OKF("Re-initializing maps to %u bytes due cmplog", new_map_size); + + afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); + afl->top_rated = + ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); + afl->clean_trace_custom = + ck_realloc(afl->clean_trace_custom, new_map_size); + afl->first_trace = ck_realloc(afl->first_trace, new_map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); + + afl_fsrv_kill(&afl->fsrv); + afl_fsrv_kill(&afl->cmplog_fsrv); + afl_shm_deinit(&afl->shm); + afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same + + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); - afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); - afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); - afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); - afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); - afl->top_rated = - ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); - afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); - afl->clean_trace_custom = - ck_realloc(afl->clean_trace_custom, new_map_size); - afl->first_trace = ck_realloc(afl->first_trace, new_map_size); - afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); - - afl_fsrv_kill(&afl->fsrv); - afl_fsrv_kill(&afl->cmplog_fsrv); - afl_shm_deinit(&afl->shm); - afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same - - afl->fsrv.trace_bits = - afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); - setenv("AFL_NO_AUTODICT", "1", 1); // loaded already - afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child); - - afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child); - - map_size = new_map_size; + map_size = new_map_size; - } + } else { - if (set_env) { unsetenv("AFL_MAP_SIZE"); } + afl->cmplog_fsrv.map_size = map_size; } - afl->cmplog_fsrv.map_size = map_size; - OKF("Cmplog forkserver successfully started"); } -- cgit 1.4.1 From 16e3e2a7f78a11702fb394803bec78b3b0a99906 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Thu, 11 Mar 2021 01:39:21 +0100 Subject: implemented #801 --- src/afl-fuzz-stats.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index bd856088..2e7de7b3 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -823,7 +823,12 @@ void show_stats(afl_state_t *afl) { SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP " total crashes : %s%-22s" bSTG bV "\n", u_stringify_int(IB(0), afl->fsrv.total_execs), - afl->unique_crashes ? cLRD : cRST, tmp); + // New crashes this round -> Red, restored crashes -> yellow, else + // white. + afl->total_crashes ? cLRD + : afl->unique_crashes ? cYEL + : cRST, + tmp); } -- cgit 1.4.1 From adeb0d18b12e717fe866e1790a522b4f158f11e1 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Thu, 11 Mar 2021 08:59:26 +0100 Subject: fix the auto map fix --- src/afl-fuzz.c | 159 ++++++++++++++++++++++++++++++------------------------ test/test-llvm.sh | 4 +- 2 files changed, 92 insertions(+), 71 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index cd049fd5..096ee29d 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1581,46 +1581,58 @@ int main(int argc, char **argv_orig, char **envp) { afl->fsrv.trace_bits = afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); - if (afl->fsrv.map_size <= 8000000) { + if (!afl->non_instrumented_mode && !afl->fsrv.qemu_mode && + !afl->unicorn_mode) { - afl->fsrv.map_size = 8000000; // dummy temporary value - setenv("AFL_MAP_SIZE", "8000000", 1); + if (map_size <= 8000000&& !afl->non_instrumented_mode && + !afl->fsrv.qemu_mode && !afl->unicorn_mode) { - } + afl->fsrv.map_size = 8000000; // dummy temporary value + setenv("AFL_MAP_SIZE", "8000000", 1); - u32 new_map_size = afl_fsrv_get_mapsize( - &afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); - - // only reinitialize when it makes sense - if (map_size < new_map_size || - (new_map_size > map_size && new_map_size - map_size > MAP_SIZE && - (!afl->cmplog_binary || new_map_size == MAP_SIZE))) { - - OKF("Re-initializing maps to %u bytes", new_map_size); - - afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); - afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); - afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); - afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); - afl->top_rated = ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); - afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); - afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, new_map_size); - afl->first_trace = ck_realloc(afl->first_trace, new_map_size); - afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); - - afl_fsrv_kill(&afl->fsrv); - afl_shm_deinit(&afl->shm); - afl->fsrv.map_size = new_map_size; - afl->fsrv.trace_bits = - afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); - setenv("AFL_NO_AUTODICT", "1", 1); // loaded already - afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child); - map_size = new_map_size; + } - } else { + u32 new_map_size = afl_fsrv_get_mapsize( + &afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); + + if (new_map_size && new_map_size != map_size) { + + // only reinitialize when it makes sense + if (map_size < new_map_size || + (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { + + OKF("Re-initializing maps to %u bytes", new_map_size); + + afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); + afl->top_rated = + ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); + afl->clean_trace_custom = + ck_realloc(afl->clean_trace_custom, new_map_size); + afl->first_trace = ck_realloc(afl->first_trace, new_map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); + + afl_fsrv_kill(&afl->fsrv); + afl_shm_deinit(&afl->shm); + afl->fsrv.map_size = new_map_size; + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); - afl->fsrv.map_size = map_size; + } + + map_size = new_map_size; + + } else { + + afl->fsrv.map_size = map_size; + + } } @@ -1634,50 +1646,59 @@ int main(int argc, char **argv_orig, char **envp) { afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; afl->cmplog_fsrv.init_child_func = cmplog_exec_child; + if (map_size <= 8000000 && !afl->non_instrumented_mode && + !afl->fsrv.qemu_mode && !afl->unicorn_mode) { + + afl->fsrv.map_size = 8000000; // dummy temporary value + setenv("AFL_MAP_SIZE", "8000000", 1); + + } + u32 new_map_size = afl_fsrv_get_mapsize(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); - // only reinitialize when necessary - if (map_size < new_map_size || - (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { - - OKF("Re-initializing maps to %u bytes due cmplog", new_map_size); - - afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); - afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); - afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); - afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); - afl->top_rated = - ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); - afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); - afl->clean_trace_custom = - ck_realloc(afl->clean_trace_custom, new_map_size); - afl->first_trace = ck_realloc(afl->first_trace, new_map_size); - afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); - - afl_fsrv_kill(&afl->fsrv); - afl_fsrv_kill(&afl->cmplog_fsrv); - afl_shm_deinit(&afl->shm); - afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same - - afl->fsrv.trace_bits = - afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); - setenv("AFL_NO_AUTODICT", "1", 1); // loaded already - afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child); - - afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child); + if (new_map_size && new_map_size != map_size) { - map_size = new_map_size; + // only reinitialize when it needs to be larger + if (map_size < new_map_size) { - } else { + OKF("Re-initializing maps to %u bytes due cmplog", new_map_size); + + afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); + afl->top_rated = + ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); + afl->clean_trace_custom = + ck_realloc(afl->clean_trace_custom, new_map_size); + afl->first_trace = ck_realloc(afl->first_trace, new_map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); + + afl_fsrv_kill(&afl->fsrv); + afl_fsrv_kill(&afl->cmplog_fsrv); + afl_shm_deinit(&afl->shm); + afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same - afl->cmplog_fsrv.map_size = map_size; + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + } + + map_size = new_map_size; } + afl->cmplog_fsrv.map_size = map_size; + OKF("Cmplog forkserver successfully started"); } diff --git a/test/test-llvm.sh b/test/test-llvm.sh index 6503cd98..1bcf013a 100755 --- a/test/test-llvm.sh +++ b/test/test-llvm.sh @@ -162,9 +162,9 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { test -e test-floatingpoint && { mkdir -p in echo ZZZZ > in/in - $ECHO "$GREY[*] running afl-fuzz with floating point splitting, this will take max. 45 seconds" + $ECHO "$GREY[*] running afl-fuzz with floating point splitting, this will take max. 30 seconds" { - AFL_BENCH_UNTIL_CRASH=1 AFL_NO_UI=1 ../afl-fuzz -Z -s 1 -V45 -m ${MEM_LIMIT} -i in -o out -D -- ./test-floatingpoint >>errors 2>&1 + AFL_BENCH_UNTIL_CRASH=1 AFL_NO_UI=1 ../afl-fuzz -Z -s 1 -V30 -m ${MEM_LIMIT} -i in -o out -D -- ./test-floatingpoint >>errors 2>&1 } >>errors 2>&1 test -n "$( ls out/default/crashes/id:* 2>/dev/null )" && { $ECHO "$GREEN[+] llvm_mode laf-intel floatingpoint splitting feature works correctly" -- cgit 1.4.1 From b7d90a9e318933211936be8f5174690cd8c01e24 Mon Sep 17 00:00:00 2001 From: aflpp Date: Thu, 11 Mar 2021 09:32:22 +0100 Subject: new auto map --- src/afl-fuzz.c | 130 +++++++++++++++++++++++++++------------------------------ 1 file changed, 61 insertions(+), 69 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 096ee29d..dbf4df37 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1584,7 +1584,7 @@ int main(int argc, char **argv_orig, char **envp) { if (!afl->non_instrumented_mode && !afl->fsrv.qemu_mode && !afl->unicorn_mode) { - if (map_size <= 8000000&& !afl->non_instrumented_mode && + if (map_size <= 8000000 && !afl->non_instrumented_mode && !afl->fsrv.qemu_mode && !afl->unicorn_mode) { afl->fsrv.map_size = 8000000; // dummy temporary value @@ -1595,45 +1595,40 @@ int main(int argc, char **argv_orig, char **envp) { u32 new_map_size = afl_fsrv_get_mapsize( &afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); - if (new_map_size && new_map_size != map_size) { - - // only reinitialize when it makes sense - if (map_size < new_map_size || - (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { - - OKF("Re-initializing maps to %u bytes", new_map_size); - - afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); - afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); - afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); - afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); - afl->top_rated = - ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); - afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); - afl->clean_trace_custom = - ck_realloc(afl->clean_trace_custom, new_map_size); - afl->first_trace = ck_realloc(afl->first_trace, new_map_size); - afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); - - afl_fsrv_kill(&afl->fsrv); - afl_shm_deinit(&afl->shm); - afl->fsrv.map_size = new_map_size; - afl->fsrv.trace_bits = - afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); - setenv("AFL_NO_AUTODICT", "1", 1); // loaded already - afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child); - - } + // only reinitialize when it makes sense + if (map_size < new_map_size || + (!afl->cmplog_binary && new_map_size < map_size && + map_size - new_map_size > MAP_SIZE)) { + + OKF("Re-initializing maps to %u bytes", new_map_size); + + afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); + afl->top_rated = + ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); + afl->clean_trace_custom = + ck_realloc(afl->clean_trace_custom, new_map_size); + afl->first_trace = ck_realloc(afl->first_trace, new_map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); + + afl_fsrv_kill(&afl->fsrv); + afl_shm_deinit(&afl->shm); + afl->fsrv.map_size = new_map_size; + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); map_size = new_map_size; - } else { - - afl->fsrv.map_size = map_size; - } + afl->fsrv.map_size = map_size; + } if (afl->cmplog_binary) { @@ -1658,40 +1653,37 @@ int main(int argc, char **argv_orig, char **envp) { afl_fsrv_get_mapsize(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); - if (new_map_size && new_map_size != map_size) { - - // only reinitialize when it needs to be larger - if (map_size < new_map_size) { - - OKF("Re-initializing maps to %u bytes due cmplog", new_map_size); - - afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); - afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); - afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); - afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); - afl->top_rated = - ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); - afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); - afl->clean_trace_custom = - ck_realloc(afl->clean_trace_custom, new_map_size); - afl->first_trace = ck_realloc(afl->first_trace, new_map_size); - afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); - - afl_fsrv_kill(&afl->fsrv); - afl_fsrv_kill(&afl->cmplog_fsrv); - afl_shm_deinit(&afl->shm); - afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same - - afl->fsrv.trace_bits = - afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); - setenv("AFL_NO_AUTODICT", "1", 1); // loaded already - afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child); - - afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child); - - } + // only reinitialize when it needs to be larger + if (map_size < new_map_size || + (new_map_size < map_size && map_size - new_map_size > MAP_SIZE)) { + + OKF("Re-initializing maps to %u bytes due cmplog", new_map_size); + + afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); + afl->top_rated = + ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); + afl->clean_trace_custom = + ck_realloc(afl->clean_trace_custom, new_map_size); + afl->first_trace = ck_realloc(afl->first_trace, new_map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); + + afl_fsrv_kill(&afl->fsrv); + afl_fsrv_kill(&afl->cmplog_fsrv); + afl_shm_deinit(&afl->shm); + afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same + + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); map_size = new_map_size; -- cgit 1.4.1 From 8ff5063545aeed101105a63d40d0b068af6c9ec1 Mon Sep 17 00:00:00 2001 From: aflpp Date: Thu, 11 Mar 2021 18:03:18 +0100 Subject: fix --- src/afl-fuzz.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index dbf4df37..7fe89c11 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1596,9 +1596,9 @@ int main(int argc, char **argv_orig, char **envp) { &afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); // only reinitialize when it makes sense - if (map_size < new_map_size || - (!afl->cmplog_binary && new_map_size < map_size && - map_size - new_map_size > MAP_SIZE)) { + if ((map_size < new_map_size || + (new_map_size != MAP_SIZE && new_map_size < map_size && + map_size - new_map_size > MAP_SIZE))) { OKF("Re-initializing maps to %u bytes", new_map_size); @@ -1644,7 +1644,7 @@ int main(int argc, char **argv_orig, char **envp) { if (map_size <= 8000000 && !afl->non_instrumented_mode && !afl->fsrv.qemu_mode && !afl->unicorn_mode) { - afl->fsrv.map_size = 8000000; // dummy temporary value + afl->cmplog_fsrv.map_size = 8000000; // dummy temporary value setenv("AFL_MAP_SIZE", "8000000", 1); } @@ -1654,8 +1654,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->afl_env.afl_debug_child); // only reinitialize when it needs to be larger - if (map_size < new_map_size || - (new_map_size < map_size && map_size - new_map_size > MAP_SIZE)) { + if (map_size < new_map_size) { OKF("Re-initializing maps to %u bytes due cmplog", new_map_size); @@ -1674,22 +1673,23 @@ int main(int argc, char **argv_orig, char **envp) { afl_fsrv_kill(&afl->fsrv); afl_fsrv_kill(&afl->cmplog_fsrv); afl_shm_deinit(&afl->shm); + afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same + map_size = new_map_size; + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already afl->fsrv.trace_bits = afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); - setenv("AFL_NO_AUTODICT", "1", 1); // loaded already afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); - afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); - map_size = new_map_size; + } else { - } + afl->cmplog_fsrv.map_size = new_map_size; - afl->cmplog_fsrv.map_size = map_size; + } OKF("Cmplog forkserver successfully started"); -- cgit 1.4.1 From d32b1d6b0c03cfc223c26c9af661c2592469a0de Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 12 Mar 2021 03:46:50 +0100 Subject: unique crashes yellow on resume (#801) --- src/afl-fuzz-stats.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 2e7de7b3..99059a2d 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -645,6 +645,13 @@ void show_stats(afl_state_t *afl) { #define SP10 SP5 SP5 #define SP20 SP10 SP10 + /* Since `total_crashes` does not get reloaded from disk on restart, + it indicates if we found crashes this round already -> paint red. + If it's 0, but `unique_crashes` is set from a past run, paint in yellow. */ + char *crash_color = afl->total_crashes ? cLRD + : afl->unique_crashes ? cYEL + : cRST; + /* Lord, forgive me this. */ SAYF(SET_G1 bSTG bLT bH bSTOP cCYA @@ -732,7 +739,7 @@ void show_stats(afl_state_t *afl) { u_stringify_time_diff(time_tmp, cur_ms, afl->last_crash_time); SAYF(bV bSTOP " last uniq crash : " cRST "%-33s " bSTG bV bSTOP " uniq crashes : %s%-6s" bSTG bV "\n", - time_tmp, afl->unique_crashes ? cLRD : cRST, tmp); + time_tmp, crash_color, tmp); sprintf(tmp, "%s%s", u_stringify_int(IB(0), afl->unique_hangs), (afl->unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); @@ -815,20 +822,13 @@ void show_stats(afl_state_t *afl) { SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP " new crashes : %s%-22s" bSTG bV "\n", - u_stringify_int(IB(0), afl->fsrv.total_execs), - afl->unique_crashes ? cLRD : cRST, tmp); + u_stringify_int(IB(0), afl->fsrv.total_execs), crash_color, tmp); } else { SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP " total crashes : %s%-22s" bSTG bV "\n", - u_stringify_int(IB(0), afl->fsrv.total_execs), - // New crashes this round -> Red, restored crashes -> yellow, else - // white. - afl->total_crashes ? cLRD - : afl->unique_crashes ? cYEL - : cRST, - tmp); + u_stringify_int(IB(0), afl->fsrv.total_execs), crash_color, tmp); } -- cgit 1.4.1 From 3753f56c254ac96023fbc295777b13d80a7a9d31 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 12 Mar 2021 03:47:12 +0100 Subject: make get_offsets executable --- src/afl-fuzz.c | 4 ++-- unicorn_mode/samples/speedtest/get_offsets.py | 0 2 files changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 unicorn_mode/samples/speedtest/get_offsets.py (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 7fe89c11..ff27048a 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1597,8 +1597,8 @@ int main(int argc, char **argv_orig, char **envp) { // only reinitialize when it makes sense if ((map_size < new_map_size || - (new_map_size != MAP_SIZE && new_map_size < map_size && - map_size - new_map_size > MAP_SIZE))) { + (new_map_size != MAP_SIZE && new_map_size < map_size && + map_size - new_map_size > MAP_SIZE))) { OKF("Re-initializing maps to %u bytes", new_map_size); diff --git a/unicorn_mode/samples/speedtest/get_offsets.py b/unicorn_mode/samples/speedtest/get_offsets.py old mode 100644 new mode 100755 -- cgit 1.4.1 From 61c8304f246527563d8be7e85d47f5ffc24682ba Mon Sep 17 00:00:00 2001 From: aflpp Date: Mon, 15 Mar 2021 20:32:01 +0100 Subject: fixes --- src/afl-fuzz-queue.c | 2 +- src/afl-fuzz.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 835aba40..b2f88205 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -325,7 +325,7 @@ static u8 check_if_text(afl_state_t *afl, struct queue_entry *q) { if (len >= MAX_FILE) len = MAX_FILE - 1; if ((fd = open(q->fname, O_RDONLY)) < 0) return 0; - buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len); + buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len + 1); comp = read(fd, buf, len); close(fd); if (comp != (ssize_t)len) return 0; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index ff27048a..64e4b869 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1680,6 +1680,7 @@ int main(int argc, char **argv_orig, char **envp) { setenv("AFL_NO_AUTODICT", "1", 1); // loaded already afl->fsrv.trace_bits = afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); + afl->cmplog_fsrv.trace_bits = afl->fsrv.trace_bits; afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, -- cgit 1.4.1 From d4fb7f8b4015297e1c74b28d671eba058cfb6366 Mon Sep 17 00:00:00 2001 From: realmadsci <71108352+realmadsci@users.noreply.github.com> Date: Fri, 12 Mar 2021 15:53:42 -0500 Subject: Add AFL_QEMU_CUSTOM_BIN environment flag In QEMU mode (-Q), setting AFL_QEMU_CUSTOM_BIN cause afl-fuzz to skip prepending afl-qemu-trace to your command line. Use this if you wish to use a custom afl-qemu-trace or if you need to modify the afl-qemu-trace arguments. --- docs/env_variables.md | 4 ++++ include/envs.h | 1 + src/afl-common.c | 7 +++++++ src/afl-fuzz-init.c | 1 + 4 files changed, 13 insertions(+) (limited to 'src') diff --git a/docs/env_variables.md b/docs/env_variables.md index a20f1e42..c6ad0aa4 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -393,6 +393,10 @@ checks or alter some of the more exotic semantics of the tool: - In QEMU mode (-Q), `AFL_PATH` will be searched for afl-qemu-trace. + - In QEMU mode (-Q), setting `AFL_QEMU_CUSTOM_BIN` cause afl-fuzz to skip + prepending `afl-qemu-trace` to your command line. Use this if you wish to use a + custom afl-qemu-trace or if you need to modify the afl-qemu-trace arguments. + - Setting `AFL_CYCLE_SCHEDULES` will switch to a different schedule everytime a cycle is finished. diff --git a/include/envs.h b/include/envs.h index 4d4d6b0e..e92bee2a 100644 --- a/include/envs.h +++ b/include/envs.h @@ -130,6 +130,7 @@ static char *afl_environment_variables[] = { "AFL_PERFORMANCE_FILE", "AFL_PRELOAD", "AFL_PYTHON_MODULE", + "AFL_QEMU_CUSTOM_BIN", "AFL_QEMU_COMPCOV", "AFL_QEMU_COMPCOV_DEBUG", "AFL_QEMU_DEBUG_MAPS", diff --git a/src/afl-common.c b/src/afl-common.c index 9f6eb564..58fbf765 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -149,6 +149,13 @@ void argv_cpy_free(char **argv) { char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { + if (unlikely(getenv("AFL_QEMU_CUSTOM_BIN"))) { + WARNF( + "AFL_QEMU_CUSTOM_BIN is enabled. " + "You must run your target under afl-qemu-trace on your own!"); + return argv; + } + if (!unlikely(own_loc)) { FATAL("BUG: param own_loc is NULL"); } u8 *tmp, *cp = NULL, *rsl, *own_copy; diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index ca2f75f1..82c1799e 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2592,6 +2592,7 @@ void check_binary(afl_state_t *afl, u8 *fname) { } if (afl->afl_env.afl_skip_bin_check || afl->use_wine || afl->unicorn_mode || + (afl->fsrv.qemu_mode && getenv("AFL_QEMU_CUSTOM_BIN")) || afl->non_instrumented_mode) { return; -- cgit 1.4.1 From 01ad7610beaf772063c9011daae5fa3a3232494c Mon Sep 17 00:00:00 2001 From: realmadsci <71108352+realmadsci@users.noreply.github.com> Date: Mon, 15 Mar 2021 11:45:58 -0400 Subject: Remove AFL_PRELOAD and AFL_USE_QASAN handlers These are now processed in afl-qemu-trace so that the "copy+paste" code that is in all of the other AFL tools can be removed. This also allows the AFL_USE_QASAN flag to work the same when used with tools like afl-fuzz as it does with afl-qemu-trace. This is important in situations where loading the QASAN library changes the address of your desired entrypoint, or for crash validation using the same environment that afl-fuzz was using. With this change, the same set of environment variables can be used in exactly the same way between afl-fuzz, afl-showmap, and afl-qemu-trace, and you will get exactly the same guest environment. --- include/common.h | 1 - src/afl-analyze.c | 58 +-------------------------- src/afl-common.c | 60 ---------------------------- src/afl-fuzz.c | 59 +-------------------------- src/afl-showmap.c | 58 +-------------------------- src/afl-tmin.c | 58 +-------------------------- utils/afl_network_proxy/afl-network-server.c | 33 +-------------- 7 files changed, 5 insertions(+), 322 deletions(-) (limited to 'src') diff --git a/include/common.h b/include/common.h index cd728536..b7adbaec 100644 --- a/include/common.h +++ b/include/common.h @@ -48,7 +48,6 @@ void argv_cpy_free(char **argv); char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv); char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv); char * get_afl_env(char *env); -u8 * get_libqasan_path(u8 *own_loc); extern u8 be_quiet; extern u8 *doc_path; /* path to documentation dir */ diff --git a/src/afl-analyze.c b/src/afl-analyze.c index d46ecb8d..86b0f7e9 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -822,38 +822,7 @@ static void set_up_environment(void) { if (qemu_mode) { - u8 *qemu_preload = getenv("QEMU_SET_ENV"); - u8 *afl_preload = getenv("AFL_PRELOAD"); - u8 *buf; - - s32 i, afl_preload_size = strlen(afl_preload); - for (i = 0; i < afl_preload_size; ++i) { - - if (afl_preload[i] == ',') { - - PFATAL( - "Comma (',') is not allowed in AFL_PRELOAD when -Q is " - "specified!"); - - } - - } - - if (qemu_preload) { - - buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - qemu_preload, afl_preload, afl_preload); - - } else { - - buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - afl_preload, afl_preload); - - } - - setenv("QEMU_SET_ENV", buf, 1); - - ck_free(buf); + /* afl-qemu-trace takes care of converting AFL_PRELOAD. */ } else { @@ -1079,31 +1048,6 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !in_file) { usage(argv[0]); } - if (qemu_mode && getenv("AFL_USE_QASAN")) { - - u8 *preload = getenv("AFL_PRELOAD"); - u8 *libqasan = get_libqasan_path(argv_orig[0]); - - if (!preload) { - - setenv("AFL_PRELOAD", libqasan, 0); - - } else { - - u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); - strcpy(result, libqasan); - strcat(result, " "); - strcat(result, preload); - - setenv("AFL_PRELOAD", result, 1); - ck_free(result); - - } - - ck_free(libqasan); - - } - map_size = get_map_size(); use_hex_offsets = !!get_afl_env("AFL_ANALYZE_HEX"); diff --git a/src/afl-common.c b/src/afl-common.c index 9f6eb564..5ac50595 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -338,66 +338,6 @@ char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { } -/* Get libqasan path. */ - -u8 *get_libqasan_path(u8 *own_loc) { - - if (!unlikely(own_loc)) { FATAL("BUG: param own_loc is NULL"); } - - u8 *tmp, *cp = NULL, *rsl, *own_copy; - - tmp = getenv("AFL_PATH"); - - if (tmp) { - - cp = alloc_printf("%s/libqasan.so", tmp); - - if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); } - - return cp; - - } - - own_copy = ck_strdup(own_loc); - rsl = strrchr(own_copy, '/'); - - if (rsl) { - - *rsl = 0; - - cp = alloc_printf("%s/libqasan.so", own_copy); - ck_free(own_copy); - - if (!access(cp, X_OK)) { return cp; } - - } else { - - ck_free(own_copy); - - } - - if (!access(AFL_PATH "/libqasan.so", X_OK)) { - - if (cp) { ck_free(cp); } - - return ck_strdup(AFL_PATH "/libqasan.so"); - - } - - SAYF("\n" cLRD "[-] " cRST - "Oops, unable to find the 'libqasan.so' binary. The binary must be " - "built\n" - " separately by following the instructions in " - "qemu_mode/libqasan/README.md. " - "If you\n" - " already have the binary installed, you may need to specify " - "AFL_PATH in the\n" - " environment.\n"); - - FATAL("Failed to locate 'libqasan.so'."); - -} - /* Find binary, used by analyze, showmap, tmin @returns the path, allocating the string */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index ff27048a..3ca377f6 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1022,32 +1022,6 @@ int main(int argc, char **argv_orig, char **envp) { } - if (afl->fsrv.qemu_mode && getenv("AFL_USE_QASAN")) { - - u8 *preload = getenv("AFL_PRELOAD"); - u8 *libqasan = get_libqasan_path(argv_orig[0]); - - if (!preload) { - - setenv("AFL_PRELOAD", libqasan, 0); - - } else { - - u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); - strcpy(result, libqasan); - strcat(result, " "); - strcat(result, preload); - - setenv("AFL_PRELOAD", result, 1); - ck_free(result); - - } - - afl->afl_env.afl_preload = (u8 *)getenv("AFL_PRELOAD"); - ck_free(libqasan); - - } - if (afl->fsrv.mem_limit && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260; OKF("afl++ is maintained by Marc \"van Hauser\" Heuse, Heiko \"hexcoder\" " @@ -1312,38 +1286,7 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->fsrv.qemu_mode) { - u8 *qemu_preload = getenv("QEMU_SET_ENV"); - u8 *afl_preload = getenv("AFL_PRELOAD"); - u8 *buf; - - s32 j, afl_preload_size = strlen(afl_preload); - for (j = 0; j < afl_preload_size; ++j) { - - if (afl_preload[j] == ',') { - - PFATAL( - "Comma (',') is not allowed in AFL_PRELOAD when -Q is " - "specified!"); - - } - - } - - if (qemu_preload) { - - buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - qemu_preload, afl_preload, afl_preload); - - } else { - - buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - afl_preload, afl_preload); - - } - - setenv("QEMU_SET_ENV", buf, 1); - - ck_free(buf); + /* afl-qemu-trace takes care of converting AFL_PRELOAD. */ } else { diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 0fc76193..7bf5a9c7 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -599,38 +599,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { if (fsrv->qemu_mode) { - u8 *qemu_preload = getenv("QEMU_SET_ENV"); - u8 *afl_preload = getenv("AFL_PRELOAD"); - u8 *buf; - - s32 i, afl_preload_size = strlen(afl_preload); - for (i = 0; i < afl_preload_size; ++i) { - - if (afl_preload[i] == ',') { - - PFATAL( - "Comma (',') is not allowed in AFL_PRELOAD when -Q is " - "specified!"); - - } - - } - - if (qemu_preload) { - - buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - qemu_preload, afl_preload, afl_preload); - - } else { - - buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - afl_preload, afl_preload); - - } - - setenv("QEMU_SET_ENV", buf, 1); - - ck_free(buf); + /* afl-qemu-trace takes care of converting AFL_PRELOAD. */ } else { @@ -946,31 +915,6 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !out_file) { usage(argv[0]); } - if (fsrv->qemu_mode && getenv("AFL_USE_QASAN")) { - - u8 *preload = getenv("AFL_PRELOAD"); - u8 *libqasan = get_libqasan_path(argv_orig[0]); - - if (!preload) { - - setenv("AFL_PRELOAD", libqasan, 0); - - } else { - - u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); - strcpy(result, libqasan); - strcat(result, " "); - strcat(result, preload); - - setenv("AFL_PRELOAD", result, 1); - ck_free(result); - - } - - ck_free(libqasan); - - } - if (in_dir) { if (!out_file && !collect_coverage) diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 6d04c652..7ef8b9bf 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -753,38 +753,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { if (fsrv->qemu_mode) { - u8 *qemu_preload = getenv("QEMU_SET_ENV"); - u8 *afl_preload = getenv("AFL_PRELOAD"); - u8 *buf; - - s32 i, afl_preload_size = strlen(afl_preload); - for (i = 0; i < afl_preload_size; ++i) { - - if (afl_preload[i] == ',') { - - PFATAL( - "Comma (',') is not allowed in AFL_PRELOAD when -Q is " - "specified!"); - - } - - } - - if (qemu_preload) { - - buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - qemu_preload, afl_preload, afl_preload); - - } else { - - buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - afl_preload, afl_preload); - - } - - setenv("QEMU_SET_ENV", buf, 1); - - ck_free(buf); + /* afl-qemu-trace takes care of converting AFL_PRELOAD. */ } else { @@ -1079,31 +1048,6 @@ int main(int argc, char **argv_orig, char **envp) { check_environment_vars(envp); setenv("AFL_NO_AUTODICT", "1", 1); - if (fsrv->qemu_mode && getenv("AFL_USE_QASAN")) { - - u8 *preload = getenv("AFL_PRELOAD"); - u8 *libqasan = get_libqasan_path(argv_orig[0]); - - if (!preload) { - - setenv("AFL_PRELOAD", libqasan, 0); - - } else { - - u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); - strcpy(result, libqasan); - strcat(result, " "); - strcat(result, preload); - - setenv("AFL_PRELOAD", result, 1); - ck_free(result); - - } - - ck_free(libqasan); - - } - /* initialize cmplog_mode */ shm.cmplog_mode = 0; diff --git a/utils/afl_network_proxy/afl-network-server.c b/utils/afl_network_proxy/afl-network-server.c index fe225416..0dfae658 100644 --- a/utils/afl_network_proxy/afl-network-server.c +++ b/utils/afl_network_proxy/afl-network-server.c @@ -237,38 +237,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { if (fsrv->qemu_mode) { - u8 *qemu_preload = getenv("QEMU_SET_ENV"); - u8 *afl_preload = getenv("AFL_PRELOAD"); - u8 *buf; - - s32 i, afl_preload_size = strlen(afl_preload); - for (i = 0; i < afl_preload_size; ++i) { - - if (afl_preload[i] == ',') { - - PFATAL( - "Comma (',') is not allowed in AFL_PRELOAD when -Q is " - "specified!"); - - } - - } - - if (qemu_preload) { - - buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - qemu_preload, afl_preload, afl_preload); - - } else { - - buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - afl_preload, afl_preload); - - } - - setenv("QEMU_SET_ENV", buf, 1); - - afl_free(buf); + /* afl-qemu-trace takes care of converting AFL_PRELOAD. */ } else { -- cgit 1.4.1 From 862cb3217f5983e5cfff6568f6b31fcf1e960802 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 16 Mar 2021 14:38:13 +0100 Subject: fix cmplog rtn --- include/envs.h | 1 + instrumentation/afl-compiler-rt.o.c | 10 +++++----- src/afl-common.c | 6 ++++-- utils/aflpp_driver/aflpp_driver.c | 10 ++++++++++ 4 files changed, 20 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/include/envs.h b/include/envs.h index e92bee2a..cfd73b68 100644 --- a/include/envs.h +++ b/include/envs.h @@ -50,6 +50,7 @@ static char *afl_environment_variables[] = { "AFL_FAST_CAL", "AFL_FORCE_UI", "AFL_FUZZER_ARGS", // oss-fuzz + "AFL_GDB", "AFL_GCC_ALLOWLIST", "AFL_GCC_DENYLIST", "AFL_GCC_BLOCKLIST", diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index cca38cd0..32dbc53d 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1730,18 +1730,18 @@ __attribute__((weak)) void *__asan_region_is_poisoned(void *beg, size_t size) { // to avoid to call it on .text addresses static int area_is_valid(void *ptr, size_t len) { - if (unlikely(__asan_region_is_poisoned(ptr, len))) { return 0; } + if (unlikely(!ptr || __asan_region_is_poisoned(ptr, len))) { return 0; } - long r = syscall(__afl_dummy_fd[1], SYS_write, ptr, len); + long r = syscall(SYS_write, __afl_dummy_fd[1], ptr, len); 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); + if (page >= p + len) { return 0; } // no isnt, return fail + len = page - p - len; + r = syscall(SYS_write, __afl_dummy_fd[1], p, len); } diff --git a/src/afl-common.c b/src/afl-common.c index bfb05a67..27b63434 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -150,10 +150,12 @@ void argv_cpy_free(char **argv) { char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { if (unlikely(getenv("AFL_QEMU_CUSTOM_BIN"))) { + WARNF( - "AFL_QEMU_CUSTOM_BIN is enabled. " - "You must run your target under afl-qemu-trace on your own!"); + "AFL_QEMU_CUSTOM_BIN is enabled. " + "You must run your target under afl-qemu-trace on your own!"); return argv; + } if (!unlikely(own_loc)) { FATAL("BUG: param own_loc is NULL"); } diff --git a/utils/aflpp_driver/aflpp_driver.c b/utils/aflpp_driver/aflpp_driver.c index 9c97607c..f0f3a47d 100644 --- a/utils/aflpp_driver/aflpp_driver.c +++ b/utils/aflpp_driver/aflpp_driver.c @@ -208,6 +208,16 @@ int main(int argc, char **argv) { "======================================================\n", argv[0], argv[0]); + if (getenv("AFL_GDB")) { + + char cmd[64]; + snprintf(cmd, sizeof(cmd), "cat /proc/%d/maps", getpid()); + system(cmd); + fprintf(stderr, "DEBUG: aflpp_driver pid is %d\n", getpid()); + sleep(1); + + } + output_file = stderr; maybe_duplicate_stderr(); maybe_close_fd_mask(); -- cgit 1.4.1 From 7c2436c7114e21f029b06346421a73910deac578 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 16 Mar 2021 19:34:34 +0100 Subject: no runtime for shared linking --- docs/Changelog.md | 4 +++- src/afl-cc.c | 59 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 34 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index 17d68b0a..bf04c58e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,7 +9,9 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . ### Version ++3.12a (dev) - - fix cmplog rtn (rare crash and not being able to gather ptr data) + - afl-cc: + - fix cmplog rtn (rare crash and not being able to gather ptr data) + - link runtime not to shared libs - qemu_mode (thanks @realmadsci): - move AFL_PRELOAD and AFL_USE_QASAN logic inside afl-qemu-trace - add AFL_QEMU_CUSTOM_BIN diff --git a/src/afl-cc.c b/src/afl-cc.c index 44654de0..18401d0d 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -959,56 +959,59 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode != GCC && compiler_mode != CLANG) { - switch (bit_mode) { + if (!shared_linking) { - case 0: - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-compiler-rt.o", obj_path); - if (lto_mode) - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); - break; + switch (bit_mode) { - case 32: - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-compiler-rt-32.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m32 is not supported by your compiler"); - if (lto_mode) { + case 0: + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-compiler-rt.o", obj_path); + if (lto_mode) + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); + break; + case 32: cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path); + alloc_printf("%s/afl-compiler-rt-32.o", obj_path); if (access(cc_params[cc_par_cnt - 1], R_OK)) FATAL("-m32 is not supported by your compiler"); + if (lto_mode) { - } + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m32 is not supported by your compiler"); - break; + } - case 64: - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-compiler-rt-64.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m64 is not supported by your compiler"); - if (lto_mode) { + break; + case 64: cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path); + alloc_printf("%s/afl-compiler-rt-64.o", obj_path); if (access(cc_params[cc_par_cnt - 1], R_OK)) FATAL("-m64 is not supported by your compiler"); + if (lto_mode) { - } + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m64 is not supported by your compiler"); - break; + } - } + break; + + } #if !defined(__APPLE__) && !defined(__sun) - if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif + } + #if defined(USEMMAP) && !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; #endif -- cgit 1.4.1 From f59ef29c267c8500cc07115b10355c82dd770064 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 01:08:10 +0100 Subject: fix attempt --- src/afl-fuzz.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index cfb507a7..12642557 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1539,9 +1539,9 @@ int main(int argc, char **argv_orig, char **envp) { &afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); // only reinitialize when it makes sense - if ((map_size < new_map_size || + if ((map_size < new_map_size /*|| (new_map_size != MAP_SIZE && new_map_size < map_size && - map_size - new_map_size > MAP_SIZE))) { + map_size - new_map_size > MAP_SIZE)*/)) { OKF("Re-initializing maps to %u bytes", new_map_size); @@ -1570,8 +1570,6 @@ int main(int argc, char **argv_orig, char **envp) { } - afl->fsrv.map_size = map_size; - } if (afl->cmplog_binary) { @@ -1629,16 +1627,14 @@ int main(int argc, char **argv_orig, char **envp) { afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); - } else { - - afl->cmplog_fsrv.map_size = new_map_size; - } OKF("Cmplog forkserver successfully started"); } + fprintf(stderr, "NORMAL %u, CMPLOG %u\n", afl->fsrv.map_size, afl->cmplog_fsrv.map_size); + load_auto(afl); if (extras_dir_cnt) { -- cgit 1.4.1 From 413807db01b642a2ad4c70e94517d74c11ace91c Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 01:25:21 +0100 Subject: lto test --- src/afl-cc.c | 6 ++++-- src/afl-fuzz.c | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 18401d0d..4a724c12 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -959,11 +959,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode != GCC && compiler_mode != CLANG) { - if (!shared_linking) { - switch (bit_mode) { case 0: + if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("%s/afl-compiler-rt.o", obj_path); if (lto_mode) @@ -972,6 +971,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { break; case 32: + if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("%s/afl-compiler-rt-32.o", obj_path); if (access(cc_params[cc_par_cnt - 1], R_OK)) @@ -988,6 +988,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { break; case 64: + if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("%s/afl-compiler-rt-64.o", obj_path); if (access(cc_params[cc_par_cnt - 1], R_OK)) @@ -1006,6 +1007,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if !defined(__APPLE__) && !defined(__sun) + if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 12642557..6c170632 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1633,6 +1633,7 @@ int main(int argc, char **argv_orig, char **envp) { } + printf("NORMAL %u, CMPLOG %u\n", afl->fsrv.map_size, afl->cmplog_fsrv.map_size); fprintf(stderr, "NORMAL %u, CMPLOG %u\n", afl->fsrv.map_size, afl->cmplog_fsrv.map_size); load_auto(afl); -- cgit 1.4.1 From c6f1c56c15801662eef7b0b19e42287ddb3b97a7 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 01:27:01 +0100 Subject: fix --- src/afl-cc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index 4a724c12..206066fd 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1018,8 +1018,6 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-lrt"; #endif - } - #endif cc_params[cc_par_cnt] = NULL; -- cgit 1.4.1 From dda4757b358d1d28d5e48028ea22023c3977d706 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 01:40:27 +0100 Subject: debug --- src/afl-fuzz.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 6c170632..2fde561c 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1633,8 +1633,10 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->debug) { printf("NORMAL %u, CMPLOG %u\n", afl->fsrv.map_size, afl->cmplog_fsrv.map_size); fprintf(stderr, "NORMAL %u, CMPLOG %u\n", afl->fsrv.map_size, afl->cmplog_fsrv.map_size); + } load_auto(afl); -- cgit 1.4.1 From 5e2a5f1110e29c36f1c41fb4677ab698c5d571c0 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 10:26:02 +0100 Subject: shmem map size in config.h --- include/config.h | 9 ++++++ instrumentation/afl-compiler-rt.o.c | 6 ++-- src/afl-cc.c | 62 ++++++++++++++++++------------------- src/afl-common.c | 2 +- src/afl-fuzz.c | 24 +++++++++----- 5 files changed, 60 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/include/config.h b/include/config.h index b049fee0..29225f6b 100644 --- a/include/config.h +++ b/include/config.h @@ -34,6 +34,15 @@ * * ******************************************************/ +/* Default shared memory map size. Most targets just need a coverage map + between 20-250kb. Plus there is an auto-detection feature in afl-fuzz. + However if a target has problematic constructors and init arrays then + this can fail. Hence afl-fuzz deploys a larger default map. The largest + map seen so far is the xlsx fuzzer for libreoffice which is 5MB. + At runtime this value can be overriden via AFL_MAP_SIZE. + Default: 8MB (defined in bytes) */ +#define DEFAULT_SHMEM_SIZE (8 * 1024 * 1024) + /* CMPLOG/REDQUEEN TUNING * * Here you can modify tuning and solving options for CMPLOG. diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 70148b78..c635ae63 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1774,14 +1774,14 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { */ if (unlikely(!__afl_cmp_map)) return; - //fprintf(stderr, "RTN1 %p %p\n", ptr1, ptr2); + // fprintf(stderr, "RTN1 %p %p\n", ptr1, ptr2); int l1, l2; if ((l1 = area_is_valid(ptr1, 32)) <= 0 || (l2 = area_is_valid(ptr2, 32)) <= 0) return; int len = MIN(l1, l2); - //fprintf(stderr, "RTN2 %u\n", len); + // fprintf(stderr, "RTN2 %u\n", len); uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); k &= CMP_MAP_W - 1; @@ -1812,7 +1812,7 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { ptr1, len); __builtin_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1, ptr2, len); - //fprintf(stderr, "RTN3\n"); + // fprintf(stderr, "RTN3\n"); } diff --git a/src/afl-cc.c b/src/afl-cc.c index 206066fd..ebbd390c 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -959,63 +959,63 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode != GCC && compiler_mode != CLANG) { - switch (bit_mode) { + switch (bit_mode) { - case 0: - if (!shared_linking) + case 0: + if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("%s/afl-compiler-rt.o", obj_path); - if (lto_mode) - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); - break; + if (lto_mode) + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); + break; - case 32: - if (!shared_linking) + case 32: + if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("%s/afl-compiler-rt-32.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m32 is not supported by your compiler"); + if (lto_mode) { + + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path); if (access(cc_params[cc_par_cnt - 1], R_OK)) FATAL("-m32 is not supported by your compiler"); - if (lto_mode) { - - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m32 is not supported by your compiler"); - } + } - break; + break; - case 64: - if (!shared_linking) + case 64: + if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("%s/afl-compiler-rt-64.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m64 is not supported by your compiler"); + if (lto_mode) { + + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path); if (access(cc_params[cc_par_cnt - 1], R_OK)) FATAL("-m64 is not supported by your compiler"); - if (lto_mode) { - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m64 is not supported by your compiler"); - - } + } - break; + break; - } + } #if !defined(__APPLE__) && !defined(__sun) - if (!shared_linking) + if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif - } + } #if defined(USEMMAP) && !defined(__HAIKU__) - cc_params[cc_par_cnt++] = "-lrt"; + cc_params[cc_par_cnt++] = "-lrt"; #endif #endif diff --git a/src/afl-common.c b/src/afl-common.c index 27b63434..7e56ce3f 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -1072,7 +1072,7 @@ u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms) { /* Reads the map size from ENV */ u32 get_map_size(void) { - uint32_t map_size = 8000000; // a very large default map + uint32_t map_size = DEFAULT_SHMEM_SIZE; char * ptr; if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 2fde561c..8318a92e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1527,11 +1527,13 @@ int main(int argc, char **argv_orig, char **envp) { if (!afl->non_instrumented_mode && !afl->fsrv.qemu_mode && !afl->unicorn_mode) { - if (map_size <= 8000000 && !afl->non_instrumented_mode && + if (map_size <= DEFAULT_SHMEM_SIZE && !afl->non_instrumented_mode && !afl->fsrv.qemu_mode && !afl->unicorn_mode) { - afl->fsrv.map_size = 8000000; // dummy temporary value - setenv("AFL_MAP_SIZE", "8000000", 1); + afl->fsrv.map_size = DEFAULT_SHMEM_SIZE; // dummy temporary value + char vbuf[16]; + snprintf(vbuf, sizeof(vbuf), "%u", DEFAULT_SHMEM_SIZE); + setenv("AFL_MAP_SIZE", vbuf, 1); } @@ -1582,11 +1584,13 @@ int main(int argc, char **argv_orig, char **envp) { afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; afl->cmplog_fsrv.init_child_func = cmplog_exec_child; - if (map_size <= 8000000 && !afl->non_instrumented_mode && + if (map_size <= DEFAULT_SHMEM_SIZE && !afl->non_instrumented_mode && !afl->fsrv.qemu_mode && !afl->unicorn_mode) { - afl->cmplog_fsrv.map_size = 8000000; // dummy temporary value - setenv("AFL_MAP_SIZE", "8000000", 1); + afl->fsrv.map_size = DEFAULT_SHMEM_SIZE; // dummy temporary value + char vbuf[16]; + snprintf(vbuf, sizeof(vbuf), "%u", DEFAULT_SHMEM_SIZE); + setenv("AFL_MAP_SIZE", vbuf, 1); } @@ -1634,8 +1638,12 @@ int main(int argc, char **argv_orig, char **envp) { } if (afl->debug) { - printf("NORMAL %u, CMPLOG %u\n", afl->fsrv.map_size, afl->cmplog_fsrv.map_size); - fprintf(stderr, "NORMAL %u, CMPLOG %u\n", afl->fsrv.map_size, afl->cmplog_fsrv.map_size); + + printf("NORMAL %u, CMPLOG %u\n", afl->fsrv.map_size, + afl->cmplog_fsrv.map_size); + fprintf(stderr, "NORMAL %u, CMPLOG %u\n", afl->fsrv.map_size, + afl->cmplog_fsrv.map_size); + } load_auto(afl); -- cgit 1.4.1 From cdd30c766b7102dcd5413efd8f009b4388b40425 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 11:25:14 +0100 Subject: fix m32/m64 --- src/afl-cc.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/afl-cc.c b/src/afl-cc.c index ebbd390c..c4f8f3fc 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -971,11 +971,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { break; case 32: - if (!shared_linking) + if (!shared_linking) { + cc_params[cc_par_cnt++] = alloc_printf("%s/afl-compiler-rt-32.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m32 is not supported by your compiler"); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m32 is not supported by your compiler"); + + } + if (lto_mode) { cc_params[cc_par_cnt++] = @@ -988,11 +992,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { break; case 64: - if (!shared_linking) + if (!shared_linking) { + cc_params[cc_par_cnt++] = alloc_printf("%s/afl-compiler-rt-64.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m64 is not supported by your compiler"); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m64 is not supported by your compiler"); + + } + if (lto_mode) { cc_params[cc_par_cnt++] = -- cgit 1.4.1 From 513bd703845283c4f94ae2760c48ca8cc38a0076 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 12:44:07 +0100 Subject: shared linking fix --- dynamic_list.txt | 16 ++++++++++++++++ src/afl-cc.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/dynamic_list.txt b/dynamic_list.txt index 3c0b054f..985ec799 100644 --- a/dynamic_list.txt +++ b/dynamic_list.txt @@ -5,6 +5,22 @@ "__afl_auto_init"; "__afl_area_initial"; "__afl_prev_loc"; + "__afl_prev_caller"; + "__afl_prev_ctx"; + "__afl_final_loc"; + "__afl_map_addr"; + "__afl_dictionary"; + "__afl_dictionary_len"; + "__afl_selective_coverage"; + "__afl_selective_coverage_start_off"; + "__afl_selective_coverage_temp;" + "__afl_coverage_discard"; + "__afl_coverage_skip"; + "__afl_coverage_on"; + "__afl_coverage_off"; + "__afl_coverage_interesting"; + "__afl_fuzz_len"; + "__afl_fuzz_ptr"; "__sanitizer_cov_trace_pc_guard"; "__sanitizer_cov_trace_pc_guard_init"; }; diff --git a/src/afl-cc.c b/src/afl-cc.c index c4f8f3fc..e13f285d 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -682,17 +682,42 @@ static void edit_params(u32 argc, char **argv, char **envp) { /* Detect stray -v calls from ./configure scripts. */ + u8 skip_next = 0; while (--argc) { u8 *cur = *(++argv); + if (skip_next) { + + skip_next = 0; + continue; + + } + if (!strncmp(cur, "--afl", 5)) continue; if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue; if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; if (!strncmp(cur, "-fno-unroll", 11)) continue; if (strstr(cur, "afl-compiler-rt") || strstr(cur, "afl-llvm-rt")) continue; - if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined")) + if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined") || + !strcmp(cur, "--no-undefined")) { + continue; + + } + + if (!strcmp(cur, "-z")) { + + u8 *param = *(argv + 1); + if (!strcmp(param, "defs")) { + + skip_next = 1; + continue; + + } + + } + if (!strncmp(cur, "-fsanitize=fuzzer-", strlen("-fsanitize=fuzzer-")) || !strncmp(cur, "-fsanitize-coverage", strlen("-fsanitize-coverage"))) { @@ -1015,7 +1040,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if !defined(__APPLE__) && !defined(__sun) - if (!shared_linking) + if (shared_linking) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif -- cgit 1.4.1 From 94312796f936ba1830b61432a0f958e192dd212f Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 13:16:02 +0100 Subject: better map variability --- docs/Changelog.md | 1 + src/afl-fuzz.c | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/docs/Changelog.md b/docs/Changelog.md index bf04c58e..8dc218af 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -12,6 +12,7 @@ sending a mail to . - afl-cc: - fix cmplog rtn (rare crash and not being able to gather ptr data) - link runtime not to shared libs + - ensure shared libraries are properly built and instrumented - qemu_mode (thanks @realmadsci): - move AFL_PRELOAD and AFL_USE_QASAN logic inside afl-qemu-trace - add AFL_QEMU_CUSTOM_BIN diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 8318a92e..ff4c5281 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1584,12 +1584,14 @@ int main(int argc, char **argv_orig, char **envp) { afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; afl->cmplog_fsrv.init_child_func = cmplog_exec_child; - if (map_size <= DEFAULT_SHMEM_SIZE && !afl->non_instrumented_mode && - !afl->fsrv.qemu_mode && !afl->unicorn_mode) { + if ((map_size <= DEFAULT_SHMEM_SIZE || + afl->cmplog_fsrv.map_size < map_size) && + !afl->non_instrumented_mode && !afl->fsrv.qemu_mode && + !afl->unicorn_mode) { - afl->fsrv.map_size = DEFAULT_SHMEM_SIZE; // dummy temporary value + afl->cmplog_fsrv.map_size = MAX(map_size, (u32)DEFAULT_SHMEM_SIZE); char vbuf[16]; - snprintf(vbuf, sizeof(vbuf), "%u", DEFAULT_SHMEM_SIZE); + snprintf(vbuf, sizeof(vbuf), "%u", afl->cmplog_fsrv.map_size); setenv("AFL_MAP_SIZE", vbuf, 1); } -- cgit 1.4.1