• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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