blob: db24c26f41afd520b4bc4766c7e4370ab20ca2c7 [file] [log] [blame]
# Copyright 2018 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/package.gni")
# Declare a fuzzed executable target.
#
# This target allows you to create an instrumented executable file for fuzzing
# with a sanitizer. Fuzz target authors should not use this template directly.
# Instead, use 'fuzz-target' with an associated 'fuzz-package' specifying the
# supported sanitizers.
#
# Flags: cflags, cflags_c, cflags_cc, asmflags, defines, include_dirs,
# ldflags, lib_dirs, libs,
# Deps: data_deps, deps, public_deps
# Dependent configs: all_dependent_configs, public_configs
# General: check_includes, configs, data, inputs, output_name,
# output_extension, public, sources, testonly, visibility
template("fuzzed_executable") {
executable(target_name) {
_target_type = "fuzzed_executable"
# Explicitly forward visibility, implicitly forward everything else.
# See comment in //build/config/BUILDCONFIG.gn for details on this pattern.
forward_variables_from(invoker, [ "visibility" ])
forward_variables_from(invoker, "*", [ "visibility" ])
}
}
# Defines a fuzz target binary
#
# The fuzz_target template is used to create binaries which leverage LLVM's
# libFuzzer to perform fuzz testing.
#
# Parameters
#
# options (optional)
# [list of strings] Each option is of the form "key=value" and indicates
# command line options that the fuzzer should be invoked with. Valid keys
# are libFuzzer options (https://llvm.org/docs/LibFuzzer.html#options).
#
# dictionary (optional)
# [file] If specified, a file containing inputs, one per line, that the
# fuzzer will use to generate new mutations.
#
# corpora (optional)
# [list of strings] One or more locations from which to get a fuzzing
# corpus, e.g. a project path to a directory of artifacts, a project
# path to a CIPD ensure file, or a GCS URL.
#
# sources (optional)
# [list of files] The C++ sources required to build the fuzzer binary. If
# present, this must define 'LLVMFuzzerTestOneInput' and will typically be
# a single file (with deps including the software under test).
#
template("fuzz_target") {
assert(
defined(invoker.dictionary) || defined(invoker.options) ||
defined(invoker.corpora) || defined(invoker.sources),
"At least one of [dictionary, options, corpora, sources] must be defined for $target_name")
forward_variables_from(invoker,
[
"dictionary",
"options",
"corpora",
])
if (current_toolchain == toolchain_variant.base) {
fuzz_gen_dir = "${target_gen_dir}/${target_name}"
fuzz_name = target_name
# Copy the fuzzer dictionary
if (defined(dictionary)) {
copy("${fuzz_name}_dictionary") {
sources = [
dictionary,
]
outputs = [
"${fuzz_gen_dir}/dictionary",
]
}
} else {
action("${fuzz_name}_dictionary") {
script = "//build/fuzzing/echo.py"
outputs = [
"${fuzz_gen_dir}/dictionary",
]
args = [ rebase_path("${fuzz_gen_dir}/dictionary") ]
}
}
# Copy the options the fuzzer should be invoked with
action("${fuzz_name}_options") {
script = "//build/fuzzing/echo.py"
outputs = [
"${fuzz_gen_dir}/options",
]
args = [ rebase_path("${fuzz_gen_dir}/options") ]
if (defined(options)) {
args += options
}
}
# Copy the corpora available for the fuzzer
action("${fuzz_name}_corpora") {
script = "//build/fuzzing/echo.py"
outputs = [
"${fuzz_gen_dir}/corpora",
]
args = [ rebase_path("${fuzz_gen_dir}/corpora") ]
if (defined(corpora)) {
args += corpora
}
}
# Create component manifest targets for all the sanitizers
action("${fuzz_name}_cmx") {
script = "//build/fuzzing/gen_fuzzer_cmx.py"
outputs = [
"${fuzz_gen_dir}/${fuzz_name}.cmx",
]
args = [
"--out",
rebase_path("${fuzz_gen_dir}/${fuzz_name}.cmx"),
"--bin",
"${fuzz_name}",
]
if (defined(invoker.cmx)) {
args += [
"--cmx",
rebase_path(invoker.cmx),
]
}
}
} else {
if (defined(dictionary)) {
not_needed([ "dictionary" ])
}
if (defined(options)) {
not_needed([ "options" ])
}
if (defined(corpora)) {
not_needed([ "corpora" ])
}
}
# Build the actual fuzzer, and ensure the necessary inputs are created or copied
if (defined(invoker.sources)) {
fuzzed_executable(target_name) {
forward_variables_from(invoker, "*")
testonly = true
}
}
}
set_defaults("fuzz_target") {
configs = default_executable_configs
}
# Defines a package of fuzz target binaries
#
# The fuzz_package template is used to bundle several fuzz_targets and their
# associated data into a single Fuchsia package. NOTE! "_fuzzers" is appended
# to the package name, that is, 'fuzz-package("foo")' should be referenced as
# "foo_fuzzers" in the package manifests.
#
# Parameters
#
# targets (required)
# [list of targets] The fuzz_targets to include in this package.
#
# sanitizers (required)
# [list of variants] A set of sanitizer variants. The resulting package
# will contain binaries for each sanitizer/target combination.
#
# meta (optional)
# binaries (optional)
# tests (optional)
# libraries (optional)
# resources (optional)
# extra (optional)
# Passed through to the normal package template.
#
template("fuzz_package") {
assert(defined(invoker.targets), "targets must be defined for $target_name}")
assert(defined(invoker.sanitizers),
"sanitizers must be defined for $target_name")
not_needed(invoker, "*")
# Only assemble the package once; handle the specific sanitizers in the loop below
if (current_toolchain == toolchain_variant.base) {
fuzz = {
binaries = []
extra = []
deps = []
libraries = []
meta = []
resources = []
tests = []
forward_variables_from(invoker, "*")
}
foreach(fuzz_target, fuzz.targets) {
fuzz_name = get_label_info(fuzz_target, "name")
# Find the executable variant for the sanitized fuzzer
sanitized_target = ""
foreach(sanitizer, fuzz.sanitizers) {
foreach(selector, select_variant_canonical) {
if (selector.variant == "${sanitizer}-fuzzer") {
selected = false
if (!selected && defined(selector.target_type)) {
selector_target_type = []
selector_target_type = selector.target_type
selected = selector_target_type[0] == "fuzzed_executable"
}
if (!selected && defined(selector.output_name)) {
selector_output_name = []
selector_output_name = selector.output_name
selected = selector_output_name[0] == fuzz_name
}
if (selected && sanitized_target == "") {
sanitized_target = "${fuzz_target}(${toolchain_variant.base}-${sanitizer}-fuzzer)"
}
}
}
}
fuzz_gen_dir =
get_label_info(fuzz_target, "target_gen_dir") + "/${fuzz_name}"
# Only add the binary and component manifest if fuzzing
if (sanitized_target != "") {
fuzz_out_dir = get_label_info("${sanitized_target}", "root_out_dir")
fuzz.deps += [
sanitized_target,
"${fuzz_target}_cmx",
]
fuzz.binaries += [
{
name = fuzz_name
source = rebase_path("${fuzz_out_dir}/${fuzz_name}")
dest = "${fuzz_name}"
},
]
fuzz.meta += [
{
path = rebase_path("${fuzz_gen_dir}/${fuzz_name}.cmx")
dest = "${fuzz_name}.cmx"
},
]
}
# Resources are common to all variants
fuzz.deps += [
"${fuzz_target}_corpora",
"${fuzz_target}_dictionary",
"${fuzz_target}_options",
]
fuzz.resources += [
{
path = rebase_path("${fuzz_gen_dir}/corpora")
dest = "${fuzz_name}/corpora"
},
{
path = rebase_path("${fuzz_gen_dir}/dictionary")
dest = "${fuzz_name}/dictionary"
},
{
path = rebase_path("${fuzz_gen_dir}/options")
dest = "${fuzz_name}/options"
},
]
}
# Build the actual package
package("${target_name}_fuzzers") {
testonly = true
deps = fuzz.deps
binaries = fuzz.binaries
extra = fuzz.extra
libraries = fuzz.libraries
meta = fuzz.meta
resources = fuzz.resources
tests = fuzz.tests
}
}
}