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