$npx -y skills add trailofbits/skills --skill libaflLibAFL is a modular fuzzing library for building custom fuzzers. Use for advanced fuzzing needs, custom mutators, or non-standard fuzzing targets.
| 1 | # LibAFL |
| 2 | |
| 3 | LibAFL is a modular fuzzing library that implements features from AFL-based fuzzers like AFL++. Unlike traditional fuzzers, LibAFL provides all functionality in a modular and customizable way as a Rust library. It can be used as a drop-in replacement for libFuzzer or as a library to build custom fuzzers from scratch. |
| 4 | |
| 5 | ## When to Use |
| 6 | |
| 7 | | Fuzzer | Best For | Complexity | |
| 8 | |--------|----------|------------| |
| 9 | | libFuzzer | Quick setup, single-threaded | Low | |
| 10 | | AFL++ | Multi-core, general purpose | Medium | |
| 11 | | LibAFL | Custom fuzzers, advanced features, research | High | |
| 12 | |
| 13 | **Choose LibAFL when:** |
| 14 | - You need custom mutation strategies or feedback mechanisms |
| 15 | - Standard fuzzers don't support your target architecture |
| 16 | - You want to implement novel fuzzing techniques |
| 17 | - You need fine-grained control over fuzzing components |
| 18 | - You're conducting fuzzing research |
| 19 | |
| 20 | ## Quick Start |
| 21 | |
| 22 | LibAFL can be used as a drop-in replacement for libFuzzer with minimal setup: |
| 23 | |
| 24 | ```c++ |
| 25 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
| 26 | // Call your code with fuzzer-provided data |
| 27 | my_function(data, size); |
| 28 | return 0; |
| 29 | } |
| 30 | ``` |
| 31 | |
| 32 | Build LibAFL's libFuzzer compatibility layer: |
| 33 | ```bash |
| 34 | git clone https://github.com/AFLplusplus/LibAFL |
| 35 | cd LibAFL/libafl_libfuzzer_runtime |
| 36 | ./build.sh |
| 37 | ``` |
| 38 | |
| 39 | Compile and run: |
| 40 | ```bash |
| 41 | clang++ -DNO_MAIN -g -O2 -fsanitize=fuzzer-no-link libFuzzer.a harness.cc main.cc -o fuzz |
| 42 | ./fuzz corpus/ |
| 43 | ``` |
| 44 | |
| 45 | ## Installation |
| 46 | |
| 47 | ### Prerequisites |
| 48 | |
| 49 | - Clang/LLVM 15-18 |
| 50 | - Rust (via rustup) |
| 51 | - Additional system dependencies |
| 52 | |
| 53 | ### Linux/macOS |
| 54 | |
| 55 | Install Clang: |
| 56 | ```bash |
| 57 | apt install clang |
| 58 | ``` |
| 59 | |
| 60 | Or install a specific version via apt.llvm.org: |
| 61 | ```bash |
| 62 | wget https://apt.llvm.org/llvm.sh |
| 63 | chmod +x llvm.sh |
| 64 | sudo ./llvm.sh 15 |
| 65 | ``` |
| 66 | |
| 67 | Configure environment for Rust: |
| 68 | ```bash |
| 69 | export RUSTFLAGS="-C linker=/usr/bin/clang-15" |
| 70 | export CC="clang-15" |
| 71 | export CXX="clang++-15" |
| 72 | ``` |
| 73 | |
| 74 | Install Rust: |
| 75 | ```bash |
| 76 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh |
| 77 | ``` |
| 78 | |
| 79 | Install additional dependencies: |
| 80 | ```bash |
| 81 | apt install libssl-dev pkg-config |
| 82 | ``` |
| 83 | |
| 84 | For libFuzzer compatibility mode, install nightly Rust: |
| 85 | ```bash |
| 86 | rustup toolchain install nightly --component llvm-tools |
| 87 | ``` |
| 88 | |
| 89 | ### Verification |
| 90 | |
| 91 | Build LibAFL to verify installation: |
| 92 | ```bash |
| 93 | cd LibAFL/libafl_libfuzzer_runtime |
| 94 | ./build.sh |
| 95 | # Should produce libFuzzer.a |
| 96 | ``` |
| 97 | |
| 98 | ## Writing a Harness |
| 99 | |
| 100 | LibAFL harnesses follow the same pattern as libFuzzer when using drop-in replacement mode: |
| 101 | |
| 102 | ```c++ |
| 103 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
| 104 | // Your fuzzing target code here |
| 105 | return 0; |
| 106 | } |
| 107 | ``` |
| 108 | |
| 109 | When building custom fuzzers with LibAFL as a Rust library, harness logic is integrated directly into the fuzzer. See the "Writing a Custom Fuzzer" section below for the full pattern. |
| 110 | |
| 111 | > **See Also:** For detailed harness writing techniques, see the **harness-writing** technique skill. |
| 112 | |
| 113 | ## Usage Modes |
| 114 | |
| 115 | LibAFL supports two primary usage modes: |
| 116 | |
| 117 | ### 1. libFuzzer Drop-in Replacement |
| 118 | |
| 119 | Use LibAFL as a replacement for libFuzzer with existing harnesses. |
| 120 | |
| 121 | **Compilation:** |
| 122 | ```bash |
| 123 | clang++ -DNO_MAIN -g -O2 -fsanitize=fuzzer-no-link libFuzzer.a harness.cc main.cc -o fuzz |
| 124 | ``` |
| 125 | |
| 126 | **Running:** |
| 127 | ```bash |
| 128 | ./fuzz corpus/ |
| 129 | ``` |
| 130 | |
| 131 | **Recommended for long campaigns:** |
| 132 | ```bash |
| 133 | ./fuzz -fork=1 -ignore_crashes=1 corpus/ |
| 134 | ``` |
| 135 | |
| 136 | ### 2. Custom Fuzzer as Rust Library |
| 137 | |
| 138 | Build a fully customized fuzzer using LibAFL components. |
| 139 | |
| 140 | **Create project:** |
| 141 | ```bash |
| 142 | cargo init --lib my_fuzzer |
| 143 | cd my_fuzzer |
| 144 | cargo add libafl@0.13 libafl_targets@0.13 libafl_bolts@0.13 libafl_cc@0.13 \ |
| 145 | --features "libafl_targets@0.13/libfuzzer,libafl_targets@0.13/sancov_pcguard_hitcounts" |
| 146 | ``` |
| 147 | |
| 148 | **Configure Cargo.toml:** |
| 149 | ```toml |
| 150 | [lib] |
| 151 | crate-type = ["staticlib"] |
| 152 | ``` |
| 153 | |
| 154 | ## Writing a Custom Fuzzer |
| 155 | |
| 156 | > **See Also:** For detailed harness writing techniques, patterns for handling complex inputs, |
| 157 | > and advanced strategies, see the **fuzz-harness-writing** technique skill. |
| 158 | |
| 159 | ### Fuzzer Components |
| 160 | |
| 161 | A LibAFL fuzzer consists of modular components: |
| 162 | |
| 163 | 1. **Observers** - Collect execution feedback (coverage, timing) |
| 164 | 2. **Feedback** - Determine if inputs are interesting |
| 165 | 3. **Objective** - Define fuzzing goals (crashes, timeouts) |
| 166 | 4. **State** - Maintain corpus and metadata |
| 167 | 5. **Mutators** - Generate new inputs |
| 168 | 6. **Scheduler** - Select which inputs to mutate |
| 169 | 7. **Executor** - Run the target with inputs |
| 170 | |
| 171 | ### Basic Fuzzer Structure |
| 172 | |
| 173 | ```rust |
| 174 | use libafl::prelude::*; |
| 175 | use libafl_bolts::prelude::*; |
| 176 | use libafl_targets::{libfuzzer_test_one_input, std_edges_map_observer}; |
| 177 | |
| 178 | #[no_mangle] |
| 179 | pub extern "C" fn libafl_main() { |
| 180 | let mut run_client = |state: Option<_>, mut restarting_mgr, _core_id| { |
| 181 | // 1. Setup observers |
| 182 | let edges_observer = HitcountsMapObserver::new( |
| 183 | unsafe { std_edges_map_observer("edges") } |
| 184 | ).track_indices(); |
| 185 | let time_observer = TimeObserver::new( |