1.. _module-pw_fuzzer-guides-using_libfuzzer: 2 3========================================= 4pw_fuzzer: Adding Fuzzers Using LibFuzzer 5========================================= 6.. pigweed-module-subpage:: 7 :name: pw_fuzzer 8 9.. note:: 10 11 `libFuzzer`_ is currently only supported on Linux and MacOS using clang. 12 13.. _module-pw_fuzzer-guides-using_libfuzzer-toolchain: 14 15----------------------------------------- 16Step 0: Set up libFuzzer for your project 17----------------------------------------- 18.. note:: 19 20 This workflow only needs to be done once for a project. 21 22`libFuzzer`_ is a LLVM compiler runtime and should included with your ``clang`` 23installation. In order to use it, you only need to define a suitable toolchain. 24 25.. tab-set:: 26 27 .. tab-item:: GN 28 :sync: gn 29 30 Use ``pw_toolchain_host_clang``, or derive a new toolchain from it. 31 For example: 32 33 .. code-block:: 34 35 import("$dir_pw_toolchain/host/target_toolchains.gni") 36 37 my_toolchains = { 38 ... 39 clang_fuzz = { 40 name = "my_clang_fuzz" 41 forward_variables_from(pw_toolchain_host.clang_fuzz, "*", ["name"]) 42 } 43 ... 44 } 45 46 .. tab-item:: CMake 47 :sync: cmake 48 49 LibFuzzer-style fuzzers are not currently supported by Pigweed when using 50 CMake. 51 52 .. tab-item:: Bazel 53 :sync: bazel 54 55 Include ``rules_fuzzing`` in your ``MODULE.bazel`` file. For example: 56 57 .. code-block:: 58 59 bazel_dep(name = "rules_fuzzing", version = "0.5.2") 60 61 Then, import the libFuzzer build configurations in your ``.bazelrc`` file 62 by adding and adapting the following: 63 64 .. code-block:: 65 66 # Include FuzzTest build configurations. 67 import %workspace%/path/to/pigweed/pw_fuzzer/libfuzzer.bazelrc 68 69------------------------------------ 70Step 1: Write a fuzz target function 71------------------------------------ 72To write a fuzzer, a developer needs to write a `fuzz target function`_ 73following the guidelines given by libFuzzer: 74 75.. code-block:: cpp 76 77 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { 78 DoSomethingInterestingWithMyAPI(data, size); 79 return 0; // Non-zero return values are reserved for future use. 80 } 81 82When writing your fuzz target function, you may want to consider: 83 84- It is acceptable to return early if the input doesn't meet some constraints, 85 e.g. it is too short. 86- If your fuzzer accepts data with a well-defined format, you can bootstrap 87 coverage by crafting examples and adding them to a `corpus`_. 88- There are tools to `split a fuzzing input`_ into multiple fields if needed; 89 the `FuzzedDataProvider`_ is particularly easy to use. 90- If your code acts on "transformed" inputs, such as encoded or compressed 91 inputs, you may want to try `structure aware fuzzing`. 92- You can do `startup initialization`_ if you need to. 93- If your code is non-deterministic or uses checksums, you may want to disable 94 those **only** when fuzzing by using LLVM's 95 `FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION`_ 96 97------------------------------------ 98Step 2: Add the fuzzer to your build 99------------------------------------ 100To build a fuzzer, do the following: 101 102.. tab-set:: 103 104 .. tab-item:: GN 105 :sync: gn 106 107 Add the GN target to the module using ``pw_fuzzer`` GN template. If you 108 wish to limit when the generated unit test is run, you can set 109 ``enable_test_if`` in the same manner as ``enable_if`` for `pw_test`: 110 111 .. code-block:: 112 113 # In $dir_my_module/BUILD.gn 114 import("$dir_pw_fuzzer/fuzzer.gni") 115 116 pw_fuzzer("my_fuzzer") { 117 sources = [ "my_fuzzer.cc" ] 118 deps = [ ":my_lib" ] 119 enable_test_if = device_has_1m_flash 120 } 121 122 Add the fuzzer GN target to the module's group of fuzzers. Create this 123 group if it does not exist. 124 125 .. code-block:: 126 127 # In $dir_my_module/BUILD.gn 128 group("fuzzers") { 129 deps = [ 130 ... 131 ":my_fuzzer", 132 ] 133 } 134 135 Make sure this group is referenced from a top-level ``fuzzers`` target in 136 your project, with the appropriate 137 :ref:`fuzzing toolchain<module-pw_fuzzer-guides-using_libfuzzer-toolchain>`. 138 For example: 139 140 .. code-block:: 141 142 # In //BUILD.gn 143 group("fuzzers") { 144 deps = [ 145 ... 146 "$dir_my_module:fuzzers(//my_toolchains:host_clang_fuzz)", 147 ] 148 } 149 150 .. tab-item:: CMake 151 :sync: cmake 152 153 LibFuzzer-style fuzzers are not currently supported by Pigweed when using 154 CMake. 155 156 .. tab-item:: Bazel 157 :sync: bazel 158 159 Add a Bazel target to the module using the ``pw_cc_fuzz_test`` rule. For 160 example: 161 162 .. code-block:: 163 164 # In $dir_my_module/BUILD.bazel 165 pw_cc_fuzz_test( 166 name = "my_fuzzer", 167 srcs = ["my_fuzzer.cc"], 168 deps = [":my_lib"] 169 ) 170 171---------------------------------------------- 172Step 3: Add the fuzzer unit test to your build 173---------------------------------------------- 174Pigweed automatically generates unit tests for libFuzzer-based fuzzers in some 175build systems. 176 177.. tab-set:: 178 179 .. tab-item:: GN 180 :sync: gn 181 182 The generated unit test will be suffixed by ``_test`` and needs to be 183 added to the module's test group. This test verifies the fuzzer can build 184 and run, even when not being built in a 185 :ref:`fuzzing toolchain<module-pw_fuzzer-guides-using_libfuzzer-toolchain>`. 186 For example, for a fuzzer called ``my_fuzzer``, add the following: 187 188 .. code-block:: 189 190 # In $dir_my_module/BUILD.gn 191 pw_test_group("tests") { 192 tests = [ 193 ... 194 ":my_fuzzer_test", 195 ] 196 } 197 198 .. tab-item:: CMake 199 :sync: cmake 200 201 LibFuzzer-style fuzzers are not currently supported by Pigweed when using 202 CMake. 203 204 .. tab-item:: Bazel 205 :sync: bazel 206 207 Fuzzer unit tests are included automatically in Pigweed's Bazel build. 208 209------------------------ 210Step 4: Build the fuzzer 211------------------------ 212LibFuzzer-style fuzzers require the compiler to add instrumentation and 213runtimes when building. 214 215.. tab-set:: 216 217 .. tab-item:: GN 218 :sync: gn 219 220 Select a sanitizer runtime. See LLVM for `valid options`_. 221 222 .. code-block:: sh 223 224 $ gn gen out --args='pw_toolchain_SANITIZERS=["address"]' 225 226 Some toolchains may set a default for fuzzers if none is specified. For 227 example, `//targets/host:host_clang_fuzz` defaults to "address". 228 229 Build the fuzzers using ``ninja`` directly. 230 231 .. code-block:: sh 232 233 $ ninja -C out fuzzers 234 235 .. tab-item:: CMake 236 :sync: cmake 237 238 LibFuzzer-style fuzzers are not currently supported by Pigweed when using 239 CMake. 240 241 .. tab-item:: Bazel 242 :sync: bazel 243 244 Specify the libFuzzer config and a sanitizer config when building fuzzers. 245 246 .. code-block:: sh 247 248 $ bazel build //my_module:my_fuzzer --config=asan --config=libfuzzer 249 250---------------------------------- 251Step 5: Running the fuzzer locally 252---------------------------------- 253.. tab-set:: 254 255 .. tab-item:: GN 256 :sync: gn 257 258 The fuzzer binary will be in a subdirectory related to the toolchain. 259 Additional `libFuzzer options`_ and `corpus`_ arguments can be passed on 260 the command line. For example: 261 262 .. code-block:: sh 263 264 $ out/host_clang_fuzz/obj/my_module/bin/my_fuzzer -seed=1 path/to/corpus 265 266 Additional `sanitizer flags`_ may be passed uisng environment variables. 267 268 .. tab-item:: CMake 269 :sync: cmake 270 271 LibFuzzer-style fuzzers are not currently supported by Pigweed when using 272 CMake. 273 274 .. tab-item:: Bazel 275 :sync: bazel 276 277 Specify the libFuzzer config and a sanitizer config when building and 278 running fuzzers. For each fuzzer build rule with a given name, 279 `rules_fuzzing`_ produces a ``<name>_run`` target. For example: 280 281 .. code-block:: sh 282 283 $ bazel run //my_module:my_fuzzer_run --config=asan --config=libfuzzer\ 284 -- --timeout_secs=60 285 286Running the fuzzer should produce output similar to the following: 287 288.. code-block:: 289 290 INFO: Seed: 305325345 291 INFO: Loaded 1 modules (46 inline 8-bit counters): 46 [0x38dfc0, 0x38dfee), 292 INFO: Loaded 1 PC tables (46 PCs): 46 [0x23aaf0,0x23add0), 293 INFO: 0 files found in corpus 294 INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes 295 INFO: A corpus is not provided, starting from an empty corpus 296 #2 INITED cov: 2 ft: 3 corp: 1/1b exec/s: 0 rss: 27Mb 297 #4 NEW cov: 3 ft: 4 corp: 2/3b lim: 4 exec/s: 0 rss: 27Mb L: 2/2 MS: 2 ShuffleBytes-InsertByte- 298 #11 NEW cov: 7 ft: 8 corp: 3/7b lim: 4 exec/s: 0 rss: 27Mb L: 4/4 MS: 2 EraseBytes-CrossOver- 299 #27 REDUCE cov: 7 ft: 8 corp: 3/6b lim: 4 exec/s: 0 rss: 27Mb L: 3/3 MS: 1 EraseBytes- 300 #29 REDUCE cov: 7 ft: 8 corp: 3/5b lim: 4 exec/s: 0 rss: 27Mb L: 2/2 MS: 2 ChangeBit-EraseBytes- 301 #445 REDUCE cov: 9 ft: 10 corp: 4/13b lim: 8 exec/s: 0 rss: 27Mb L: 8/8 MS: 1 InsertRepeatedBytes- 302 ... 303 304.. TODO: b/282560789 - Add guides/improve_fuzzers.rst 305.. TODO: b/281139237 - Add guides/continuous_fuzzing.rst 306.. ---------- 307.. Next steps 308.. ---------- 309.. Once you have created a fuzzer, you may want to: 310 311.. * `Run it continuously on a fuzzing infrastructure <continuous_fuzzing>`_. 312.. * `Measure its code coverage and improve it <improve_a_fuzzer>`_. 313 314.. inclusive-language: disable 315 316.. _AddressSanitizer: https://github.com/google/sanitizers/wiki/AddressSanitizer 317.. _continuous_fuzzing: :ref:`module-pw_fuzzer-guides-continuous_fuzzing` 318.. _corpus: https://llvm.org/docs/LibFuzzer.html#corpus 319.. _fuzz target function: https://llvm.org/docs/LibFuzzer.html#fuzz-target 320.. _FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION: https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode 321.. _FuzzedDataProvider: https://github.com/llvm/llvm-project/blob/HEAD/compiler-rt/include/fuzzer/FuzzedDataProvider.h 322.. _improve_fuzzers: :ref:`module-pw_fuzzer-guides-improve_fuzzers 323.. _libFuzzer: https://llvm.org/docs/LibFuzzer.html 324.. _libFuzzer options: https://llvm.org/docs/LibFuzzer.html#options 325.. _rules_fuzzing: https://github.com/bazel-contrib/rules_fuzzing/blob/master/docs/guide.md#building-and-running 326.. _sanitizer flags: https://github.com/google/sanitizers/wiki/SanitizerCommonFlags 327.. _split a fuzzing input: https://github.com/google/fuzzing/blob/HEAD/docs/split-inputs.md 328.. _startup initialization: https://llvm.org/docs/LibFuzzer.html#startup-initialization 329.. _structure aware fuzzing: https://github.com/google/fuzzing/blob/HEAD/docs/structure-aware-fuzzing.md 330.. _valid options: https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html 331 332.. inclusive-language: enable 333