Add OSS-Fuzz support to the Bazel fuzzing rules. (#96)
* The OSS-Fuzz support in one change.
* Clarify why we're skipping the fuzzing build mode flag.
diff --git a/.bazelrc b/.bazelrc
index 4544b7b..7bece0c 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -40,3 +40,7 @@
build:msan-honggfuzz --//fuzzing:cc_engine=//fuzzing/engines:honggfuzz
build:msan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine_instrumentation=honggfuzz
build:msan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine_sanitizer=msan
+
+build:oss-fuzz --//fuzzing:cc_engine=@rules_fuzzing_oss_fuzz//:oss_fuzz_engine
+build:oss-fuzz --@rules_fuzzing//fuzzing:cc_engine_instrumentation=oss-fuzz
+build:oss-fuzz --@rules_fuzzing//fuzzing:cc_engine_sanitizer=none
diff --git a/docs/BUILD b/docs/BUILD
index 5995503..3a7cb7a 100644
--- a/docs/BUILD
+++ b/docs/BUILD
@@ -43,6 +43,8 @@
"//fuzzing/private:engine.bzl",
"//fuzzing/private:fuzz_test.bzl",
"//fuzzing/private:instrum_opts.bzl",
+ "//fuzzing/private/oss_fuzz:package.bzl",
+ "@rules_fuzzing_oss_fuzz//:instrum.bzl",
],
deps = [
":rules_cc",
diff --git a/examples/BUILD b/examples/BUILD
index 0a0c4fa..f53eb22 100644
--- a/examples/BUILD
+++ b/examples/BUILD
@@ -42,6 +42,11 @@
cc_fuzz_test(
name = "empty_fuzz_test_with_dict",
srcs = ["empty_fuzz_test.cc"],
+ corpus = [
+ "corpus_0.txt",
+ ":corpus_filegroup",
+ "test_corpus_dir",
+ ],
dicts = ["dict_dir/valid.dict"],
)
diff --git a/fuzzing/BUILD b/fuzzing/BUILD
index d6c3110..55aeb13 100644
--- a/fuzzing/BUILD
+++ b/fuzzing/BUILD
@@ -27,6 +27,7 @@
"none",
"libfuzzer",
"honggfuzz",
+ "oss-fuzz",
],
visibility = ["//visibility:public"],
)
diff --git a/fuzzing/instrum_opts.bzl b/fuzzing/instrum_opts.bzl
index c658b06..b2c16d5 100644
--- a/fuzzing/instrum_opts.bzl
+++ b/fuzzing/instrum_opts.bzl
@@ -24,12 +24,17 @@
"instrum_defaults",
"instrum_opts",
)
+load(
+ "@rules_fuzzing_oss_fuzz//:instrum.bzl",
+ "oss_fuzz_opts",
+)
# Fuzz test binary instrumentation configurations.
instrum_configs = {
"none": instrum_opts.make(),
"libfuzzer": instrum_defaults.libfuzzer,
"honggfuzz": instrum_defaults.honggfuzz,
+ "oss-fuzz": oss_fuzz_opts,
}
# Sanitizer configurations.
diff --git a/fuzzing/private/fuzz_test.bzl b/fuzzing/private/fuzz_test.bzl
index a9cd5ad..8a9d3f8 100644
--- a/fuzzing/private/fuzz_test.bzl
+++ b/fuzzing/private/fuzz_test.bzl
@@ -17,6 +17,7 @@
load("@rules_cc//cc:defs.bzl", "cc_test")
load("//fuzzing/private:common.bzl", "fuzzing_corpus", "fuzzing_dictionary", "fuzzing_launcher")
load("//fuzzing/private:binary.bzl", "fuzzing_binary")
+load("//fuzzing/private/oss_fuzz:package.bzl", "oss_fuzz_package")
def cc_fuzz_test(
name,
@@ -96,3 +97,9 @@
# this attribute must be set.
testonly = True,
)
+
+ oss_fuzz_package(
+ name = name + "_oss_fuzz",
+ binary = name,
+ testonly = True,
+ )
diff --git a/fuzzing/private/oss_fuzz/BUILD b/fuzzing/private/oss_fuzz/BUILD
new file mode 100644
index 0000000..670e678
--- /dev/null
+++ b/fuzzing/private/oss_fuzz/BUILD
@@ -0,0 +1,17 @@
+# Copyright 2020 Google LLC
+#
+# 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.
+
+exports_files([
+ "package.bzl",
+])
diff --git a/fuzzing/private/oss_fuzz/BUILD.tpl b/fuzzing/private/oss_fuzz/BUILD.tpl
new file mode 100644
index 0000000..901afdd
--- /dev/null
+++ b/fuzzing/private/oss_fuzz/BUILD.tpl
@@ -0,0 +1,34 @@
+# Copyright 2020 Google LLC
+#
+# 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.
+
+load("@rules_fuzzing//fuzzing:cc_deps.bzl", "cc_fuzzing_engine")
+load("@rules_cc//cc:defs.bzl", "cc_library")
+
+cc_fuzzing_engine(
+ name = "oss_fuzz_engine",
+ display_name = "OSS-Fuzz",
+ launcher = "oss_fuzz_launcher.sh",
+ library = ":oss_fuzz_stub",
+ visibility = ["//visibility:public"],
+)
+
+cc_library(
+ name = "oss_fuzz_stub",
+ srcs = [%{stub_srcs}],
+ linkopts = [%{stub_linkopts}],
+)
+
+exports_files([
+ "instrum.bzl",
+])
\ No newline at end of file
diff --git a/fuzzing/private/oss_fuzz/instrum.bzl.tpl b/fuzzing/private/oss_fuzz/instrum.bzl.tpl
new file mode 100644
index 0000000..04f1e54
--- /dev/null
+++ b/fuzzing/private/oss_fuzz/instrum.bzl.tpl
@@ -0,0 +1,22 @@
+# Copyright 2020 Google LLC
+#
+# 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.
+
+"""Instrumentation options for OSS-Fuzz."""
+
+load("@rules_fuzzing//fuzzing/private:instrum_opts.bzl", "instrum_opts")
+
+oss_fuzz_opts = instrum_opts.make(
+ conlyopts = [%{conlyopts}],
+ cxxopts = [%{cxxopts}],
+)
diff --git a/fuzzing/private/oss_fuzz/package.bzl b/fuzzing/private/oss_fuzz/package.bzl
new file mode 100644
index 0000000..24cdcf7
--- /dev/null
+++ b/fuzzing/private/oss_fuzz/package.bzl
@@ -0,0 +1,71 @@
+# Copyright 2020 Google LLC
+#
+# 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.
+
+"""Rule for packaging fuzz tests in the expected OSS-Fuzz format."""
+
+load("//fuzzing/private:binary.bzl", "CcFuzzingBinaryInfo")
+
+def _oss_fuzz_package_impl(ctx):
+ output_archive = ctx.actions.declare_file(ctx.label.name + ".tar")
+ binary_info = ctx.attr.binary[CcFuzzingBinaryInfo]
+
+ action_inputs = [binary_info.binary_file]
+ if binary_info.corpus_dir:
+ action_inputs.append(binary_info.corpus_dir)
+ if binary_info.dictionary_file:
+ action_inputs.append(binary_info.dictionary_file)
+ ctx.actions.run_shell(
+ outputs = [output_archive],
+ inputs = action_inputs,
+ command = """
+ declare -r STAGING_DIR="$(pwd)/{output}.staging"
+ mkdir "$STAGING_DIR"
+ ln -s "$(pwd)/{binary_path}" "$STAGING_DIR/{base_name}"
+ if [[ -n "{corpus_dir}" ]]; then
+ pushd "{corpus_dir}" >/dev/null
+ zip --quiet -r "$STAGING_DIR/{base_name}_seed_corpus.zip" ./*
+ popd >/dev/null
+ fi
+ if [[ -n "{dictionary_path}" ]]; then
+ ln -s "$(pwd)/{dictionary_path}" "$STAGING_DIR/{base_name}.dict"
+ fi
+ tar -chf "{output}" -C "$STAGING_DIR" .
+ """.format(
+ base_name = ctx.executable.binary.basename,
+ binary_path = binary_info.binary_file.path,
+ corpus_dir = binary_info.corpus_dir.path if binary_info.corpus_dir else "",
+ dictionary_path = binary_info.dictionary_file.path if binary_info.dictionary_file else "",
+ output = output_archive.path,
+ ),
+ )
+ return [DefaultInfo(files = depset([output_archive]))]
+
+oss_fuzz_package = rule(
+ implementation = _oss_fuzz_package_impl,
+ doc = """
+Packages a fuzz test in a TAR archive compatible with the OSS-Fuzz format.
+
+> NOTE: The current implementation does not yet support packaging the
+> binary runfiles.
+""",
+ attrs = {
+ "binary": attr.label(
+ executable = True,
+ doc = "The fuzz test executable.",
+ providers = [CcFuzzingBinaryInfo],
+ mandatory = True,
+ cfg = "target",
+ ),
+ },
+)
diff --git a/fuzzing/private/oss_fuzz/repository.bzl b/fuzzing/private/oss_fuzz/repository.bzl
new file mode 100644
index 0000000..6713bfe
--- /dev/null
+++ b/fuzzing/private/oss_fuzz/repository.bzl
@@ -0,0 +1,112 @@
+# Copyright 2020 Google LLC
+#
+# 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.
+
+"""Repository rule for configuring the OSS-Fuzz engine and instrumentation."""
+
+def _to_list_repr(elements):
+ return ", ".join([repr(element) for element in elements])
+
+def _extract_build_params(
+ repository_ctx,
+ fuzzing_engine_library,
+ cflags,
+ cxxflags):
+ stub_srcs = []
+ stub_linkopts = []
+ instrum_conlyopts = []
+ instrum_cxxopts = []
+
+ if fuzzing_engine_library:
+ if fuzzing_engine_library.startswith("-"):
+ # This is actually a flag, add it to the linker flags.
+ stub_linkopts.append(fuzzing_engine_library)
+ elif fuzzing_engine_library.endswith(".a"):
+ repository_ctx.symlink(
+ repository_ctx.path(fuzzing_engine_library),
+ "oss_fuzz_engine.a",
+ )
+ stub_srcs.append("oss_fuzz_engine.a")
+ else:
+ fail("Unsupported $LIB_FUZZING_ENGINE value '%s'" % fuzzing_engine_library)
+ for cflag in cflags:
+ # Skip the fuzzing build more flag, since it is separately controlled
+ # by the --//fuzzing:cc_fuzzing_build_mode configuration flag.
+ if cflag == "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION":
+ continue
+ instrum_conlyopts.append(cflag)
+ if cflag not in stub_linkopts:
+ stub_linkopts.append(cflag)
+ for cxxflag in cxxflags:
+ if cxxflag == "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION":
+ continue
+ instrum_cxxopts.append(cxxflag)
+ if cxxflag not in stub_linkopts:
+ stub_linkopts.append(cxxflag)
+
+ return struct(
+ stub_srcs = stub_srcs,
+ stub_linkopts = stub_linkopts,
+ instrum_conlyopts = instrum_conlyopts,
+ instrum_cxxopts = instrum_cxxopts,
+ )
+
+def _oss_fuzz_repository(repository_ctx):
+ environ = repository_ctx.os.environ
+ fuzzing_engine_library = environ.get("LIB_FUZZING_ENGINE")
+ cflags = environ.get("CFLAGS", "").split(" ")
+ cxxflags = environ.get("CXXFLAGS", "").split(" ")
+
+ build_params = _extract_build_params(
+ repository_ctx,
+ fuzzing_engine_library,
+ cflags,
+ cxxflags,
+ )
+
+ repository_ctx.template(
+ "BUILD",
+ repository_ctx.path(Label("@rules_fuzzing//fuzzing/private/oss_fuzz:BUILD.tpl")),
+ {
+ "%{stub_srcs}": _to_list_repr(build_params.stub_srcs),
+ "%{stub_linkopts}": _to_list_repr(build_params.stub_linkopts),
+ },
+ )
+ repository_ctx.template(
+ "instrum.bzl",
+ repository_ctx.path(Label("@rules_fuzzing//fuzzing/private/oss_fuzz:instrum.bzl.tpl")),
+ {
+ "%{conlyopts}": _to_list_repr(build_params.instrum_conlyopts),
+ "%{cxxopts}": _to_list_repr(build_params.instrum_cxxopts),
+ },
+ )
+ repository_ctx.file(
+ "oss_fuzz_launcher.sh",
+ "echo 'The OSS-Fuzz engine is not meant to be executed.'; exit 1",
+ )
+
+oss_fuzz_repository = repository_rule(
+ implementation = _oss_fuzz_repository,
+ environ = [
+ "LIB_FUZZING_ENGINE",
+ "CFLAGS",
+ "CXXFLAGS",
+ "SANITIZER",
+ ],
+ local = True,
+ doc = """
+Generates a repository containing an OSS-Fuzz fuzzing engine defintion.
+
+The fuzzing engine is defined in the //:oss_fuzz_engine target.
+""",
+)
diff --git a/fuzzing/repositories.bzl b/fuzzing/repositories.bzl
index cf7db2b..d897f5e 100644
--- a/fuzzing/repositories.bzl
+++ b/fuzzing/repositories.bzl
@@ -16,6 +16,7 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
+load("//fuzzing/private/oss_fuzz:repository.bzl", "oss_fuzz_repository")
def rules_fuzzing_dependencies():
"""Instantiates the dependencies of the fuzzing rules."""
@@ -56,3 +57,7 @@
url = "https://github.com/google/honggfuzz/archive/e0670137531242d66c9cf8a6dee677c055a8aacb.zip",
strip_prefix = "honggfuzz-e0670137531242d66c9cf8a6dee677c055a8aacb",
)
+
+ oss_fuzz_repository(
+ name = "rules_fuzzing_oss_fuzz",
+ )