CmpLog: Solving Magic Bytes
Relevant source files
The following files were used as context for generating this wiki page:CmpLog is a highly effective instrumentation-assisted feature in AFL++ designed to solve comparison-based checks in target programs, often referred to as "magic bytes" or "magic values". It is based on the concept of Redqueen. When a standard fuzzer encounters a check like if (input == 0xDEADBEEF), it is statistically improbable that random mutations will guess the correct value. CmpLog addresses this by instrumenting comparison instructions, logging the constant values involved, and then intelligently using this information to craft new inputs that are more likely to pass these checks, thereby unlocking new code paths.
This feature is supported across various instrumentation modes, including LLVM, GCC, QEMU, and Frida, making it a versatile tool for overcoming common fuzzing roadblocks. It operates by running the target in a special mode to log comparison operands to a shared memory map, which afl-fuzz then analyzes to guide its mutation strategies.
Sources: instrumentation/README.cmplog.md, qemu_mode/README.md:103-110, instrumentation/README.llvm.md:104-106
Architecture and Core Concepts
The CmpLog system operates in two main phases: logging and solving. It uses a dedicated forkserver and a shared memory region to communicate comparison data from the instrumented target back to the main afl-fuzz process.
Shared Memory Layout
CmpLog utilizes a shared memory structure, struct cmp_map, to log operand data from instrumented comparisons. This map is indexed by the callsite location of the comparison.
headers: An array ofstruct cmp_headerthat stores metadata for each logged comparison, such as the number of hits and the shape (size) of the operands.log: A 2D array ofstruct cmp_operandsthat stores the actual operand values (v0,v1) for each comparison.
Sources: include/cmplog.h:48-52
The structure of the CmpLog shared memory map is visualized below.
Diagram showing the layout of the cmp_map struct in shared memory.
Sources: include/cmplog.h:48-52
Execution Flow
When CmpLog is enabled, afl-fuzz spawns a secondary forkserver, cmplog_fsrv, specifically for running the target in logging mode. The main fuzzer then enters a dedicated CmpLog stage for certain queue entries.
The general flow is as follows:
afl-fuzzselects a testcase from the queue for the CmpLog stage.- The target is executed via
cmplog_fsrvwith the selected testcase. - The instrumented target executes, and on encountering a comparison, it logs the operands into the
cmp_mapshared memory. afl-fuzzreads thecmp_mapto identify which input bytes influence which comparisons (a process called "colorization").afl-fuzzapplies various solving strategies (e.g., arithmetic, transformational) to the "colored" bytes to generate new inputs.- These new inputs are executed through the normal forkserver to see if they yield new coverage.
Sources: src/afl-fuzz.c:2010-2046, src/afl-fuzz-cmplog.c
This sequence diagram illustrates the interaction between the main fuzzer and the CmpLog forkserver.
Sequence of events during a CmpLog execution cycle.
Sources: src/afl-fuzz-cmplog.c, src/afl-fuzz.c:2010-2046
Enabling and Configuration
CmpLog is enabled and configured through command-line arguments to afl-fuzz and can be further tuned with compile-time definitions.
Command-Line Options
| Option | Argument | Description |
|---|---|---|
-c | path/to/binary or 0 | Enables CmpLog. If the target is not compiled for CmpLog, a separate CmpLog-enabled binary must be provided. Use 0 if the main fuzzing target is already CmpLog-instrumented (e.g., in QEMU/Frida mode). Use - to disable. |
-l | opts | A string of characters to configure CmpLog's behavior. |
Sources: src/afl-fuzz.c:564-577, src/afl-fuzz.c:926-973
The -l option accepts the following characters to fine-tune the CmpLog stage:
| Character | Description |
|---|---|
1 | Sets CmpLog level to 1 (small files). |
2 | Sets CmpLog level to 2 (larger files, default). |
3 | Sets CmpLog level to 3 (all files). This also disables trimming. |
A | Enables arithmetic solving. |
T | Enables transformational solving. |
X | Enables extreme transformational solving. |
R | Enables random colorization bytes. |
Sources: src/afl-fuzz.c:926-973
Environment Variables
| Variable | Description |
|---|---|
AFL_CMPLOG_ONLY_NEW | If set, CmpLog will not be run on the initial testcases from the input directory. This is useful for speeding up resumption of a fuzzing session. |
Sources: src/afl-fuzz.c:378
Compile-Time Configuration
Several parameters for CmpLog can be tuned at compile time by editing config.h.
| Macro | Default Value | Description |
|---|---|---|
CMPLOG_CORPUS_PERCENT | 5U | The minimum percentage of the corpus to perform CmpLog on. |
CMPLOG_POSITIONS_MAX | 12288 | The number of potential positions from which to decide if CmpLog becomes useless. |
CMPLOG_FAIL_MAX | 96 | The maximum allowed number of failed mutation attempts per logged CMP value before giving up. |
Sources: config.h:87-96
The CmpLog Stage: Colorization and Solving
The core of CmpLog's effectiveness lies in its dedicated fuzzing stage, managed by fuzz_one_cmplog in src/afl-fuzz-cmplog.c. This stage performs "colorization" to map input bytes to specific comparisons and then applies solving strategies.
Colorization
Colorization is the process of discovering which bytes in an input file affect the operands of a logged comparison.
- The original input is executed, and the initial
cmp_mapis stored. - The fuzzer iterates through the input, flipping one byte at a time.
- After each flip, the target is re-executed.
- The new
cmp_mapis compared to the original. If a logged operand changes, the flipped byte is "colored" as influencing that specific comparison. - This creates a
taintmap, linking input offsets to the comparisons they affect.
Sources: src/afl-fuzz-cmplog.c:119-195
Solving Strategies
Once colorization is complete, afl-fuzz applies several strategies to the tainted bytes to guess the correct "magic" values.
- Insertion (
colorization_solve_insertion): The logged constant operand is directly inserted into the input at the tainted offset. This is the simplest and often most effective strategy. - Arithmetic (
colorization_solve_arithmetic): If enabled (-lA), this strategy performs simple arithmetic operations (e.g.,+1,-1, byte-swapping) on the logged operand before inserting it. This helps solve checks likeif (input + 1 == value). - Transformation (
colorization_solve_transform): If enabled (-lT), this applies a series of more complex, predefined transformations to the logged values. This can help with simple encodings or data manipulations.
The flowchart below outlines the logic of the CmpLog stage.
High-level logic of the fuzz_one_cmplog function.
Sources: src/afl-fuzz-cmplog.c
Data Structures
The CmpLog feature relies on a set of precise data structures defined in include/cmplog.h to pass information from the instrumented target to the fuzzer.
CmpLog data structures and their relationships.
Sources: include/cmplog.h:26-79
struct cmp_header
This structure contains metadata for each logged comparison at a specific program location.
| Field | Bits | Description |
|---|---|---|
hits | 6 | The number of times this comparison was hit (up to 63). |
shape | 5 | The size of the operands in bytes (e.g., 1, 2, 4, 8). |
type | 1 | The type of comparison: CMP_TYPE_INS (instruction) or CMP_TYPE_RTN (function call like memcmp). |
attribute | 4 | The type of arithmetic comparison (e.g., equal, not equal, greater than). |
Sources: include/cmplog.h:32-38
struct cmp_operands and struct cmpfn_operands
These structures hold the values of the operands being compared.
struct cmp_operandsis used for integer comparisons of various sizes (up to 256-bit, though not fully supported by compilers).struct cmpfn_operandsis used for logging string/memory comparison functions likestrcmpandmemcmp, storing up to 32 bytes of each operand.
Sources: include/cmplog.h:40-58, include/cmplog.h:60-69
Conclusion
CmpLog is a powerful extension to AFL++ that systematically overcomes one of the most significant challenges in fuzzing: solving for specific "magic" values. By instrumenting comparisons, logging operands, and applying intelligent solving strategies, it can automatically discover inputs that pass complex validation checks. This allows the fuzzer to explore deeper code paths that would otherwise remain unreachable, significantly increasing the effectiveness and coverage of a fuzzing campaign. Its integration across multiple instrumentation backends makes it a fundamental and widely applicable feature of the AFL++ ecosystem.
Sources: instrumentation/README.cmplog.md