From 993b55e5321ceabcc80c82f25fd5d83f7b5045c4 Mon Sep 17 00:00:00 2001 From: Aaron Green Date: Fri, 13 Sep 2024 23:29:49 +0000 Subject: [PATCH] pw_fuzzer: Improve .bazelrc fuzzing support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL drops the fuzztest.bazelrc generated by FuzzTest, and instead adds imports two new files from //pw_fuzzer: libfuzzer.bazelrc and fuzztest.bazelrc. These facilitate adding fuzzing support to downstream projects' .bazelrc files as well. Additionally, this CL fixes up the Bazel support for FuzzTest. It uses the //pw_toolchain/host_clang:asan_feature and adds a //pw_toolchain/host_clang:fuzztest_feature instead of using FuzzTest's approach of setting {c|link}opts directly. Finally, this CL extands the same support for libFuzzer, in the form of //pw_fuzzer/libfuzzer.bazelrc. Bug: b/347696410 Change-Id: I39ae203574a1065f76a6ca0ac0b1e08242cb380a Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/216112 Reviewed-by: Ted Pudlik Lint: Lint 🤖 Commit-Queue: Aaron Green --- .bazelrc | 20 +++----- pigweed.json | 6 +++ .../fuchsia/bt_host/BUILD.bazel | 9 ++++ pw_fuzzer/fuzztest.bazelrc | 32 +++++++++++++ pw_fuzzer/guides/fuzztest.rst | 24 ++++++++-- pw_fuzzer/guides/libfuzzer.rst | 21 ++------ pw_fuzzer/libfuzzer.bazelrc | 25 ++++++++++ pw_system/BUILD.bazel | 5 ++ pw_toolchain/host_clang/BUILD.bazel | 22 +++++++++ pw_toolchain_bazel/flag_sets/BUILD.bazel | 14 ++++++ targets/rp2040/BUILD.bazel | 5 ++ third_party/fuzztest/fuzztest.bazelrc | 48 ------------------- 12 files changed, 149 insertions(+), 82 deletions(-) create mode 100644 pw_fuzzer/fuzztest.bazelrc create mode 100644 pw_fuzzer/libfuzzer.bazelrc delete mode 100644 third_party/fuzztest/fuzztest.bazelrc diff --git a/.bazelrc b/.bazelrc index b3316ba2e5..96f6099d86 100644 --- a/.bazelrc +++ b/.bazelrc @@ -100,20 +100,6 @@ common:asan --//pw_toolchain/host_clang:asan # accumulate. test:asan --test_tag_filters=-integration,-noasan -# Define the --config=asan-libfuzzer configuration. -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 - -# Include FuzzTest build configurations. -try-import %workspace%/third_party/fuzztest/fuzztest.bazelrc -build:fuzztest \ - --//pw_fuzzer:fuzztest_backend=@com_google_fuzztest//fuzztest:fuzztest_core -build:fuzztest \ - --@pigweed//pw_unit_test:main=@com_google_fuzztest//fuzztest:fuzztest_gtest_main - common:ubsan --//pw_toolchain/host_clang:ubsan # TODO: https://github.com/bazelbuild/bazel/issues/7322 - We need to repeat the # -integration because the test_tag_filters flag does not accumulate. @@ -124,6 +110,12 @@ common:tsan --//pw_toolchain/host_clang:tsan # -integration because the test_tag_filters flag does not accumulate. test:tsan --test_tag_filters=-integration,-notsan +# Include libFuzzer build configurations. +import %workspace%/pw_fuzzer/libfuzzer.bazelrc + +# Include FuzzTest build configurations. +import %workspace%/pw_fuzzer/fuzztest.bazelrc + # Configure workspace status command to test pw_build_info git_build_info # https://bazel.build/docs/user-manual#workspace-status-command # TODO: b/348046338 - Add a Windows version of this command diff --git a/pigweed.json b/pigweed.json index ac47c6405c..64a1d7e7ef 100644 --- a/pigweed.json +++ b/pigweed.json @@ -153,6 +153,12 @@ "--aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect", "--output_groups=clippy_checks", "//..." + ], + [ + "build", + "--config=asan", + "--config=fuzztest", + "//..." ] ], "integration": [ diff --git a/pw_bluetooth_sapphire/fuchsia/bt_host/BUILD.bazel b/pw_bluetooth_sapphire/fuchsia/bt_host/BUILD.bazel index 3cebfb59e4..ba244f8345 100644 --- a/pw_bluetooth_sapphire/fuchsia/bt_host/BUILD.bazel +++ b/pw_bluetooth_sapphire/fuchsia/bt_host/BUILD.bazel @@ -54,6 +54,15 @@ cc_library( "host.h", "util.h", ], + # `fuchsia_cc_binary` does not understand Pigweed's sanitizer features, e.g. + # //pw_toolchain/host_clang:asan_feature. This leads to link errors if the + # binary is linked against FuzzTest, as the latter expects the sanitizer to + # provide the SanitizerCoverage implementation. Since the binary is not + # intended to be a fuzzer, it can just be excluded from fuzzing builds. + target_compatible_with = select({ + "//pw_toolchain/host_clang:fuzztest_enabled": ["@platforms//:incompatible"], + "//conditions:default": [], + }), deps = [ ":bt_host_config", "//pw_async_fuchsia:dispatcher", diff --git a/pw_fuzzer/fuzztest.bazelrc b/pw_fuzzer/fuzztest.bazelrc new file mode 100644 index 0000000000..57c952a879 --- /dev/null +++ b/pw_fuzzer/fuzztest.bazelrc @@ -0,0 +1,32 @@ +# Copyright 2024 The Pigweed Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# Adapted from @com_google_fuzztest//bazel:setup_configs + +build:fuzztest --@pigweed//pw_toolchain/host_clang:fuzztest +build:fuzztest --dynamic_mode=off +build:fuzztest \ + --per_file_copt=+//,-fuzztest/.*,-googletest/.*,-googlemock/.*@-fsanitize-coverage=inline-8bit-counters,-fsanitize-coverage=trace-cmp +build:fuzztest \ + --@pigweed//pw_fuzzer:fuzztest_backend=@com_google_fuzztest//fuzztest:fuzztest_core +build:fuzztest \ + --@pigweed//pw_unit_test:backend=@pigweed//pw_unit_test:googletest +build:fuzztest \ + --@pigweed//pw_unit_test:main=@com_google_fuzztest//fuzztest:fuzztest_gtest_main + +# For fuzzing, we want to disregard any test strategy options and execute each +# test locally and one at a time. We also want to display as much information as +# soon as it is available, including PRNG seeds and corpus statistics. +# Both of these are achieved by streaming the test output. +test:fuzztest --test_output=streamed diff --git a/pw_fuzzer/guides/fuzztest.rst b/pw_fuzzer/guides/fuzztest.rst index 6a85d8d317..ce353f20b4 100644 --- a/pw_fuzzer/guides/fuzztest.rst +++ b/pw_fuzzer/guides/fuzztest.rst @@ -80,14 +80,30 @@ See the following: .. tab-item:: Bazel :sync: bazel - FuzzTest provides a build configuration that can be imported into your - ``.bazelrc`` file. Add the following: + Include Abseil-C++ and GoogleTest in your ``WORKSPACE`` file. For example: + + .. code-block:: + + http_archive( + name = "com_google_absl", + sha256 = "338420448b140f0dfd1a1ea3c3ce71b3bc172071f24f4d9a57d59b45037da440", + strip_prefix = "abseil-cpp-20240116.0", + url = "https://github.com/abseil/abseil-cpp/releases/download/20240116.0/abseil-cpp-20240116.0.tar.gz", + ) + + git_repository( + name = "com_google_googletest", + commit = "3b6d48e8d5c1d9b3f9f10ac030a94008bfaf032b", + remote = "https://pigweed.googlesource.com/third_party/github/google/googletest", + ) + + Then, import the FuzzTest build configurations in your ``.bazelrc`` file + by adding and adapting the following: .. code-block:: # Include FuzzTest build configurations. - try-import %workspace%/third_party/fuzztest/fuzztest.bazelrc - build:fuzztest --@pigweed//targets:fuzztest_config=//pw_fuzzer:fuzztest + try-import %workspace%/path/to/pigweed/pw_fuzzer/fuzztest.bazelrc ---------------------------------------- Step 1: Write a unit test for the target diff --git a/pw_fuzzer/guides/libfuzzer.rst b/pw_fuzzer/guides/libfuzzer.rst index 31fa3404b4..0e79c82d68 100644 --- a/pw_fuzzer/guides/libfuzzer.rst +++ b/pw_fuzzer/guides/libfuzzer.rst @@ -52,19 +52,10 @@ installation. In order to use it, you only need to define a suitable toolchain. .. tab-item:: Bazel :sync: bazel - Include ``rules_fuzzing`` and its Abseil C++ dependency in your - ``WORKSPACE`` file. For example: + Include ``rules_fuzzing`` 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", @@ -81,15 +72,13 @@ installation. In order to use it, you only need to define a suitable toolchain. rules_fuzzing_init() - Then, define the following build configuration in your ``.bazelrc`` file: + Then, import the libFuzzer build configurations in your ``.bazelrc`` file + by adding and adapting the following: .. 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 + # Include FuzzTest build configurations. + try-import %workspace%/path/to/pigweed/pw_fuzzer/libfuzzer.bazelrc ------------------------------------ Step 1: Write a fuzz target function diff --git a/pw_fuzzer/libfuzzer.bazelrc b/pw_fuzzer/libfuzzer.bazelrc new file mode 100644 index 0000000000..9271e58363 --- /dev/null +++ b/pw_fuzzer/libfuzzer.bazelrc @@ -0,0 +1,25 @@ +# Copyright 2024 The Pigweed Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# TODO(b/347696410): Currently, building with `--config=libfuzzer` causes link +# errors for `pw_target_runner`. + +build:libfuzzer \ + --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:libfuzzer +build:libfuzzer \ + --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer +build:libfuzzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=asan + +test:libfuzzer --test_output=streamed + diff --git a/pw_system/BUILD.bazel b/pw_system/BUILD.bazel index 0efc3da711..7e6e391bbb 100644 --- a/pw_system/BUILD.bazel +++ b/pw_system/BUILD.bazel @@ -542,6 +542,11 @@ pw_cc_binary( # testonly dependencies. testonly = True, srcs = ["example_user_app_init.cc"], + # TODO(b/365184562): This target does not build with asan and fuzztest. + target_compatible_with = select({ + "//pw_fuzzer:use_fuzztest": ["@platforms//:incompatible"], + "//conditions:default": [], + }), deps = [ ":pw_system", "//pw_unit_test:rpc_service", diff --git a/pw_toolchain/host_clang/BUILD.bazel b/pw_toolchain/host_clang/BUILD.bazel index 9dcf49d55a..e19c4a5812 100644 --- a/pw_toolchain/host_clang/BUILD.bazel +++ b/pw_toolchain/host_clang/BUILD.bazel @@ -219,6 +219,28 @@ pw_cc_feature_set( all_of = [":rules_rust_unsupported_feature"], ) +bool_flag( + name = "fuzztest", + build_setting_default = False, +) + +config_setting( + name = "fuzztest_enabled", + flag_values = { + ":fuzztest": "true", + }, +) + +pw_cc_feature( + name = "fuzztest_feature", + enabled = select({ + ":fuzztest_enabled": True, + "//conditions:default": False, + }), + feature_name = "fuzztest", + flag_sets = ["@pw_toolchain//flag_sets:fuzztest"], +) + # This is a sentinel feature defined by rules_rust. It is by definition # unsupported: rules_rust will disable this feature when linking Rust code. pw_cc_feature( diff --git a/pw_toolchain_bazel/flag_sets/BUILD.bazel b/pw_toolchain_bazel/flag_sets/BUILD.bazel index 12df528626..330f2975ef 100644 --- a/pw_toolchain_bazel/flag_sets/BUILD.bazel +++ b/pw_toolchain_bazel/flag_sets/BUILD.bazel @@ -156,3 +156,17 @@ pw_cc_flag_set( "-DTHREAD_SANITIZER", ], ) + +pw_cc_flag_set( + name = "fuzztest", + actions = [ + "@pw_toolchain//actions:all_compiler_actions", + "@pw_toolchain//actions:all_link_actions", + ], + flags = [ + "-mcrc32", + "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION", + "-UNDEBUG", + "-D_LIBCPP_ENABLE_ASSERTIONS=1", + ], +) diff --git a/targets/rp2040/BUILD.bazel b/targets/rp2040/BUILD.bazel index 00af6a3ab3..f4ce23d38b 100644 --- a/targets/rp2040/BUILD.bazel +++ b/targets/rp2040/BUILD.bazel @@ -189,6 +189,11 @@ pw_cc_binary( name = "system_async_example", testonly = True, srcs = ["system_async_example.cc"], + # TODO(b/365184562): This target does not build with asan and fuzztest. + target_compatible_with = select({ + "//pw_fuzzer:use_fuzztest": ["@platforms//:incompatible"], + "//conditions:default": [], + }), deps = [ "//pw_channel:rp2_stdio_channel", "//pw_libcxx", diff --git a/third_party/fuzztest/fuzztest.bazelrc b/third_party/fuzztest/fuzztest.bazelrc deleted file mode 100644 index 1721cf56bd..0000000000 --- a/third_party/fuzztest/fuzztest.bazelrc +++ /dev/null @@ -1,48 +0,0 @@ -### DO NOT EDIT. Generated file. -# -# To regenerate, run the following from your project's workspace: -# -# bazel run @com_google_fuzztest//bazel:setup_configs > fuzztest.bazelrc -# -# And don't forget to add the following to your project's .bazelrc: -# -# try-import %workspace%/fuzztest.bazelrc - - -### Common options. -# -# Do not use directly. - -# Compile and link with Address Sanitizer (ASAN). -build:fuzztest-common --linkopt=-fsanitize=address -build:fuzztest-common --copt=-fsanitize=address - -# Standard define for "ifdef-ing" any fuzz test specific code. -build:fuzztest-common --copt=-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - -# In fuzz tests, we want to catch assertion violations even in optimized builds. -build:fuzztest-common --copt=-UNDEBUG - -# Enable libc++ assertions. -# See https://libcxx.llvm.org/UsingLibcxx.html#enabling-the-safe-libc-mode -build:fuzztest-common --copt=-D_LIBCPP_ENABLE_ASSERTIONS=1 - - -### FuzzTest build configuration. -# -# Use with: --config=fuzztest - -build:fuzztest --config=fuzztest-common - -# Link statically. -build:fuzztest --dynamic_mode=off - -# We rely on the following flag instead of the compiler provided -# __has_feature(address_sanitizer) to know that we have an ASAN build even in -# the uninstrumented runtime. -build:fuzztest --copt=-DADDRESS_SANITIZER - -# We apply coverage tracking instrumentation to everything but the -# FuzzTest framework itself (including GoogleTest and GoogleMock). -build:fuzztest --per_file_copt=+//,-fuzztest/.*,-googletest/.*,-googlemock/.*@-fsanitize-coverage=inline-8bit-counters,-fsanitize-coverage=trace-cmp -