Symbolic Execution Mutator (SymCC)
Relevant source files
The following files were used as context for generating this wiki page:The SymCC custom mutator for AFL++ is a proof-of-concept integration that uses the SymCC symbolic execution engine to generate new inputs capable of reaching new program paths. It functions as an external mutator loaded via the AFL_CUSTOM_MUTATOR_LIBRARY environment variable.
This mutator works by taking an input from AFL++, feeding it to a version of the target binary that has been instrumented by SymCC, and then collecting any new inputs generated by SymCC's concolic execution. It is noted that this is an example implementation and that other tools, such as the SymQEMU custom mutator or the native fuzzing helpers of SymCC, may be more effective.
Sources: custom_mutators/symcc/README.md, custom_mutators/symcc/symcc.c
Setup and Usage
To use the SymCC custom mutator, you must first build SymCC and use it to instrument the target binary. Afterward, the mutator itself can be built and used with afl-fuzz.
Sources: custom_mutators/symcc/README.md
Build Process
- Build SymCC and Instrument Target: Follow the instructions in the SymCC repository to build the symbolic execution engine and compile your target program with it.
- Build the Custom Mutator: Navigate to the
custom_mutators/symcc/directory within the AFL++ project and runmake. This will produce the shared librarysymcc-mutator.so.
Sources: custom_mutators/symcc/README.md
Configuration
The mutator is configured using environment variables when launching afl-fuzz.
| Environment Variable | Description | Example |
|---|---|---|
AFL_CUSTOM_MUTATOR_LIBRARY | Path to the compiled custom mutator shared library. | custom_mutators/symcc/symcc-mutator.so |
SYMCC_TARGET | Path to the target binary that was compiled with SymCC. | /path/to/symcc/compiled/target |
Sources: custom_mutators/symcc/README.md, src/afl-fuzz.c:4012-4043
Running with afl-fuzz
Once the setup is complete, you can run afl-fuzz with the custom mutator enabled.
SYMCC_TARGET=/path/to/symcc/compiled/target \
AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/symcc/symcc-mutator.so \
afl-fuzz -i in -o out -- /path/to/normal/fuzz/target
Sources: custom_mutators/symcc/README.md
Architecture and Core Logic
The SymCC mutator acts as a bridge between the AFL++ fuzzer and the SymCC symbolic execution engine. It intercepts the fuzzing process to invoke SymCC on the current test case and feeds any resulting path-finding inputs back to AFL++.
Sources: custom_mutators/symcc/symcc.c
Data Flow
The following diagram illustrates the workflow within the afl_custom_fuzz function each time it is called by afl-fuzz.
This diagram shows the process of executing the SymCC-instrumented binary and retrieving new test cases.
Sources: custom_mutators/symcc/symcc.c:104-162
Key Data Structures
The mutator's state is managed by the my_mutator_t struct.
typedef struct my_mutator {
afl_state_t *afl;
char *symcc_target;
char *tmp_dir;
char *out_dir;
u8 *mutated_out;
} my_mutator_t;
afl: A pointer to the main AFL++ state.symcc_target: Path to the SymCC-instrumented binary, from theSYMCC_TARGETenvironment variable.tmp_dir: Path to a temporary directory for SymCC to use.out_dir: Path to the output directory withintmp_dirwhere SymCC places generated inputs.mutated_out: A buffer to hold the new input read from SymCC's output.
Sources: custom_mutators/symcc/symcc.c:32-41
API Implementation
The mutator implements the standard AFL++ custom mutator API.
Sources: src/afl-fuzz-mutators.c:147-285
afl_custom_init
This function is called once when afl-fuzz starts.
- It allocates the
my_mutator_tstate structure. - It retrieves the path to the SymCC-instrumented target from the
SYMCC_TARGETenvironment variable. If not set, it aborts. - It creates a temporary directory (e.g.,
/tmp/symcc_mutator.XXXXXX) for SymCC to write its outputs. - It allocates a buffer (
mutated_out) to store the generated test cases.
Sources: custom_mutators/symcc/symcc.c:48-81
afl_custom_fuzz
This is the core function, called by afl-fuzz to generate a new test case.
- It writes the input buffer provided by
afl-fuzzto a temporary file (cur_input). - It constructs and executes a command to run the
symcc_targetwith the temporary input file. The output directory for SymCC is also specified.// custom_mutators/symcc/symcc.c:119-124 snprintf(cmd, sizeof(cmd), "mkdir -p %s; %s %s >/dev/null 2>&1", data->out_dir, data->symcc_target, fn); if (system(cmd)) { WARNF("system() failed"); } - It then scans the
data->out_dirfor any files generated by SymCC. - If a new file is found, it is read into the
*out_bufprovided byafl-fuzz, and its size is returned. - If no new files are found, it returns
0, signaling toafl-fuzzthat no mutation was produced and that AFL++ should proceed with its internal mutation strategies.
Sources: custom_mutators/symcc/symcc.c:104-162
afl_custom_deinit
This function is called when afl-fuzz exits. It cleans up by recursively removing the temporary directory created during initialization, ensuring no artifacts are left behind.
Sources: custom_mutators/symcc/symcc.c:83-93
Comparison with SymQEMU Mutator
The SymCC mutator's README.md explicitly states that the symqemu custom mutator is a better alternative. The SymQEMU mutator also uses symbolic execution but is integrated via SymQEMU, a symbolic execution engine built on QEMU. This suggests a tighter and potentially more efficient integration compared to the system() call approach used by the SymCC mutator.
The SymQEMU mutator also offers more advanced configuration options, such as SYMQEMU_ALL to control which queue items are solved and SYMQEMU_LATE to delay concolic solving until the fuzzer has been running for a while without new finds.
Sources: custom_mutators/symcc/README.md:9-10, custom_mutators/symqemu/README.md
Conclusion
The SymCC custom mutator serves as a functional proof-of-concept for integrating an external symbolic execution engine with AFL++. It demonstrates a simple, file-based mechanism for passing data between the fuzzer and the symbolic execution engine. While it can successfully leverage SymCC to discover new paths, it is presented as a basic example, with the more advanced symqemu custom mutator being the recommended choice for users seeking to combine fuzzing with symbolic execution in AFL++.
Sources: custom_mutators/symcc/README.md, custom_mutators/symqemu/README.md