Using libFuzzer Mutators
Relevant source files
The following files were used as context for generating this wiki page:Introduction
AFL++ provides a flexible custom mutator interface that allows users to replace or augment its default mutation strategies. One of the available custom mutators is an integration of libFuzzer's mutation engine. This allows afl-fuzz to leverage the mutation algorithms developed for libFuzzer.
This feature is a partial implementation of the libFuzzer engine, primarily intended for experimental use rather than for production fuzzing campaigns. It exposes libFuzzer's LLVMFuzzerMutate function as a custom mutator that can be dynamically loaded by the main afl-fuzz engine. For more general information on custom mutators, see Custom Mutators.
Sources: custom_mutators/libfuzzer/README.md, custom_mutators/README.md:1-21
Building and Usage
Building
The libFuzzer custom mutator can be built into a shared library (.so file) from the custom_mutators/libfuzzer/ directory.
# Navigate to the libfuzzer custom mutator directory
cd custom_mutators/libfuzzer/
# Build the shared library
make
This command compiles the C++ source files (FuzzerMutate.cpp, FuzzerLoop.cpp, etc.) into libfuzzer-mutator.so.
Sources: custom_mutators/libfuzzer/README.md:5-5
Running with afl-fuzz
To use the compiled libFuzzer mutator, you must specify its path using the AFL_CUSTOM_MUTATOR_LIBRARY environment variable when invoking afl-fuzz.
AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/libfuzzer/libfuzzer-mutator.so afl-fuzz [options] -- /path/to/target
You can also choose to use only the custom mutator and disable AFL++'s internal mutators by setting AFL_CUSTOM_MUTATOR_ONLY=1.
Sources: custom_mutators/libfuzzer/README.md:7-7, custom_mutators/README.md:12-14
Architecture and Integration
The libFuzzer mutator integrates with AFL++ as a standard custom mutator shared library. The afl-fuzz executable loads this library at startup and calls its exported functions at different stages of the fuzzing loop.
The following diagram illustrates the high-level integration between afl-fuzz and the libFuzzer custom mutator.
Loading the Mutator
During initialization, afl-fuzz calls the setup_custom_mutators function. This function reads the AFL_CUSTOM_MUTATOR_LIBRARY environment variable, loads the specified shared library using dlopen, and resolves symbols for the various custom mutator hooks (e.g., afl_custom_init, afl_custom_fuzz) using dlsym.
The core of the integration is the afl_custom_fuzz hook, which is the function afl-fuzz will call to perform a mutation. In this integration, afl_custom_fuzz is not directly implemented; instead, the standard LLVMFuzzerMutate function is exported and used by a wrapper inside the AFL++ driver logic when afl_custom_fuzz is not found.
Sources: src/afl-fuzz.c:5143-5169, include/afl-fuzz.h:1020-1035
The LLVMFuzzerMutate Entry Point
The libfuzzer-mutator.so library exports the LLVMFuzzerMutate function, which serves as the bridge between AFL++ and the libFuzzer mutation engine. This function is the standard entry point for libFuzzer-compatible harnesses.
// custom_mutators/libfuzzer/FuzzerLoop.cpp:1108-1119
ATTRIBUTE_INTERFACE size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size,
size_t MaxSize) {
assert(fuzzer::F);
fuzzer::F->GetMD().StartMutationSequence();
size_t r = fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize);
#ifdef INTROSPECTION
introspection_ptr = fuzzer::F->GetMD().WriteMutationSequence();
#endif
return r;
}
This function receives a data buffer from afl-fuzz, invokes the libFuzzer MutationDispatcher to mutate it, and returns the size of the new, mutated data.
Sources: custom_mutators/libfuzzer/FuzzerLoop.cpp:1108-1119, utils/aflpp_driver/aflpp_driver.c:203-211
Core libFuzzer Components
The custom mutator is a self-contained implementation of the core libFuzzer mutation logic.
The Fuzzer and MutationDispatcher Classes
The integration relies on two main classes from the libFuzzer codebase: Fuzzer and MutationDispatcher.
fuzzer::Fuzzer: A global object that holds the overall state, including a reference to theMutationDispatcher.fuzzer::MutationDispatcher: This class is responsible for applying a sequence of random mutations to a given input. It contains a collection of different mutation functions.
The relationship between these classes is shown below.
Mutation Strategies
The MutationDispatcher selects from a wide variety of mutation strategies to alter the input buffer. These are the same strategies used by the standalone libFuzzer engine.
| Mutator Function | Description |
|---|---|
Mutate_ShuffleBytes | Shuffles a random sub-array of bytes. |
Mutate_EraseBytes | Erases a random range of bytes. |
Mutate_InsertByte | Inserts a single random byte. |
Mutate_InsertRepeatedBytes | Inserts a number of identical random bytes. |
Mutate_ChangeByte | Changes a single byte to a random value. |
Mutate_ChangeBit | Flips a single bit. |
Mutate_AddWord | Adds a word from the dictionary (e.g., interesting_8/16/32). |
Mutate_CopyPart | Copies a part of the input to another location. |
Mutate_CrossOver | Splices the input with another input (currently not used in this integration). |
Sources: custom_mutators/libfuzzer/FuzzerMutate.cpp:100-474
Mutation Flow
When afl-fuzz decides to use the custom mutator, it passes the current test case to the LLVMFuzzerMutate function. The function then orchestrates the mutation and returns the result.
This sequence diagram shows the call flow for a single mutation.
Limitations
This integration is a partial port of the libFuzzer engine and has some known limitations:
- No Splicing/Crossover: The crossover functionality, which combines two different inputs, is not implemented.
- No Dictionary Support: The mutator does not make use of user-provided or auto-generated dictionaries.
Sources: custom_mutators/libfuzzer/README.md:9-11
Conclusion
The libFuzzer custom mutator for AFL++ provides an easy way to integrate libFuzzer's mutation engine into an afl-fuzz campaign. While it is primarily for experimental purposes and lacks some of libFuzzer's advanced features like dictionary support and crossover, it allows fuzzing targets with a different set of mutation strategies than AFL++'s default havoc and splice stages. This can be useful for exploring different parts of a target's state space.
Sources: custom_mutators/libfuzzer/README.md, custom_mutators/README.md:19-21