aboutsummaryrefslogtreecommitdiff
path: root/pw_fuzzer/guides/libfuzzer.rst
diff options
context:
space:
mode:
Diffstat (limited to 'pw_fuzzer/guides/libfuzzer.rst')
-rw-r--r--pw_fuzzer/guides/libfuzzer.rst359
1 files changed, 359 insertions, 0 deletions
diff --git a/pw_fuzzer/guides/libfuzzer.rst b/pw_fuzzer/guides/libfuzzer.rst
new file mode 100644
index 000000000..a0c51a418
--- /dev/null
+++ b/pw_fuzzer/guides/libfuzzer.rst
@@ -0,0 +1,359 @@
+.. _module-pw_fuzzer-guides-using_libfuzzer:
+
+=========================================
+pw_fuzzer: Adding Fuzzers Using LibFuzzer
+=========================================
+.. pigweed-module-subpage::
+ :name: pw_fuzzer
+ :tagline: Better C++ code through easier fuzzing
+
+.. note::
+
+ `libFuzzer`_ is currently only supported on Linux and MacOS using clang.
+
+.. _module-pw_fuzzer-guides-using_libfuzzer-toolchain:
+
+-----------------------------------------
+Step 0: Set up libFuzzer for your project
+-----------------------------------------
+.. note::
+
+ This workflow only needs to be done once for a project.
+
+`libFuzzer`_ is a LLVM compiler runtime and should included with your ``clang``
+installation. In order to use it, you only need to define a suitable toolchain.
+
+.. tab-set::
+
+ .. tab-item:: GN
+ :sync: gn
+
+ Use ``pw_toolchain_host_clang``, or derive a new toolchain from it.
+ For example:
+
+ .. code-block::
+
+ import("$dir_pw_toolchain/host/target_toolchains.gni")
+
+ my_toolchains = {
+ ...
+ clang_fuzz = {
+ name = "my_clang_fuzz"
+ forward_variables_from(pw_toolchain_host.clang_fuzz, "*", ["name"])
+ }
+ ...
+ }
+
+ .. tab-item:: CMake
+ :sync: cmake
+
+ LibFuzzer-style fuzzers are not currently supported by Pigweed when using
+ CMake.
+
+ .. tab-item:: Bazel
+ :sync: bazel
+
+ Include ``rules_fuzzing`` and its Abseil C++ dependency in your
+ ``WORKSPACE`` file. For example:
+
+ .. code-block::
+
+ # Required by: rules_fuzzing.
+ http_archive(
+ name = "com_google_absl",
+ sha256 = "3ea49a7d97421b88a8c48a0de16c16048e17725c7ec0f1d3ea2683a2a75adc21",
+ strip_prefix = "abseil-cpp-20230125.0",
+ urls = ["https://github.com/abseil/abseil-cpp/archive/refs/tags/20230125.0.tar.gz"],
+ )
+
+ # Set up rules for fuzz testing.
+ http_archive(
+ name = "rules_fuzzing",
+ sha256 = "d9002dd3cd6437017f08593124fdd1b13b3473c7b929ceb0e60d317cb9346118",
+ strip_prefix = "rules_fuzzing-0.3.2",
+ urls = ["https://github.com/bazelbuild/rules_fuzzing/archive/v0.3.2.zip"],
+ )
+
+ load("@rules_fuzzing//fuzzing:repositories.bzl", "rules_fuzzing_dependencies")
+
+ rules_fuzzing_dependencies()
+
+ load("@rules_fuzzing//fuzzing:init.bzl", "rules_fuzzing_init")
+
+ rules_fuzzing_init()
+
+ Then, define the following build configuration in your ``.bazelrc`` file:
+
+ .. code-block::
+
+ build:asan-libfuzzer \
+ --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:libfuzzer
+ build:asan-libfuzzer \
+ --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer
+ build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=asan
+
+------------------------------------
+Step 1: Write a fuzz target function
+------------------------------------
+To write a fuzzer, a developer needs to write a `fuzz target function`_
+following the guidelines given by libFuzzer:
+
+.. code-block:: cpp
+
+ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ DoSomethingInterestingWithMyAPI(data, size);
+ return 0; // Non-zero return values are reserved for future use.
+ }
+
+When writing your fuzz target function, you may want to consider:
+
+- It is acceptable to return early if the input doesn't meet some constraints,
+ e.g. it is too short.
+- If your fuzzer accepts data with a well-defined format, you can bootstrap
+ coverage by crafting examples and adding them to a `corpus`_.
+- There are tools to `split a fuzzing input`_ into multiple fields if needed;
+ the `FuzzedDataProvider`_ is particularly easy to use.
+- If your code acts on "transformed" inputs, such as encoded or compressed
+ inputs, you may want to try `structure aware fuzzing`.
+- You can do `startup initialization`_ if you need to.
+- If your code is non-deterministic or uses checksums, you may want to disable
+ those **only** when fuzzing by using LLVM's
+ `FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION`_
+
+------------------------------------
+Step 2: Add the fuzzer to your build
+------------------------------------
+To build a fuzzer, do the following:
+
+.. tab-set::
+
+ .. tab-item:: GN
+ :sync: gn
+
+ Add the GN target to the module using ``pw_fuzzer`` GN template. If you
+ wish to limit when the generated unit test is run, you can set
+ ``enable_test_if`` in the same manner as ``enable_if`` for `pw_test`:
+
+ .. code-block::
+
+ # In $dir_my_module/BUILD.gn
+ import("$dir_pw_fuzzer/fuzzer.gni")
+
+ pw_fuzzer("my_fuzzer") {
+ sources = [ "my_fuzzer.cc" ]
+ deps = [ ":my_lib" ]
+ enable_test_if = device_has_1m_flash
+ }
+
+ Add the fuzzer GN target to the module's group of fuzzers. Create this
+ group if it does not exist.
+
+ .. code-block::
+
+ # In $dir_my_module/BUILD.gn
+ group("fuzzers") {
+ deps = [
+ ...
+ ":my_fuzzer",
+ ]
+ }
+
+ Make sure this group is referenced from a top-level ``fuzzers`` target in
+ your project, with the appropriate
+ :ref:`fuzzing toolchain<module-pw_fuzzer-guides-using_libfuzzer-toolchain>`.
+ For example:
+
+ .. code-block::
+
+ # In //BUILD.gn
+ group("fuzzers") {
+ deps = [
+ ...
+ "$dir_my_module:fuzzers(//my_toolchains:host_clang_fuzz)",
+ ]
+ }
+
+ .. tab-item:: CMake
+ :sync: cmake
+
+ LibFuzzer-style fuzzers are not currently supported by Pigweed when using
+ CMake.
+
+ .. tab-item:: Bazel
+ :sync: bazel
+
+ Add a Bazel target to the module using the ``pw_cc_fuzz_test`` rule. For
+ example:
+
+ .. code-block::
+
+ # In $dir_my_module/BUILD.bazel
+ pw_cc_fuzz_test(
+ name = "my_fuzzer",
+ srcs = ["my_fuzzer.cc"],
+ deps = [":my_lib"]
+ )
+
+----------------------------------------------
+Step 3: Add the fuzzer unit test to your build
+----------------------------------------------
+Pigweed automatically generates unit tests for libFuzzer-based fuzzers in some
+build systems.
+
+.. tab-set::
+
+ .. tab-item:: GN
+ :sync: gn
+
+ The generated unit test will be suffixed by ``_test`` and needs to be
+ added to the module's test group. This test verifies the fuzzer can build
+ and run, even when not being built in a
+ :ref:`fuzzing toolchain<module-pw_fuzzer-guides-using_libfuzzer-toolchain>`.
+ For example, for a fuzzer called ``my_fuzzer``, add the following:
+
+ .. code-block::
+
+ # In $dir_my_module/BUILD.gn
+ pw_test_group("tests") {
+ tests = [
+ ...
+ ":my_fuzzer_test",
+ ]
+ }
+
+ .. tab-item:: CMake
+ :sync: cmake
+
+ LibFuzzer-style fuzzers are not currently supported by Pigweed when using
+ CMake.
+
+ .. tab-item:: Bazel
+ :sync: bazel
+
+ Fuzzer unit tests are not generated for Pigweed's Bazel build.
+
+------------------------
+Step 4: Build the fuzzer
+------------------------
+LibFuzzer-style fuzzers require the compiler to add instrumentation and
+runtimes when building.
+
+.. tab-set::
+
+ .. tab-item:: GN
+ :sync: gn
+
+ Select a sanitizer runtime. See LLVM for `valid options`_.
+
+ .. code-block:: sh
+
+ $ gn gen out --args='pw_toolchain_SANITIZERS=["address"]'
+
+ Some toolchains may set a default for fuzzers if none is specified. For
+ example, `//targets/host:host_clang_fuzz` defaults to "address".
+
+ Build the fuzzers using ``ninja`` directly.
+
+ .. code-block:: sh
+
+ $ ninja -C out fuzzers
+
+ .. tab-item:: CMake
+ :sync: cmake
+
+ LibFuzzer-style fuzzers are not currently supported by Pigweed when using
+ CMake.
+
+ .. tab-item:: Bazel
+ :sync: bazel
+
+ Specify the `AddressSanitizer`_
+ :ref:`fuzzing toolchain<module-pw_fuzzer-guides-using_libfuzzer-toolchain>`
+ via a ``--config`` when building fuzzers.
+
+ .. code-block:: sh
+
+ $ bazel build //my_module:my_fuzzer --config=asan-libfuzzer
+
+----------------------------------
+Step 5: Running the fuzzer locally
+----------------------------------
+.. tab-set::
+
+ .. tab-item:: GN
+ :sync: gn
+
+ The fuzzer binary will be in a subdirectory related to the toolchain.
+ Additional `libFuzzer options`_ and `corpus`_ arguments can be passed on
+ the command line. For example:
+
+ .. code-block:: sh
+
+ $ out/host_clang_fuzz/obj/my_module/bin/my_fuzzer -seed=1 path/to/corpus
+
+ Additional `sanitizer flags`_ may be passed uisng environment variables.
+
+ .. tab-item:: CMake
+ :sync: cmake
+
+ LibFuzzer-style fuzzers are not currently supported by Pigweed when using
+ CMake.
+
+ .. tab-item:: Bazel
+ :sync: bazel
+
+ Specify the `AddressSanitizer`_
+ :ref:`fuzzing toolchain<module-pw_fuzzer-guides-using_libfuzzer-toolchain>`
+ via a ``--config`` when building and running fuzzers. Additional
+ `libFuzzer options`_ and `corpus`_ arguments can be passed on the command
+ line. For example:
+
+ .. code-block:: sh
+
+ $ bazel run //my_module:my_fuzzer --config=asan-libfuzzer -- \
+ -seed=1 path/to/corpus
+
+Running the fuzzer should produce output similar to the following:
+
+.. code-block::
+
+ INFO: Seed: 305325345
+ INFO: Loaded 1 modules (46 inline 8-bit counters): 46 [0x38dfc0, 0x38dfee),
+ INFO: Loaded 1 PC tables (46 PCs): 46 [0x23aaf0,0x23add0),
+ INFO: 0 files found in corpus
+ INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
+ INFO: A corpus is not provided, starting from an empty corpus
+ #2 INITED cov: 2 ft: 3 corp: 1/1b exec/s: 0 rss: 27Mb
+ #4 NEW cov: 3 ft: 4 corp: 2/3b lim: 4 exec/s: 0 rss: 27Mb L: 2/2 MS: 2 ShuffleBytes-InsertByte-
+ #11 NEW cov: 7 ft: 8 corp: 3/7b lim: 4 exec/s: 0 rss: 27Mb L: 4/4 MS: 2 EraseBytes-CrossOver-
+ #27 REDUCE cov: 7 ft: 8 corp: 3/6b lim: 4 exec/s: 0 rss: 27Mb L: 3/3 MS: 1 EraseBytes-
+ #29 REDUCE cov: 7 ft: 8 corp: 3/5b lim: 4 exec/s: 0 rss: 27Mb L: 2/2 MS: 2 ChangeBit-EraseBytes-
+ #445 REDUCE cov: 9 ft: 10 corp: 4/13b lim: 8 exec/s: 0 rss: 27Mb L: 8/8 MS: 1 InsertRepeatedBytes-
+ ...
+
+.. TODO: b/282560789 - Add guides/improve_fuzzers.rst
+.. TODO: b/281139237 - Add guides/continuous_fuzzing.rst
+.. ----------
+.. Next steps
+.. ----------
+.. Once you have created a fuzzer, you may want to:
+
+.. * `Run it continuously on a fuzzing infrastructure <continuous_fuzzing>`_.
+.. * `Measure its code coverage and improve it <improve_a_fuzzer>`_.
+
+
+.. _AddressSanitizer: https://github.com/google/sanitizers/wiki/AddressSanitizer
+.. _continuous_fuzzing: :ref:`module-pw_fuzzer-guides-continuous_fuzzing`
+.. _corpus: https://llvm.org/docs/LibFuzzer.html#corpus
+.. _fuzz target function: https://llvm.org/docs/LibFuzzer.html#fuzz-target
+.. _FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION: https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode
+.. _FuzzedDataProvider: https://github.com/llvm/llvm-project/blob/HEAD/compiler-rt/include/fuzzer/FuzzedDataProvider.h
+.. _improve_fuzzers: :ref:`module-pw_fuzzer-guides-improve_fuzzers
+.. _libFuzzer: https://llvm.org/docs/LibFuzzer.html
+.. _libFuzzer options: https://llvm.org/docs/LibFuzzer.html#options
+.. _sanitizer flags: https://github.com/google/sanitizers/wiki/SanitizerCommonFlags
+.. _split a fuzzing input: https://github.com/google/fuzzing/blob/HEAD/docs/split-inputs.md
+.. _startup initialization: https://llvm.org/docs/LibFuzzer.html#startup-initialization
+.. _structure aware fuzzing: https://github.com/google/fuzzing/blob/HEAD/docs/structure-aware-fuzzing.md
+.. _valid options: https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html
+