From 2bcca667121b8963be92a1a5e6e5bcba2c60d186 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 21 Mar 2023 13:37:32 -0700 Subject: [PATCH] [fuzzing] Roll forward dep on fuzztest (#32667) --------- Co-authored-by: ctiller --- bazel/grpc_deps.bzl | 21 +++++++++ fuzztest/BUILD | 25 +++++++++++ fuzztest/README.md | 7 +++ fuzztest/fuzztest_test.cc | 26 +++++++++++ fuzztest/grpc_fuzz_test.bzl | 28 ++++++++++++ .../bazel/test_single_bazel_version.sh | 5 +++ tools/bazel.rc | 3 ++ tools/distrib/check_copyright.py | 1 + tools/distrib/check_include_guards.py | 2 +- tools/distrib/check_naked_includes.py | 1 + tools/distrib/fix_build_deps.py | 19 ++++++-- tools/distrib/iwyu.sh | 1 + tools/dockerfile/grpc_iwyu/iwyu.sh | 2 + tools/fuzztest.bazelrc | 43 +++++++++++++++++++ .../run_tests/sanity/check_bazel_workspace.py | 5 ++- 15 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 fuzztest/BUILD create mode 100644 fuzztest/README.md create mode 100644 fuzztest/fuzztest_test.cc create mode 100644 fuzztest/grpc_fuzz_test.bzl create mode 100644 tools/fuzztest.bazelrc diff --git a/bazel/grpc_deps.bzl b/bazel/grpc_deps.bzl index 3074a501d61..cffa141d077 100644 --- a/bazel/grpc_deps.bzl +++ b/bazel/grpc_deps.bzl @@ -100,6 +100,16 @@ def grpc_deps(): actual = "@com_google_googletest//:gtest", ) + native.bind( + name = "fuzztest", + actual = "@com_google_fuzztest//fuzztest", + ) + + native.bind( + name = "fuzztest_main", + actual = "@com_google_fuzztest//fuzztest:fuzztest_gtest_main", + ) + native.bind( name = "benchmark", actual = "@com_github_google_benchmark//:benchmark", @@ -259,6 +269,17 @@ def grpc_deps(): ], ) + if "com_google_fuzztest" not in native.existing_rules(): + http_archive( + name = "com_google_fuzztest", + sha256 = "f7bb5b3bd162576f3fbbe9bb768b57931fdd98581c1818789aceee5be4eeee64", + strip_prefix = "fuzztest-62cf00c7341eb05d128d0a3cbce79ac31dbda032", + urls = [ + # 2023-03-03 + "https://github.com/google/fuzztest/archive/62cf00c7341eb05d128d0a3cbce79ac31dbda032.tar.gz", + ], + ) + if "rules_cc" not in native.existing_rules(): http_archive( name = "rules_cc", diff --git a/fuzztest/BUILD b/fuzztest/BUILD new file mode 100644 index 00000000000..94752de38c5 --- /dev/null +++ b/fuzztest/BUILD @@ -0,0 +1,25 @@ +# Copyright 2023 gRPC 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 +# +# http://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. + +load(":grpc_fuzz_test.bzl", "grpc_fuzz_test") + +grpc_fuzz_test( + name = "fuzztest_test", + srcs = ["fuzztest_test.cc"], + external_deps = [ + "fuzztest", + "fuzztest_main", + "gtest", + ], +) diff --git a/fuzztest/README.md b/fuzztest/README.md new file mode 100644 index 00000000000..ba3a8f93790 --- /dev/null +++ b/fuzztest/README.md @@ -0,0 +1,7 @@ +Tests that leverage fuzztest. + +These require C++17 and so cannot be run with the standard build configurations. + +To run these tests: + +bazel run //fuzztest/path/to:test -c dbg --config fuzztest diff --git a/fuzztest/fuzztest_test.cc b/fuzztest/fuzztest_test.cc new file mode 100644 index 00000000000..3a371bd0c28 --- /dev/null +++ b/fuzztest/fuzztest_test.cc @@ -0,0 +1,26 @@ +// Copyright 2023 gRPC 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 +// +// http://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. + +// Test to verify Fuzztest integration + +#include "fuzztest/fuzztest.h" + +#include "gtest/gtest.h" + +TEST(MyTestSuite, OnePlustTwoIsTwoPlusOne) { EXPECT_EQ(1 + 2, 2 + 1); } + +void IntegerAdditionCommutes(unsigned a, unsigned b) { + EXPECT_EQ(a + b, b + a); +} +FUZZ_TEST(MyTestSuite, IntegerAdditionCommutes); diff --git a/fuzztest/grpc_fuzz_test.bzl b/fuzztest/grpc_fuzz_test.bzl new file mode 100644 index 00000000000..b45fd8e3e64 --- /dev/null +++ b/fuzztest/grpc_fuzz_test.bzl @@ -0,0 +1,28 @@ +# Copyright 2023 gRPC 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 +# +# http://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. + +""" +Rules for using fuzztest. +""" + +load("//bazel:grpc_build_system.bzl", "grpc_cc_test") + +def grpc_fuzz_test(name, srcs = [], deps = [], tags = [], external_deps = []): + grpc_cc_test( + name = name, + srcs = srcs, + tags = tags + ["grpc-fuzzer", "no-cache"], + deps = deps, + external_deps = external_deps, + ) diff --git a/test/distrib/bazel/test_single_bazel_version.sh b/test/distrib/bazel/test_single_bazel_version.sh index 931e9398c65..66e3240b8ab 100755 --- a/test/distrib/bazel/test_single_bazel_version.sh +++ b/test/distrib/bazel/test_single_bazel_version.sh @@ -30,6 +30,10 @@ EXCLUDED_TARGETS=( "-//src/objective-c/..." "-//third_party/objective_c/..." + # Targets here need C++17 to build via a different configuration, so this is + # done separately + "-//fuzztest/..." + # This could be a legitmate failure due to bitrot. "-//src/proto/grpc/testing:test_gen_proto" @@ -57,6 +61,7 @@ ACTION_ENV_FLAG="--action_env=bazel_cache_invalidate=version_${VERSION}" tools/bazel version | grep "$VERSION" || { echo "Detected bazel version did not match expected value of $VERSION" >/dev/stderr; exit 1; } tools/bazel build "${ACTION_ENV_FLAG}" -- //... "${EXCLUDED_TARGETS[@]}" || FAILED_TESTS="${FAILED_TESTS}buildtest " +tools/bazel build "${ACTION_ENV_FLAG}" --config fuzztest -- //fuzztest/... || FAILED_TESTS="${FAILED_TESTS}fuzztest_buildtest " for TEST_DIRECTORY in "${TEST_DIRECTORIES[@]}"; do pushd "test/distrib/bazel/$TEST_DIRECTORY/" diff --git a/tools/bazel.rc b/tools/bazel.rc index f15c6b5f54b..2c3518116fc 100644 --- a/tools/bazel.rc +++ b/tools/bazel.rc @@ -142,3 +142,6 @@ build:python_poller_engine --test_env="GRPC_ASYNCIO_ENGINE=poller" # Compile database generation config build:compdb --build_tag_filters=-nocompdb --features=-layering_check + +try-import %workspace%/tools/fuzztest.bazelrc +build:fuzztest --cxxopt=-std=c++17 diff --git a/tools/distrib/check_copyright.py b/tools/distrib/check_copyright.py index 494eb3be91b..0e6d45c5d39 100755 --- a/tools/distrib/check_copyright.py +++ b/tools/distrib/check_copyright.py @@ -142,6 +142,7 @@ _ENFORCE_CPP_STYLE_COMMENT_PATH_PREFIX = tuple([ 'src/cpp/', 'test/core/', 'test/cpp/', + 'fuzztest/', ]) RE_YEAR = r'Copyright (?P[0-9]+\-)?(?P[0-9]+) ([Tt]he )?gRPC [Aa]uthors(\.|)' diff --git a/tools/distrib/check_include_guards.py b/tools/distrib/check_include_guards.py index c5466542007..47ecc08e218 100755 --- a/tools/distrib/check_include_guards.py +++ b/tools/distrib/check_include_guards.py @@ -189,7 +189,7 @@ argp.add_argument('-f', '--fix', default=False, action='store_true') argp.add_argument('--precommit', default=False, action='store_true') args = argp.parse_args() -grep_filter = r"grep -E '^(include|src/core|src/cpp|test/core|test/cpp)/.*\.h$'" +grep_filter = r"grep -E '^(include|src/core|src/cpp|test/core|test/cpp|fuzztest/)/.*\.h$'" if args.precommit: git_command = 'git diff --name-only HEAD' else: diff --git a/tools/distrib/check_naked_includes.py b/tools/distrib/check_naked_includes.py index 5af510a42b6..961e376e5dc 100755 --- a/tools/distrib/check_naked_includes.py +++ b/tools/distrib/check_naked_includes.py @@ -38,6 +38,7 @@ CHECK_SUBDIRS = [ 'src/cpp', 'test/core', 'test/cpp', + 'fuzztest', ] for subdir in CHECK_SUBDIRS: diff --git a/tools/distrib/fix_build_deps.py b/tools/distrib/fix_build_deps.py index df5ff056886..42f001c61ed 100755 --- a/tools/distrib/fix_build_deps.py +++ b/tools/distrib/fix_build_deps.py @@ -136,6 +136,7 @@ EXTERNAL_DEPS = { 'address_sorting', 'ares.h': 'cares', + 'fuzztest/fuzztest.h': ['fuzztest', 'fuzztest_main'], 'google/api/monitored_resource.pb.h': 'google/api:monitored_resource_cc_proto', 'google/devtools/cloudtrace/v2/tracing.grpc.pb.h': @@ -433,6 +434,7 @@ for dirname in [ "test/core/promise", "test/core/resource_quota", "test/core/transport/chaotic_good", + "fuzztest", ]: parsing_path = dirname exec( @@ -448,6 +450,7 @@ for dirname in [ 'grpc_cc_library': grpc_cc_library, 'grpc_cc_test': grpc_cc_library, 'grpc_fuzzer': grpc_cc_library, + 'grpc_fuzz_test': grpc_cc_library, 'grpc_proto_fuzzer': grpc_cc_library, 'select': lambda d: d["//conditions:default"], 'glob': lambda files: None, @@ -570,9 +573,13 @@ def make_library(library): if hdr in INTERNAL_DEPS: dep = INTERNAL_DEPS[hdr] - if not ('//' in dep): - dep = '//:' + dep - deps.add(dep, hdr) + if isinstance(dep, list): + for d in dep: + deps.add(d, hdr) + else: + if not ('//' in dep): + dep = '//:' + dep + deps.add(dep, hdr) continue if hdr in vendors: @@ -588,7 +595,11 @@ def make_library(library): continue if hdr in EXTERNAL_DEPS: - external_deps.add(EXTERNAL_DEPS[hdr], hdr) + if isinstance(EXTERNAL_DEPS[hdr], list): + for dep in EXTERNAL_DEPS[hdr]: + external_deps.add(dep, hdr) + else: + external_deps.add(EXTERNAL_DEPS[hdr], hdr) continue if hdr.startswith('opencensus/'): diff --git a/tools/distrib/iwyu.sh b/tools/distrib/iwyu.sh index 6976ac24140..12591541df3 100755 --- a/tools/distrib/iwyu.sh +++ b/tools/distrib/iwyu.sh @@ -35,6 +35,7 @@ tools/distrib/gen_compilation_database.py \ "//src/compiler/..." \ "//test/core/..." \ "//test/cpp/..." \ + "//fuzztest/..." \ $MANUAL_TARGETS # run iwyu against the checked out codebase diff --git a/tools/dockerfile/grpc_iwyu/iwyu.sh b/tools/dockerfile/grpc_iwyu/iwyu.sh index ac2d69971fb..8b212085f83 100755 --- a/tools/dockerfile/grpc_iwyu/iwyu.sh +++ b/tools/dockerfile/grpc_iwyu/iwyu.sh @@ -66,6 +66,7 @@ sed -i 's,^#!/usr/bin/env python,#!/usr/bin/env python3,g' ${IWYU_ROOT}/iwyu/fix cat compile_commands.json \ | sed "s/ -DNDEBUG//g" \ + | sed "s/ -std=c\\+\\+14/ -std=c++17/g" \ | sed "s,\"file\": \",\"file\": \"${IWYU_ROOT}/,g" \ > compile_commands_for_iwyu.json @@ -74,6 +75,7 @@ export ENABLED_MODULES=' src/core/lib src/cpp test/core + fuzztest ' export DISABLED_MODULES=' diff --git a/tools/fuzztest.bazelrc b/tools/fuzztest.bazelrc new file mode 100644 index 00000000000..b2616effcea --- /dev/null +++ b/tools/fuzztest.bazelrc @@ -0,0 +1,43 @@ +### 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. + +# Link with Address Sanitizer (ASAN). +build:fuzztest-common --linkopt=-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 + + +### 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 and ASAN instrumentation to everything but the +# FuzzTest framework itself (including GoogleTest and GoogleMock). +build:fuzztest --per_file_copt=+//,-fuzztest/.*,-googletest/.*,-googlemock/.*@-fsanitize=address,-fsanitize-coverage=inline-8bit-counters,-fsanitize-coverage=trace-cmp + diff --git a/tools/run_tests/sanity/check_bazel_workspace.py b/tools/run_tests/sanity/check_bazel_workspace.py index 4965a15e2e5..67d8a86044a 100755 --- a/tools/run_tests/sanity/check_bazel_workspace.py +++ b/tools/run_tests/sanity/check_bazel_workspace.py @@ -43,8 +43,8 @@ _TWISTED_CONSTANTLY_DEP_NAME = 'com_github_twisted_constantly' _GRPC_DEP_NAMES = [ 'upb', 'boringssl', 'zlib', 'com_google_protobuf', 'com_google_googletest', 'rules_cc', 'com_github_google_benchmark', 'com_github_cares_cares', - 'com_google_absl', 'io_opencensus_cpp', 'envoy_api', _BAZEL_SKYLIB_DEP_NAME, - _BAZEL_TOOLCHAINS_DEP_NAME, _BAZEL_COMPDB_DEP_NAME, + 'com_google_absl', 'com_google_fuzztest', 'io_opencensus_cpp', 'envoy_api', + _BAZEL_SKYLIB_DEP_NAME, _BAZEL_TOOLCHAINS_DEP_NAME, _BAZEL_COMPDB_DEP_NAME, _TWISTED_TWISTED_DEP_NAME, _YAML_PYYAML_DEP_NAME, _TWISTED_INCREMENTAL_DEP_NAME, _ZOPEFOUNDATION_ZOPE_INTERFACE_DEP_NAME, _TWISTED_CONSTANTLY_DEP_NAME, 'io_bazel_rules_go', @@ -59,6 +59,7 @@ _GRPC_BAZEL_ONLY_DEPS = [ 'upb', # third_party/upb is checked in locally 'rules_cc', 'com_google_absl', + 'com_google_fuzztest', 'io_opencensus_cpp', _BAZEL_SKYLIB_DEP_NAME, _BAZEL_TOOLCHAINS_DEP_NAME,