Skip to content

Commit

Permalink
Add OSS-Fuzz support to the Bazel fuzzing rules. (#96)
Browse files Browse the repository at this point in the history
* The OSS-Fuzz support in one change.

* Clarify why we're skipping the fuzzing build mode flag.
  • Loading branch information
stefanbucur authored Jan 6, 2021
1 parent 79cd836 commit 5471279
Show file tree
Hide file tree
Showing 12 changed files with 285 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ build:asan-honggfuzz --@rules_fuzzing//fuzzing:cc_engine_sanitizer=asan
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
2 changes: 2 additions & 0 deletions docs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ bzl_library(
"//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",
Expand Down
5 changes: 5 additions & 0 deletions examples/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ cc_fuzz_test(
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"],
)

Expand Down
1 change: 1 addition & 0 deletions fuzzing/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ string_flag(
"none",
"libfuzzer",
"honggfuzz",
"oss-fuzz",
],
visibility = ["//visibility:public"],
)
Expand Down
5 changes: 5 additions & 0 deletions fuzzing/instrum_opts.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,17 @@ load(
"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.
Expand Down
7 changes: 7 additions & 0 deletions fuzzing/private/fuzz_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -96,3 +97,9 @@ def cc_fuzz_test(
# this attribute must be set.
testonly = True,
)

oss_fuzz_package(
name = name + "_oss_fuzz",
binary = name,
testonly = True,
)
17 changes: 17 additions & 0 deletions fuzzing/private/oss_fuzz/BUILD
Original file line number Diff line number Diff line change
@@ -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",
])
34 changes: 34 additions & 0 deletions fuzzing/private/oss_fuzz/BUILD.tpl
Original file line number Diff line number Diff line change
@@ -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",
])
22 changes: 22 additions & 0 deletions fuzzing/private/oss_fuzz/instrum.bzl.tpl
Original file line number Diff line number Diff line change
@@ -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}],
)
71 changes: 71 additions & 0 deletions fuzzing/private/oss_fuzz/package.bzl
Original file line number Diff line number Diff line change
@@ -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",
),
},
)
112 changes: 112 additions & 0 deletions fuzzing/private/oss_fuzz/repository.bzl
Original file line number Diff line number Diff line change
@@ -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.
""",
)
5 changes: 5 additions & 0 deletions fuzzing/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -56,3 +57,7 @@ def rules_fuzzing_dependencies():
url = "https://github.com/google/honggfuzz/archive/e0670137531242d66c9cf8a6dee677c055a8aacb.zip",
strip_prefix = "honggfuzz-e0670137531242d66c9cf8a6dee677c055a8aacb",
)

oss_fuzz_repository(
name = "rules_fuzzing_oss_fuzz",
)

0 comments on commit 5471279

Please sign in to comment.