FuzzFactory
Domain-Specific Fuzzing with Waypoints
Install / Use
/learn @rohanpadhye/FuzzFactoryREADME
FuzzFactory: Domain-Specific Fuzzing with Waypoints

FuzzFactory is an extension of AFL that generalizes coverage-guided fuzzing to domain-specific testing goals. FuzzFactory allows users to guide the fuzzer's search process without having to modify anything in AFL's search algorithm.
Most details are described in the following research paper (PDF):
Rohan Padhye, Caroline Lemieux, Koushik Sen, Laurent Simon, and Hayawardh Vijayakumar. 2019. FuzzFactory: Domain-Specific Fuzzing with Waypoints. Proc. ACM Program. Lang. 3, OOPSLA, Article 174 (October 2019), 29 pages. https://doi.org/10.1145/3360600
A replication package for the experimental evaluation described in the paper is available on Zenodo.
A video of the SPLASH 2019 (OOPSLA) talk is available on YouTube:
What are Waypoints?
FuzzFactory's key abstraction is that of waypoints: intermediate inputs that are saved during the fuzzing loop. These inputs need not increase code coverage, but they are saved because they make some sort of domain-specific progress. For example, PerfFuzz saves inputs that increase loop execution counts, a magic-byte fuzzer may save inputs that have partially correct magic bytes, or a directed fuzzer may save inputs that are more likely to exercise a program point of interest.
How does FuzzFactory work?
FuzzFactory exposes an API (see include/waypoints.h) between the fuzzing algorithm and the test program. The test program can provide custom domain-specific feedback from test execution as key-value pairs, and specify how such feedback should be aggregated across multiple inputs by choosing a reducer function. The aggregated feedback is used to decide if a given input should be considered a waypoint. The calls to the API can be injected either by modifying a test program by hand, or by inserting appropriate instrumentation in the test program at compile-time.
Why is FuzzFactory useful?
Here is a cool example of something the authors did with FuzzFactory:
- Built
mem, a fuzzer that generates inputs that maximize arguments tomalloc()in 29 lines of code. - Built
cmp, a fuzzer that surpasses variable-sized magic values, checksums, and other comparisons across integers, strings, and byte buffers, in 355 lines of code. - Composed
cmp+mem, to build a super-fuzzer calledcmp-memthat surpasses comparisons while simultaneously maximizing mallocs, using a single command-line flag. - Used
cmp-memto find two new bugs inlibarchive[#1165, #1237]. Also replicated a known allocation bug in libpng, without using any seed inputs.
The super-fuzzer (cmp-mem) outperforms not only AFL, but also its constituents cmp and mem, on finding these memory allocation issues:
Documentation and Examples
This section assumes some familiarity with AFL.
To build FuzzFactory's custom afl-fuzz, run make in the root project directory.
You can use this afl-fuzz to fuzz regular AFL-instrumented programs as before.
You can also use this afl-fuzz with the -p option to enable fuzzing programs that are instrumented (option 1) or modified (option 2) to call into FuzzFactory's API.
Option 1: Domain-Specific Feedback via LLVM-based instrumentation
To build FuzzFactory's LLVM-based domain-specific instrumentation, run make llvm-domains in the root project directory; you will need LLVM/Clang 6+ installed (AFL needs to find llvm_config). This will build a special version of afl-clang-fast that supports domain-specific instrumentation passes as plugins. This command also builds six domain-specific instrumentation passes that ship with FuzzFactory; these correspond to the six domains listed in the paper: slow, perf, mem, valid, cmp, diff.
FuzzFactory provides an extension mechanism to quickly implement LLVM instrumentation passes that call into the FuzzFactory API; see llvm_mode/fuzzfactory.hpp and the following six domain implementations in llvm_mode:
- Domain
slow(inspired by SlowFuzz):waypoints-slow-pass.cc: Implements domainslowdescribed in Table 3 of the paper.waypoints-slow-rt.c: Allocates DSF map forslow.
- Domain
perf(port of PerfFuzz):waypoints-perf-pass.cc: Implements domainperfdescribed in Table 4 of the paper.waypoints-perf-rt.c: Allocates DSF map forperf.
- Domain
mem(malloc/calloc fuzzer):waypoints-mem-pass.cc: Implements domainmemdescribed in Table 5 of the paper.waypoints-mem-rt.c: Allocates DSF map formem.
- Domain
valid(port of Zest-v):waypoints-valid-pass.cc: Implements domainvaliddescribed in Table 6 of the paper.waypoints-valid-rt.c: Allocates DSF map forslowand defines the logic for when the argument toASSUME()isfalse.
- Domain
cmp(magic-byte and checksum fuzzer):waypoints-cmp-pass.cc: Implements domaincmpdescribed in Table 7 in the paper.waypoints-cmp-rt.c: Allocates DSF map forcmp, as well as defines all thewrapcmpfunctions that perform the common-bit-counting and update the DSF map accordingly.
- Domain
diff(incremental fuzzer):waypoints-diff-pass.cc: Implements domaindiffdescribed in Table 8 in the paper.waypoints-diff-rt.c: Allocates globals used by domaindiff.
Fuzzing with FuzzFactory's LLVM-based domains
This section assumes you have LLVM/Clang installed and have run make llvm-domains in the root directory.
The directory demo contains a single-file test program (demo.c) to illustrate the use of FuzzFactory. Please switch to this directory for the remainder of this section.
cd demo
Background: This is how you would compile demo.c with regular AFL:
../afl-clang-fast demo.c -o demo
This is how you would compile demo.c with FuzzFactory using the mem domain:
WAYPOINTS=mem ../afl-clang-fast demo.c -o demo
This is how you would compile demo.c with FuzzFactory using the cmp domain:
WAYPOINTS=cmp ../afl-clang-fast demo.c -o demo
This is how you would compile demo.c with FuzzFactory using the composition of the cmp and mem domain:
WAYPOINTS=cmp,mem ../afl-clang-fast demo.c -o demo
Now, let's fuzz the demo program using the seed file in the seeds subdirectory. The same command applies regardless of what domain was used to instrument the test program:
../afl-fuzz -p -i seeds -o results ./demo
If you fuzzed a program that has been instrumented with cmp+mem domains, you will see the following in the AFL output before fuzzing starts:
[+] 2 domain-specific front-end configs received
DSF 0: Start=0x000000, End=0x010000, Size=65536, Reducer[0]=MAX, Initial=0
DSF 1: Start=0x010000, End=0x010400, Size=1024, Reducer[0]=MAX, Initial=0
This is an indication that the test program has registered two domain-specific feedback maps with FuzzFactory.
The rest of the fuzzing session is similar to running AFL as usual. Press CTRL+C to stop fuzzing. During fuzzing, the following log file is created with verbose output about domain-specific feedback: results/fuzzfactory.log.
New Domains via LLVM Instrumentation
To implement your own domain-specific instrumentation pass, let's call it domain foo: (1) create files waypoints-foo-pass.cc and waypoints-foo-rt.c in the llvm_mode directory, (2) run make llvm-domains DOMAINS+=foo in the root directory, (3) compile your test programs with afl-clang-fast after setting the environment var WAYPOINTS=foo. For help in creating the pass and runtime files, use the implementations listed in the previous section as templates.
Option 2: Domain-Specific Feedback via Manual API Invocation
FuzzFactory can also be used to manually augment a test program and specify domain-specific testing goals. Simply include waypoints.h and use the following macros from your test program:
/**
* Creates a new DSF map `name` with `size` keys, `reducer` function, and `initial` aggregate value.
*
* To be called at the top-level global scope.
*/
FUZZFACTORY_DSF_NEW(name, size, reducer, initial)
/** Set dsf[k] = max(dsf[k], v); */
FUZZFACTORY_DSF_MAX(dsf, k, v)
/** Set dsf[k] = dsf[k] | v; */
FUZZFACTORY_DSF_BIT(dsf, k, v)
/** Set dsf[k] = v; */
FUZZFACTORY_DSF_SET(dsf, k, v)
/** Set dsf[k] = dsf[k] + v; */
FUZZFACTORY_DSF_INC(dsf, k, v)
Demo with Clang
This section assumes you have LLVM/Clang installed and have run make llvm-domains in the root directory.
To see a sample usage of these macros from a test program, cd to the demo directory and run the following:
diff demo.c demo-manual.c # Compare demo program with fuzzfactory-macro augmented test program
Compile the augmented test program as follows:
../afl-clang-fast demo-manual.c -o demo-manual
Fuzz the augmented test program using the -p option to enable domain-specific fuzzing:
../afl-fuzz -p -i seeds/ -o results ./
Related Skills
gh-issues
349.9kFetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]
oracle
349.9kBest practices for using the oracle CLI (prompt + file bundling, engines, sessions, and file attachment patterns).
taskflow-inbox-triage
349.9kname: taskflow-inbox-triage description: Example TaskFlow authoring pattern for inbox triage. Use when messages need different treatment based on intent, with some routes notifying immediately, some w
taskflow
349.9kname: taskflow description: Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layer
