diff --git a/.bazelrc b/.bazelrc index 04e7893..f7e8121 100644 --- a/.bazelrc +++ b/.bazelrc @@ -21,11 +21,13 @@ build:clang --action_env=CXX=clang++ build:asan-libfuzzer --config=clang build:asan-libfuzzer --linkopt=-fsanitize=fuzzer,address build:asan-libfuzzer --copt=-fsanitize=fuzzer,address +build:asan-libfuzzer --//fuzzing:engine=libfuzzer # Flags for Clang with MSAN and libfuzzer build:msan-libfuzzer --config=clang build:msan-libfuzzer --linkopt=-fsanitize=memory,fuzzer build:msan-libfuzzer --copt=-fsanitize=memory,fuzzer +build:msan-libfuzzer --//fuzzing:engine=libfuzzer # Flags for Clang with MSAN and libfuzzer, outputting detailed report build:msan-libfuzzer-repro --config=msan-libfuzzer diff --git a/WORKSPACE b/WORKSPACE index e9acaea..d12000d 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -18,6 +18,9 @@ workspace(name = "rules_fuzzing") load("//fuzzing:repositories.bzl", "rules_fuzzing_dependencies") rules_fuzzing_dependencies() +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") +bazel_skylib_workspace() + load("@rules_python//python:repositories.bzl", "py_repositories") py_repositories() diff --git a/fuzzing/BUILD b/fuzzing/BUILD index 12ffde7..4aea218 100644 --- a/fuzzing/BUILD +++ b/fuzzing/BUILD @@ -12,3 +12,15 @@ # 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("@bazel_skylib//rules:common_settings.bzl", "string_flag") + +string_flag( + name = "engine", + build_setting_default = "default", + values = [ + "default", + "libfuzzer", + ], + visibility = ["//visibility:public"], +) diff --git a/fuzzing/cc_deps.bzl b/fuzzing/cc_deps.bzl index 5c5b930..59f556a 100644 --- a/fuzzing/cc_deps.bzl +++ b/fuzzing/cc_deps.bzl @@ -52,6 +52,7 @@ def cc_fuzz_test( name = name + "_run", target = name, corpus = name + "_corpus" if corpus else None, + is_regression = False, # Since the script depends on the _fuzz_test above, which is a cc_test, # this attribute must be set. testonly = True, diff --git a/fuzzing/common.bzl b/fuzzing/common.bzl index c7db93d..c1604a9 100644 --- a/fuzzing/common.bzl +++ b/fuzzing/common.bzl @@ -15,18 +15,26 @@ """This file contains common rules for fuzzing test.""" +load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") + def _fuzzing_launcher_impl(ctx): # Generate a script to launcher the fuzzing test. script = ctx.actions.declare_file("%s" % ctx.label.name) + args = [ + ctx.executable._launcher.short_path, + ctx.executable.target.short_path, + "--corpus_dir=" + ctx.file.corpus.short_path if ctx.attr.corpus else "", + "--engine=" + ctx.attr._engine[BuildSettingInfo].value, + ] + + if ctx.attr.is_regression: + args.append("--regression=True") script_template = """#!/bin/sh -exec {launcher_path} {target_binary_path} --corpus_dir={corpus_dir} "$@" -""" +exec {launcher_args} "$@" """ script_content = script_template.format( - launcher_path = ctx.executable._launcher.short_path, - target_binary_path = ctx.executable.target.short_path, - corpus_dir = ctx.file.corpus.short_path if ctx.attr.corpus else "", + launcher_args = " ".join(args), ) ctx.actions.write(script, script_content, is_executable = True) @@ -50,6 +58,11 @@ Rule for creating a script to run the fuzzing test. executable = True, cfg = "host", ), + "_engine": attr.label( + default = ":engine", + doc = "The engine type.", + providers = [BuildSettingInfo], + ), "target": attr.label( executable = True, doc = "The fuzzing test to run.", @@ -60,6 +73,10 @@ Rule for creating a script to run the fuzzing test. doc = "The target to create a directory containing corpus files.", allow_single_file = True, ), + "is_regression": attr.bool( + doc = "If set true the target is for a regression test.", + default = True, + ), }, executable = True, ) diff --git a/fuzzing/repositories.bzl b/fuzzing/repositories.bzl index b01336e..18d24ec 100644 --- a/fuzzing/repositories.bzl +++ b/fuzzing/repositories.bzl @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Contains the external dependencies +"""Contains the external dependencies.""" load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") @@ -30,3 +30,12 @@ def rules_fuzzing_dependencies(): url = "https://github.com/bazelbuild/rules_pkg/releases/download/0.2.5/rules_pkg-0.2.5.tar.gz", sha256 = "352c090cc3d3f9a6b4e676cf42a6047c16824959b438895a76c2989c6d7c246a", ) + + http_archive( + name = "bazel_skylib", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz", + ], + sha256 = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44", + ) diff --git a/fuzzing/tools/launcher.py b/fuzzing/tools/launcher.py index adc2ed3..21e47de 100644 --- a/fuzzing/tools/launcher.py +++ b/fuzzing/tools/launcher.py @@ -36,6 +36,14 @@ "If non-empty, a directory that will be used as a seed corpus for the fuzzer run." ) +flags.DEFINE_enum( + "engine", "default", ["default", "libfuzzer"], + "The type of the engine, the default is to run a gUnit test.") + +flags.DEFINE_bool( + "regression", False, + "If set True, the script will trigger the target as a regression test.") + def main(argv): if len(argv) != 2: @@ -44,8 +52,11 @@ def main(argv): "\n\tpython " + __file__ + " EXECUTABLE") command_args = [argv[1]] - command_args.append("-max_total_time=" + str(FLAGS.timeout_secs)) - command_args.append("-timeout=" + str(FLAGS.timeout_secs)) + if FLAGS.engine == "libfuzzer": + command_args.append("-max_total_time=" + str(FLAGS.timeout_secs)) + command_args.append("-timeout=" + str(FLAGS.timeout_secs)) + if FLAGS.regression: + command_args.append("-runs=0") if FLAGS.corpus_dir: command_args.append(FLAGS.corpus_dir) os.execv(argv[1], command_args)