xref: /aosp_15_r20/external/pigweed/pw_fuzzer/guides/libfuzzer.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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