From 83790d65afb52a055d093451a50ce55690a25002 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 8 Jul 2020 11:16:39 +0200 Subject: eliminate race condition for cpu affinity on -M/-S --- src/afl-fuzz-init.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 85 insertions(+), 12 deletions(-) (limited to 'src/afl-fuzz-init.c') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index a2e849dc..e51b4729 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -36,14 +36,11 @@ void bind_to_free_cpu(afl_state_t *afl) { #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) cpu_set_t c; #elif defined(__NetBSD__) - cpuset_t * c; + cpuset_t *c; #elif defined(__sun) - psetid_t c; + psetid_t c; #endif - u8 cpu_used[4096] = {0}; - u32 i; - if (afl->cpu_core_count < 2) { return; } if (afl->afl_env.afl_no_affinity) { @@ -53,13 +50,46 @@ void bind_to_free_cpu(afl_state_t *afl) { } + u8 cpu_used[4096] = {0}, lockfile[PATH_MAX] = ""; + u32 i; + + if (afl->sync_id) { + + s32 lockfd, first = 1; + + snprintf(lockfile, sizeof(lockfile), "%s/.affinity_lock", afl->sync_dir); + setenv(CPU_AFFINITY_ENV_VAR, lockfile, 1); + + do { + + if ((lockfd = open(lockfile, O_RDWR | O_CREAT | O_EXCL, 0600)) < 0) { + + if (first) { + + WARNF("CPU affinity lock file present, waiting ..."); + first = 0; + + } + + usleep(1000); + + } + + } while (lockfd < 0); + + close(lockfd); + + } + #if defined(__linux__) + DIR * d; struct dirent *de; d = opendir("/proc"); if (!d) { + if (lockfile[0]) unlink(lockfile); WARNF("Unable to access /proc - can't scan for free CPU cores."); return; @@ -67,11 +97,6 @@ void bind_to_free_cpu(afl_state_t *afl) { ACTF("Checking CPU core loadout..."); - /* Introduce some jitter, in case multiple AFL tasks are doing the same - thing at the same time... */ - - usleep(R(1000) * 250); - /* Scan all /proc//status entries, checking for Cpus_allowed_list. Flag all processes bound to a specific CPU using cpu_used[]. This will fail for some exotic binding setups, but is likely good enough in almost @@ -114,20 +139,29 @@ void bind_to_free_cpu(afl_state_t *afl) { } closedir(d); + #elif defined(__FreeBSD__) || defined(__DragonFly__) + struct kinfo_proc *procs; size_t nprocs; size_t proccount; int s_name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL}; size_t s_name_l = sizeof(s_name) / sizeof(s_name[0]); - if (sysctl(s_name, s_name_l, NULL, &nprocs, NULL, 0) != 0) return; + if (sysctl(s_name, s_name_l, NULL, &nprocs, NULL, 0) != 0) { + + if (lockfile[0]) unlink(lockfile); + return; + + } + proccount = nprocs / sizeof(*procs); nprocs = nprocs * 4 / 3; procs = ck_alloc(nprocs); if (sysctl(s_name, s_name_l, procs, &nprocs, NULL, 0) != 0) { + if (lockfile[0]) unlink(lockfile); ck_free(procs); return; @@ -136,6 +170,7 @@ void bind_to_free_cpu(afl_state_t *afl) { for (i = 0; i < proccount; i++) { #if defined(__FreeBSD__) + if (!strcmp(procs[i].ki_comm, "idle")) continue; // fix when ki_oncpu = -1 @@ -145,16 +180,21 @@ void bind_to_free_cpu(afl_state_t *afl) { if (oncpu != -1 && oncpu < sizeof(cpu_used) && procs[i].ki_pctcpu > 60) cpu_used[oncpu] = 1; + #elif defined(__DragonFly__) + if (procs[i].kp_lwp.kl_cpuid < sizeof(cpu_used) && procs[i].kp_lwp.kl_pctcpu > 10) cpu_used[procs[i].kp_lwp.kl_cpuid] = 1; + #endif } ck_free(procs); + #elif defined(__NetBSD__) + struct kinfo_proc2 *procs; size_t nprocs; size_t proccount; @@ -163,13 +203,20 @@ void bind_to_free_cpu(afl_state_t *afl) { CTL_KERN, KERN_PROC2, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), 0}; size_t s_name_l = sizeof(s_name) / sizeof(s_name[0]); - if (sysctl(s_name, s_name_l, NULL, &nprocs, NULL, 0) != 0) return; + if (sysctl(s_name, s_name_l, NULL, &nprocs, NULL, 0) != 0) { + + if (lockfile[0]) unlink(lockfile); + return; + + } + proccount = nprocs / sizeof(struct kinfo_proc2); procs = ck_alloc(nprocs * sizeof(struct kinfo_proc2)); s_name[5] = proccount; if (sysctl(s_name, s_name_l, procs, &nprocs, NULL, 0) != 0) { + if (lockfile[0]) unlink(lockfile); ck_free(procs); return; @@ -183,7 +230,9 @@ void bind_to_free_cpu(afl_state_t *afl) { } ck_free(procs); + #elif defined(__sun) + kstat_named_t *n; kstat_ctl_t * m; kstat_t * k; @@ -198,6 +247,7 @@ void bind_to_free_cpu(afl_state_t *afl) { if (!k) { + if (lockfile[0]) unlink(lockfile); kstat_close(m); return; @@ -205,6 +255,7 @@ void bind_to_free_cpu(afl_state_t *afl) { if (kstat_read(m, k, NULL)) { + if (lockfile[0]) unlink(lockfile); kstat_close(m); return; @@ -220,6 +271,7 @@ void bind_to_free_cpu(afl_state_t *afl) { k = kstat_lookup(m, "cpu_stat", i, NULL); if (kstat_read(m, k, &cs)) { + if (lockfile[0]) unlink(lockfile); kstat_close(m); return; @@ -233,6 +285,7 @@ void bind_to_free_cpu(afl_state_t *afl) { } kstat_close(m); + #else #warning \ "For this platform we do not have free CPU binding code yet. If possible, please supply a PR to https://github.com/AFLplusplus/AFLplusplus" @@ -241,7 +294,9 @@ void bind_to_free_cpu(afl_state_t *afl) { size_t cpu_start = 0; try: + #if !defined(__ANDROID__) + for (i = cpu_start; i < afl->cpu_core_count; i++) { if (!cpu_used[i]) { break; } @@ -251,6 +306,7 @@ void bind_to_free_cpu(afl_state_t *afl) { 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) { @@ -274,18 +330,25 @@ void bind_to_free_cpu(afl_state_t *afl) { 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"); + #endif #if defined(__linux__) + if (sched_setaffinity(0, sizeof(c), &c)) { if (cpu_start == afl->cpu_core_count) { @@ -302,6 +365,7 @@ if (pset_assign(c, i, NULL)) PFATAL("pset_assign failed"); } #elif defined(__FreeBSD__) || defined(__DragonFly__) + if (pthread_setaffinity_np(pthread_self(), sizeof(c), &c)) { if (cpu_start == afl->cpu_core_count) @@ -314,6 +378,7 @@ if (pset_assign(c, i, NULL)) PFATAL("pset_assign failed"); } #elif defined(__NetBSD__) + if (pthread_setaffinity_np(pthread_self(), cpuset_size(c), c)) { if (cpu_start == afl->cpu_core_count) @@ -326,7 +391,9 @@ if (pthread_setaffinity_np(pthread_self(), cpuset_size(c), c)) { } cpuset_destroy(c); + #elif defined(__sun) + if (pset_bind(c, P_PID, getpid(), NULL)) { if (cpu_start == afl->cpu_core_count) @@ -339,11 +406,17 @@ if (pset_bind(c, P_PID, getpid(), NULL)) { } 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 90adc2cb853482ae058a4b09719502ec6c3c22b8 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 9 Jul 2020 15:43:05 +0100 Subject: illumos littlefixes: little typo for cpu binding and even tough gcc plugin less good than LLVM, clang is more buggy on this os. --- gcc_plugin/GNUmakefile | 7 ++++++- src/afl-fuzz-init.c | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src/afl-fuzz-init.c') diff --git a/gcc_plugin/GNUmakefile b/gcc_plugin/GNUmakefile index bf5c53e0..002437cb 100644 --- a/gcc_plugin/GNUmakefile +++ b/gcc_plugin/GNUmakefile @@ -70,9 +70,14 @@ ifeq "$(TEST_MMAP)" "1" endif ifneq "$(shell uname -s)" "Haiku" - LDFLAGS += -lrt + LDFLAGS += -lrt 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 diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index e51b4729..e95ae95f 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -398,7 +398,7 @@ 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("pthread_setaffinity failed to CPU %d, trying next CPU", i); + WARNF("pset_bind failed to CPU %d, trying next CPU", i); cpu_start++; goto try ; -- cgit 1.4.1 From 2ba88dcd8a9829612eb06eb130d688685a76a847 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 23 Jul 2020 14:17:29 +0200 Subject: skip large files, dont bail --- TODO.md | 2 ++ src/afl-fuzz-init.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src/afl-fuzz-init.c') diff --git a/TODO.md b/TODO.md index 2723657a..341f2c78 100644 --- a/TODO.md +++ b/TODO.md @@ -2,6 +2,8 @@ ## Roadmap 2.67+ + - pre_save custom module example to save away test cases + - expand on AFL_LLVM_INSTRUMENT_FILE to also support sancov allowlist format - allow to sync against honggfuzz and libfuzzer - AFL_MAP_SIZE for qemu_mode and unicorn_mode - namespace for targets? e.g. network diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index e95ae95f..609e16ba 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -466,7 +466,7 @@ void read_testcases(afl_state_t *afl) { ACTF("Scanning '%s'...", afl->in_dir); /* We use scandir() + alphasort() rather than readdir() because otherwise, - the ordering of test cases would vary somewhat randomly and would be + the ordering of test cases would vary somewhat randomly and would be difficult to control. */ nl_cnt = scandir(afl->in_dir, &nl, NULL, alphasort); @@ -527,9 +527,10 @@ void read_testcases(afl_state_t *afl) { if (st.st_size > MAX_FILE) { - FATAL("Test case '%s' is too big (%s, limit is %s)", fn2, + 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)); + continue; } -- cgit 1.4.1 From 9cddbc04206bd8d1399e5a5311c98fff5be80731 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 24 Jul 2020 12:26:52 +0200 Subject: add -F option to sync to foreign fuzzer queues --- GNUmakefile | 4 +- README.md | 20 +++--- TODO.md | 2 - docs/Changelog.md | 2 + docs/parallel_fuzzing.md | 14 ++++- include/afl-fuzz.h | 13 ++++ src/afl-fuzz-init.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++ src/afl-fuzz-run.c | 2 + src/afl-fuzz.c | 22 ++++++- 9 files changed, 211 insertions(+), 22 deletions(-) (limited to 'src/afl-fuzz-init.c') diff --git a/GNUmakefile b/GNUmakefile index f44ef95e..ab9144b8 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -455,10 +455,10 @@ code-format: ./.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/*.h ./.custom-format.py -i gcc_plugin/*.cc ./.custom-format.py -i custom_mutators/*/*.c - ./.custom-format.py -i custom_mutators/*/*.h + @#./.custom-format.py -i custom_mutators/*/*.h # destroys input.h :-( ./.custom-format.py -i examples/*/*.c ./.custom-format.py -i examples/*/*.h ./.custom-format.py -i test/*.c diff --git a/README.md b/README.md index 4e83021d..b2f41315 100644 --- a/README.md +++ b/README.md @@ -366,9 +366,9 @@ If you find other good ones, please send them to us :-) ## Power schedules -The power schedules were copied from Marcel Böhme's excellent AFLfast -implementation and expand on the ability to discover new paths and -therefore may increase the code coverage. +The power schedules were copied from Marcel Böhme's AFLfast implementation and +measure differently which queue entries to prefer and therefore may find +different paths faster for large queues. The available schedules are: @@ -382,16 +382,10 @@ The available schedules are: - mmopt (afl++ experimental) - seek (afl++ experimental) -In parallel mode (-M/-S, several instances with the shared queue), we suggest to -run the main node using the explore or fast schedule (-p explore) and the secondary -nodes with a combination of cut-off-exponential (-p coe), exponential (-p fast), -explore (-p explore) and mmopt (-p mmopt) schedules. If a schedule does -not perform well for a target, restart the secondary nodes with a different schedule. - -In single mode, using -p fast is usually slightly more beneficial than the -default explore mode. -(We don't want to change the default behavior of afl, so "fast" has not been -made the default mode). +In parallel mode (-M/-S, several instances with the shared queue), we suggest +to run the main node using the default explore schedule (`-p explore`) and the +secondary nodes with different schedules. If a schedule does not perform well +for a target, restart the secondary nodes with a different schedule. More details can be found in the paper published at the 23rd ACM Conference on Computer and Communications Security [CCS'16](https://www.sigsac.org/ccs/CCS2016/accepted-papers/) diff --git a/TODO.md b/TODO.md index ad3ef83e..ad743b6b 100644 --- a/TODO.md +++ b/TODO.md @@ -3,9 +3,7 @@ ## Roadmap 2.67+ - -i - + foreign fuzzer sync support: scandir with time sort - - pre_save custom module example to save away test cases - expand on AFL_LLVM_INSTRUMENT_FILE to also support sancov allowlist format - - allow to sync against honggfuzz and libfuzzer - AFL_MAP_SIZE for qemu_mode and unicorn_mode - namespace for targets? e.g. network - learn from honggfuzz (mutations, maybe ptrace?) diff --git a/docs/Changelog.md b/docs/Changelog.md index a25cc43c..bec87d65 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -11,6 +11,8 @@ sending a mail to . ### Version ++2.66d (devel) - afl-fuzz: + - added -F option to allow -M main fuzzers to sync to foreign fuzzers, + e.g. honggfuzz or libfuzzer - eliminated CPU affinity race condition for -S/-M runs - llvm_mode: - fixes for laf-intel float splitting (thanks to mark-griffin for diff --git a/docs/parallel_fuzzing.md b/docs/parallel_fuzzing.md index 271f8369..2ab1466c 100644 --- a/docs/parallel_fuzzing.md +++ b/docs/parallel_fuzzing.md @@ -99,7 +99,15 @@ example may be: This is not a concern if you use @@ without -f and let afl-fuzz come up with the file name. -## 3) Multi-system parallelization +## 3) Syncing with non-afl fuzzers or independant instances + +A -M main node can be told with the `-F other_fuzzer_queue_directory` option +to sync results from other fuzzers, e.g. libfuzzer or honggfuzz. + +Only the specified directory will by synced into afl, not subdirectories. +The specified directories do not need to exist yet at the start of afl. + +## 4) Multi-system parallelization The basic operating principle for multi-system parallelization is similar to the mechanism explained in section 2. The key difference is that you need to @@ -176,7 +184,7 @@ It is *not* advisable to skip the synchronization script and run the fuzzers directly on a network filesystem; unexpected latency and unkillable processes in I/O wait state can mess things up. -## 4) Remote monitoring and data collection +## 5) Remote monitoring and data collection You can use screen, nohup, tmux, or something equivalent to run remote instances of afl-fuzz. If you redirect the program's output to a file, it will @@ -200,7 +208,7 @@ Keep in mind that crashing inputs are *not* automatically propagated to the main instance, so you may still want to monitor for crashes fleet-wide from within your synchronization or health checking scripts (see afl-whatsup). -## 5) Asymmetric setups +## 6) Asymmetric setups It is perhaps worth noting that all of the following is permitted: diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index c9f84c61..cf4254ac 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -347,6 +347,13 @@ struct afl_pass_stat { }; +struct foreign_sync { + + u8 * dir; + time_t ctime; + +}; + typedef struct afl_state { /* Position of this state in the global states list */ @@ -574,6 +581,11 @@ typedef struct afl_state { u8 describe_op_buf_256[256]; /* describe_op will use this to return a string up to 256 */ +/* foreign sync */ +#define FOREIGN_SYNCS_MAX 32 + u8 foreign_sync_cnt; + struct foreign_sync foreign_syncs[FOREIGN_SYNCS_MAX]; + #ifdef _AFL_DOCUMENT_MUTATIONS u8 do_document; u32 document_counter; @@ -937,6 +949,7 @@ void fix_up_banner(afl_state_t *, u8 *); 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); /* CmpLog */ diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 609e16ba..65ad0c9f 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -438,6 +438,159 @@ static void shuffle_ptrs(afl_state_t *afl, void **ptrs, u32 cnt) { } +/* Read all testcases from foreign input directories, then queue them for + testing. Called at startup and at sync intervals. + Does not descend into subdirectories! */ + +void read_foreign_testcases(afl_state_t *afl, int first) { + + if (!afl->foreign_sync_cnt) return; + + struct dirent **nl; + s32 nl_cnt; + u32 i, iter; + + u8 val_buf[2][STRINGIFY_VAL_SIZE_MAX]; + + for (iter = 0; iter < afl->foreign_sync_cnt; iter++) { + + if (afl->foreign_syncs[iter].dir != NULL && + afl->foreign_syncs[iter].dir[0] != 0) { + + if (first) ACTF("Scanning '%s'...", afl->foreign_syncs[iter].dir); + time_t ctime_max = 0; + + /* 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->foreign_syncs[iter].dir, &nl, NULL, NULL); + + if (nl_cnt < 0) { + + if (first) { + + WARNF("Unable to open directory '%s'", afl->foreign_syncs[iter].dir); + sleep(1); + + } + + continue; + + } + + if (nl_cnt == 0) { + + if (first) + WARNF("directory %s is currently empty", + afl->foreign_syncs[iter].dir); + continue; + + } + + /* Show stats */ + + snprintf(afl->stage_name_buf, STAGE_BUF_SIZE, "foreign sync %u", iter); + + afl->stage_name = afl->stage_name_buf; + afl->stage_cur = 0; + afl->stage_max = 0; + + for (i = 0; i < nl_cnt; ++i) { + + struct stat st; + + u8 *fn2 = + alloc_printf("%s/%s", afl->foreign_syncs[iter].dir, nl[i]->d_name); + + free(nl[i]); /* not tracked */ + + if (unlikely(lstat(fn2, &st) || access(fn2, R_OK))) { + + if (first) PFATAL("Unable to access '%s'", fn2); + continue; + + } + + /* we detect new files by their ctime */ + if (likely(st.st_ctime <= afl->foreign_syncs[iter].ctime)) { + + ck_free(fn2); + continue; + + } + + /* This also takes care of . and .. */ + + if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn2, "/README.txt")) { + + ck_free(fn2); + continue; + + } + + if (st.st_size > MAX_FILE) { + + 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; + + } + + // lets do not use add_to_queue(afl, fn2, st.st_size, 0); + // as this could add duplicates of the startup input corpus + + int fd = open(fn2, O_RDONLY); + if (fd < 0) { + + ck_free(fn2); + continue; + + } + + u8 fault; + u8 *mem = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + + if (mem == MAP_FAILED) { + + ck_free(fn2); + continue; + + } + + write_to_testcase(afl, mem, st.st_size); + fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); + afl->syncing_party = "foreign"; + afl->queued_imported += + save_if_interesting(afl, mem, st.st_size, fault); + afl->syncing_party = 0; + munmap(mem, st.st_size); + close(fd); + + if (st.st_ctime > ctime_max) ctime_max = st.st_ctime; + + } + + afl->foreign_syncs[iter].ctime = ctime_max; + free(nl); /* not tracked */ + + } + + } + + if (first) { + + afl->last_path_time = 0; + afl->queued_at_start = afl->queued_paths; + + } + +} + /* Read all testcases from the input directory, then queue them for testing. Called at startup. */ @@ -530,6 +683,7 @@ void read_testcases(afl_state_t *afl) { 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; } diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 2a1664e2..6e3be72b 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -612,6 +612,8 @@ void sync_fuzzers(afl_state_t *afl) { } + if (afl->foreign_sync_cnt) read_foreign_testcases(afl, 0); + } /* 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 df2896d2..f03c545d 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -131,10 +131,13 @@ static void usage(afl_state_t *afl, u8 *argv0, int more_help) { "executions.\n\n" "Other stuff:\n" - " -T text - text banner to show on the screen\n" " -M/-S id - distributed mode (see docs/parallel_fuzzing.md)\n" " use -D to force -S secondary to perform deterministic " "fuzzing\n" + " -F path - sync to a foreign fuzzer queue directory (requires " + "-M, can\n" + " be specified up to %u times)\n" + " -T text - text banner to show on the screen\n" " -I command - execute this command/script when a new crash is " "found\n" //" -B bitmap.txt - mutate a specific test case, use the out/fuzz_bitmap @@ -142,7 +145,7 @@ static void usage(afl_state_t *afl, u8 *argv0, int more_help) { " -C - crash exploration mode (the peruvian rabbit thing)\n" " -e ext - file extension for the fuzz test input file (if " "needed)\n\n", - argv0, EXEC_TIMEOUT, MEM_LIMIT); + argv0, EXEC_TIMEOUT, MEM_LIMIT, FOREIGN_SYNCS_MAX); if (more_help > 1) { @@ -403,6 +406,19 @@ int main(int argc, char **argv_orig, char **envp) { afl->use_splicing = 1; break; + case 'F': /* foreign sync dir */ + + 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) + FATAL("Maximum %u entried of -F option can be specified", + FOREIGN_SYNCS_MAX); + afl->foreign_syncs[afl->foreign_sync_cnt].dir = optarg; + afl->foreign_sync_cnt++; + break; + case 'f': /* target file */ if (afl->fsrv.out_file) { FATAL("Multiple -f options not supported"); } @@ -1059,6 +1075,8 @@ int main(int argc, char **argv_orig, char **envp) { setup_cmdline_file(afl, argv + optind); read_testcases(afl); + // read_foreign_testcases(afl, 1); for the moment dont do this + load_auto(afl); pivot_inputs(afl); -- 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/afl-fuzz-init.c') 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 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/afl-fuzz-init.c') 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/afl-fuzz-init.c') 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