diff --git a/Makefile b/Makefile index 83cf3ee5..963a8fb7 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ # PROGNAME = afl -VERSION = 2.06b +VERSION = 2.07b PREFIX ?= /usr/local BIN_PATH = $(PREFIX)/bin diff --git a/afl-fuzz.c b/afl-fuzz.c index 1be6ec3a..de841415 100644 --- a/afl-fuzz.c +++ b/afl-fuzz.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,12 @@ # include #endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */ +/* For supporting -Z on systems that have sched_setaffinity. */ + +#ifdef __linux__ +# define HAVE_AFFINITY 1 +#endif /* __linux__ */ + /* A toggle to export some variables when building as a library. Not very useful for the general public. */ @@ -195,6 +202,15 @@ static u64 total_bitmap_size, /* Total bit count for all bitmaps */ static u32 cpu_core_count; /* CPU core count */ +#ifdef HAVE_AFFINITY + +static u8 use_affinity; /* Using -Z */ + +static u32 cpu_aff_main, /* Affinity for main process */ + cpu_aff_child; /* Affinity for fuzzed child */ + +#endif /* HAVE_AFFINITY */ + static FILE* plot_file; /* Gnuplot output file */ struct queue_entry { @@ -324,6 +340,25 @@ static u64 get_cur_time_us(void) { } +#ifdef HAVE_AFFINITY + +/* Set CPU affinity (on systems that support it). */ + +static void set_cpu_affinity(u32 cpu_id) { + + cpu_set_t c; + + CPU_ZERO(&c); + CPU_SET(cpu_id, &c); + + if (sched_setaffinity(0, sizeof(c), &c)) + PFATAL("sched_setaffinity failed"); + +} + +#endif /* HAVE_AFFINITY */ + + /* Generate a random number (from 0 to limit - 1). This may have slight bias. */ @@ -1862,6 +1897,10 @@ EXP_ST void init_forkserver(char** argv) { struct rlimit r; +#ifdef HAVE_AFFINITY + if (use_affinity) set_cpu_affinity(cpu_aff_child); +#endif /* HAVE_AFFINITY */ + /* Umpf. On OpenBSD, the default fd limit for root users is set to soft 128. Let's try to fix that... */ @@ -2160,6 +2199,10 @@ static u8 run_target(char** argv) { struct rlimit r; +#ifdef HAVE_AFFINITY + if (use_affinity) set_cpu_affinity(cpu_aff_child); +#endif /* HAVE_AFFINITY */ + if (mem_limit) { r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20; @@ -6770,6 +6813,9 @@ static void usage(u8* argv0) { " -T text - text banner to show on the screen\n" " -M / -S id - distributed mode (see parallel_fuzzing.txt)\n" +#ifdef HAVE_AFFINITY + " -Z core_id - set CPU affinity (see perf_tips.txt)\n" +#endif /* HAVE_AFFINITY */ " -C - crash exploration mode (the peruvian rabbit thing)\n\n" "For additional tips, please consult %s/README.\n\n", @@ -7113,6 +7159,14 @@ static void get_core_count(void) { } else WARNF("Unable to figure out the number of CPU cores."); +#ifdef HAVE_AFFINITY + + if (use_affinity) + OKF("Using specified CPU affinity: main = %u, child = %u", + cpu_aff_main, cpu_aff_child); + +#endif /* HAVE_AFFINITY */ + } @@ -7404,7 +7458,7 @@ int main(int argc, char** argv) { doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; - while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:Q")) > 0) + while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:QZ:")) > 0) switch (opt) { @@ -7500,6 +7554,35 @@ int main(int argc, char** argv) { break; +#ifdef HAVE_AFFINITY + + case 'Z': { + + s32 i; + + if (use_affinity) FATAL("Multiple -Z options not supported"); + use_affinity = 1; + + cpu_core_count = sysconf(_SC_NPROCESSORS_ONLN); + + i = sscanf(optarg, "%u,%u", &cpu_aff_main, &cpu_aff_child); + + if (i < 1 || cpu_aff_main >= cpu_core_count) + FATAL("Bogus primary core ID passed to -Z (expected 0-%u)", + cpu_core_count - 1); + + if (i == 1) cpu_aff_child = cpu_aff_main; + + if (cpu_aff_child >= cpu_core_count) + FATAL("Bogus secondary core ID passed to -Z (expected 0-%u)", + cpu_core_count - 1); + + break; + + } + +#endif /* HAVE_AFFINITY */ + case 'd': if (skip_deterministic) FATAL("Multiple -d options not supported"); @@ -7565,6 +7648,10 @@ int main(int argc, char** argv) { setup_signal_handlers(); check_asan_opts(); +#ifdef HAVE_AFFINITY + if (use_affinity) set_cpu_affinity(cpu_aff_main); +#endif /* HAVE_AFFINITY */ + if (sync_id) fix_up_sync(); if (!strcmp(in_dir, out_dir)) diff --git a/afl-gotcpu.c b/afl-gotcpu.c index 2578636e..72819cf8 100644 --- a/afl-gotcpu.c +++ b/afl-gotcpu.c @@ -4,7 +4,7 @@ Written and maintained by Michal Zalewski - Copyright 2015 Google Inc. All rights reserved. + 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. @@ -26,10 +26,12 @@ */ #define AFL_MAIN +#define _GNU_SOURCE #include #include #include +#include #include #include @@ -39,6 +41,10 @@ #include "types.h" #include "debug.h" +#ifdef __linux__ +# define HAVE_AFFINITY 1 +#endif /* __linux__ */ + /* Get unix time in microseconds. */ @@ -68,21 +74,14 @@ static u64 get_cpu_usage_us(void) { } -/* Do the benchmark thing. */ +/* Measure preemption rate. */ -int main(int argc, char** argv) { +static u32 measure_preemption(u32 target_ms) { static volatile u32 v1, v2; - s32 loop_repeats = 0, util_perc; u64 st_t, en_t, st_c, en_c, real_delta, slice_delta; - - SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by \n"); - - /* Run a busy loop for CTEST_TARGET_MS. */ - - ACTF("Measuring preemption rate (this will take %0.02f sec)...", - ((double)CTEST_TARGET_MS) / 1000); + s32 loop_repeats = 0; st_t = get_cur_time_us(); st_c = get_cpu_usage_us(); @@ -96,7 +95,7 @@ int main(int argc, char** argv) { en_t = get_cur_time_us(); - if (en_t - st_t < CTEST_TARGET_MS * 1000) { + if (en_t - st_t < target_ms * 1000) { loop_repeats++; goto repeat_loop; } @@ -109,10 +108,104 @@ int main(int argc, char** argv) { real_delta = (en_t - st_t) / 1000; slice_delta = (en_c - st_c) / 1000; - OKF("Busy loop hit %u times, real = %llu ms, slice = %llu ms.", - loop_repeats, real_delta, slice_delta); + return real_delta * 100 / slice_delta; + +} + + +/* Do the benchmark thing. */ + +int main(int argc, char** argv) { + +#ifdef HAVE_AFFINITY + + u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN), + idle_cpus = 0, maybe_cpus = 0, i; + + SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by \n"); + + ACTF("Measuring per-core preemption rate (this will take %0.02f sec)...", + ((double)CTEST_CORE_TRG_MS) * cpu_cnt / 1000); + + for (i = 0; i < cpu_cnt; i++) { + + cpu_set_t c; + u32 util_perc; + + CPU_ZERO(&c); + CPU_SET(i, &c); + + if (sched_setaffinity(0, sizeof(c), &c)) + PFATAL("sched_setaffinity failed"); + + util_perc = measure_preemption(CTEST_CORE_TRG_MS); + + if (util_perc < 105) { + + SAYF(" Core #%u: " cLGN "AVAILABLE\n" cRST, i); + maybe_cpus++; + idle_cpus++; + + } else if (util_perc < 130) { + + SAYF(" Core #%u: " cYEL "CAUTION " cRST "(%u%%)\n", i, util_perc); + maybe_cpus++; + + } else { - util_perc = real_delta * 100 / slice_delta; + SAYF(" Core #%u: " cLRD "OVERBOOKED " cRST "(%u%%)\n" cRST, i, + util_perc); + + } + + } + + SAYF(cGRA "\n>>> "); + + if (idle_cpus) { + + if (maybe_cpus == idle_cpus) { + + SAYF(cLGN "PASS: " cRST "You can run more processes on %u core%s.", + idle_cpus, idle_cpus > 1 ? "s" : ""); + + } else { + + SAYF(cLGN "PASS: " cRST "You can run more processes on %u to %u core%s.", + idle_cpus, maybe_cpus, maybe_cpus > 1 ? "s" : ""); + + } + + SAYF(cGRA " <<<" cRST "\n\n"); + return 0; + + } + + if (maybe_cpus) { + + SAYF(cYEL "CAUTION: " cRST "You may still have %u core%s available.", + maybe_cpus, maybe_cpus > 1 ? "s" : ""); + SAYF(cGRA " <<<" cRST "\n\n"); + return 1; + + } + + SAYF(cLRD "FAIL: " cRST "All cores are overbooked."); + SAYF(cGRA " <<<" cRST "\n\n"); + return 2; + +#else + + u32 util_perc; + + SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by \n"); + + /* Run a busy loop for CTEST_TARGET_MS. */ + + ACTF("Measuring gross preemption rate (this will take %0.02f sec)...", + ((double)CTEST_TARGET_MS) / 1000); + + util_perc = measure_preemption(CTEST_TARGET_MS); /* Deliver the final verdict. */ @@ -137,4 +230,6 @@ int main(int argc, char** argv) { return (util_perc > 105) + (util_perc > 130); +#endif /* ^HAVE_AFFINITY */ + } diff --git a/config.h b/config.h index 4405974a..036bb6c7 100644 --- a/config.h +++ b/config.h @@ -321,6 +321,7 @@ /* Constants for afl-gotcpu to control busy loop timing: */ #define CTEST_TARGET_MS 5000 +#define CTEST_CORE_TRG_MS 1000 #define CTEST_BUSY_CYCLES (10 * 1000 * 1000) /* Uncomment this to use inferior block-coverage-based instrumentation. Note diff --git a/docs/ChangeLog b/docs/ChangeLog index a5431874..08101682 100644 --- a/docs/ChangeLog +++ b/docs/ChangeLog @@ -13,9 +13,22 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . Not sure if you should upgrade? The lowest currently recommended version -is 2.03b. If you're stuck on an earlier release, it's strongly advisable +is 2.07b. If you're stuck on an earlier release, it's strongly advisable to get on with the times. +-------------- +Version 2.07b: +-------------- + + - Added CPU affinity option (-Z) on Linux. With some caution, this can + offer a significant (10%+) performance bump and reduce jitter. + Proposed by Austin Seipp. + + - Updated afl-gotcpu to use CPU affinity where supported. + + - Fixed confusing CPU_TARGET error messages with QEMU build. Spotted by + Daniel Komaromy and others. + -------------- Version 2.06b: -------------- diff --git a/docs/README b/docs/README index 3789d707..a84561c7 100644 --- a/docs/README +++ b/docs/README @@ -434,6 +434,7 @@ bug reports, or patches from: Jeremy Barnes Jeff Trull Guillaume Endignoux ilovezfs Daniel Godas-Lopez Franjo Ivancic + Austin Seipp Daniel Komaromy Thank you! diff --git a/docs/perf_tips.txt b/docs/perf_tips.txt index e7a8b13b..e05401d7 100644 --- a/docs/perf_tips.txt +++ b/docs/perf_tips.txt @@ -144,7 +144,29 @@ a fair amount of time allocating and initializing megabytes of memory when presented with pathological inputs. Low -m values can make them give up sooner and not waste CPU time. -8) Check OS configuration +8) Set CPU core affinity for AFL +-------------------------------- + +Making sure that the fuzzer always runs on the same (idle) CPU core can offer +a significant speed bump and reduce scheduler jitter. The benefits can be even +more striking on true multiprocessor systems. + +On Linux, you can assign the fuzzer to a specific core by first running +afl-gotcpu to see which cores are idle, and then specifying the ID of a +preferred core via -Z, like so: + + $ ./afl-fuzz -Z core_id [...other parameters...] + +Note that this parameter needs to be used with care; accidentally forcing +multiple fuzzers to share the same core may result in performance that is +worse than what you would get without -Z. + +(It is also possible to specify two comma-delimited values for -Z, in which +case, the fuzzer will run on one designated core, and the target binary will +be banished to another. This can sometimes offer minor benefits, but isn't +recommended for general use.) + +9) Check OS configuration ------------------------- There are several OS-level factors that may affect fuzzing speed: @@ -178,8 +200,8 @@ There are several OS-level factors that may affect fuzzing speed: SCHED_RR - can usually speed things up, too, but needs to be done with care. -9) If all other options fail, use -d ------------------------------------- +10) If all other options fail, use -d +------------------------------------- For programs that are genuinely slow, in cases where you really can't escape using huge input files, or when you simply want to get quick and dirty results diff --git a/qemu_mode/build_qemu_support.sh b/qemu_mode/build_qemu_support.sh index 058b9637..7224671e 100755 --- a/qemu_mode/build_qemu_support.sh +++ b/qemu_mode/build_qemu_support.sh @@ -6,7 +6,7 @@ # Written by Andrew Griffiths and # Michal Zalewski # -# Copyright 2015 Google Inc. All rights reserved. +# 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. @@ -124,6 +124,8 @@ patch -p0 /dev/null || exit 1 + cd .. -gcc test-instr.c -o test-instr || exit 1 + make >/dev/null || exit 1 -unset AFL_INST_RATIO + gcc test-instr.c -o test-instr || exit 1 -echo 0 | ./afl-showmap -m none -Q -q -o .test-instr0 ./test-instr || exit 1 -echo 1 | ./afl-showmap -m none -Q -q -o .test-instr1 ./test-instr || exit 1 + unset AFL_INST_RATIO -rm -f test-instr + echo 0 | ./afl-showmap -m none -Q -q -o .test-instr0 ./test-instr || exit 1 + echo 1 | ./afl-showmap -m none -Q -q -o .test-instr1 ./test-instr || exit 1 -cmp -s .test-instr0 .test-instr1 -DR="$?" + rm -f test-instr -rm -f .test-instr0 .test-instr1 + cmp -s .test-instr0 .test-instr1 + DR="$?" -if [ "$DR" = "0" ]; then + rm -f .test-instr0 .test-instr1 - echo "[-] Error: afl-qemu-trace instrumentation doesn't seem to work!" - exit 1 + if [ "$DR" = "0" ]; then -fi + echo "[-] Error: afl-qemu-trace instrumentation doesn't seem to work!" + exit 1 -echo "[+] Instrumentation tests passed. " + fi -echo "[+] All set, you can now use the -Q mode in afl-fuzz!" + echo "[+] Instrumentation tests passed. " + echo "[+] All set, you can now use the -Q mode in afl-fuzz!" + +else + + echo "[!] Note: can't test instrumentation when CPU_TARGET set." + echo "[+] All set, you can now (hopefully) use the -Q mode in afl-fuzz!" + +fi exit 0