| # 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/test.gni") |
| |
| # TODO(fxbug.dev/60168): Consumers should import fuzzer_package explicitly. Remove once all are |
| # migrated. |
| import("//build/fuzzing/fuzzer_package.gni") |
| |
| # A list of configs present in fuzzer toolchain variants. |
| # |
| # GN logic that needs to behave differently between fuzzer and non-fuzzer variants can determine the |
| # variant type by checking the `toolchain_variant.configs`, e.g. |
| # if (toolchain_variant.configs + fuzzer_configs - fuzzer_configs == toolchain_variant.configs) { |
| # # Not using a fuzzer toolchain variant. |
| # } else { |
| # # Using a fuzzer toolchain variant. |
| # } |
| # |
| # This approach works even with the way `import` caches this file. |
| # |
| fuzzer_configs = [ "//build/config/fuzzer" ] |
| |
| # Instructs the linker to link against libFuzzer, see config documentation for details. |
| fuzzer_engine_configs = [ "//build/config/fuzzer:engine" ] |
| |
| # Defines a fuzzer binary. |
| # |
| # The fuzzer template is used to create components containing binaries which leverage LLVM's |
| # libFuzzer to perform fuzz testing. |
| # |
| # Parameters are precisely those of an `executable`, along with: |
| # |
| # cmx (optional) |
| # [file] If specified, a file containing a component manifest to start from when generating |
| # manifests for fuzzers. |
| # |
| # corpus (optional) |
| # [path] If specified, a directory containing elements of a seed corpus. These should be a |
| # combination of hand-crafted fuzzer inputs that increase coverage, and artifacts from |
| # previous fuzzing runs, e.g. fuzzer inputs that triggered crashes. These will be used to seed |
| # new fuzzing runs, and as regression unit tests. |
| # |
| # dictionary (optional) |
| # [file] If specified, a file containing quoted inputs, one per line, that the fuzzer will use |
| # to generate new mutations. |
| # |
| # |
| # 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). |
| # |
| template("fuzzer") { |
| _label = get_label_info(":$target_name", "label_no_toolchain") |
| base_output_name = target_name |
| if (defined(invoker.output_name)) { |
| base_output_name = invoker.output_name |
| } |
| |
| # Generate the component binary |
| executable(target_name) { |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "cmx", |
| "corpus", |
| "dictionary", |
| "options", |
| "visibility", |
| ]) |
| |
| # Explicitly forward visibility for nested scopes. |
| forward_variables_from(invoker, [ "visibility" ]) |
| testonly = true |
| variant_selector_target_type = "fuzzed_executable" |
| |
| # TODO(fxbug.dev/27011): This shouldn't be necessary, but libzircon isn't currently |
| # linked into libFuzzer on Fuchsia. |
| if (is_fuchsia) { |
| if (configs + fuzzer_engine_configs - fuzzer_engine_configs != configs) { |
| if (!defined(deps)) { |
| deps = [] |
| } |
| deps += [ "//src/zircon/lib/zircon" ] |
| } |
| } |
| } |
| |
| # Generate the fuzzer component manifest |
| cmx_target = "${target_name}_cmx" |
| cmx_output = "$target_gen_dir/${base_output_name}.cmx" |
| generated_file(cmx_target) { |
| if (defined(invoker.cmx)) { |
| cmx = read_file(invoker.cmx, "json") |
| } else { |
| cmx = { |
| program = { |
| } |
| sandbox = { |
| } |
| } |
| } |
| contents = { |
| program = { |
| forward_variables_from(cmx.program, "*") |
| binary = "bin/$base_output_name" |
| } |
| sandbox = { |
| services = [] |
| features = [] |
| forward_variables_from(cmx.sandbox, "*") |
| services += [ |
| "fuchsia.logger.LogSink", |
| "fuchsia.process.Launcher", |
| ] |
| features += [ |
| "isolated-persistent-storage", |
| "isolated-temp", |
| ] |
| } |
| forward_variables_from(cmx, |
| [ |
| "dev", |
| "runner", |
| "facets", |
| ]) |
| } |
| outputs = [ cmx_output ] |
| output_conversion = "json" |
| metadata = { |
| fuzz_spec = [ |
| { |
| label = _label |
| fuzzer = base_output_name |
| manifest = get_path_info(cmx_output, "file") |
| }, |
| ] |
| } |
| } |
| |
| # Generate seed corpus. See https://llvm.org/docs/LibFuzzer.html#corpus |
| # TODO(fxbug.dev/58940): Move away from invoking add_corpus.py and towards using |
| # in-tree corpus manifests. |
| corpus_target = "${target_name}_corpus" |
| corpus_output = "${target_gen_dir}/${base_output_name}/corpus.manifest" |
| action(corpus_target) { |
| script = "//build/fuzzing/add_corpus.py" |
| depfile = "$corpus_output.d" |
| outputs = [ corpus_output ] |
| args = [ |
| "--fuzzer", |
| base_output_name, |
| "--depfile", |
| rebase_path(depfile, root_build_dir), |
| "--manifest", |
| rebase_path(corpus_output, root_build_dir), |
| ] |
| if (defined(invoker.corpus)) { |
| args += [ |
| "--corpus", |
| rebase_path(invoker.corpus, root_build_dir), |
| ] |
| metadata = { |
| fuzz_spec = [ |
| { |
| label = _label |
| corpus = get_label_info(invoker.corpus, "label_no_toolchain") |
| }, |
| ] |
| } |
| } |
| } |
| |
| # Generate dictionary. See https://llvm.org/docs/LibFuzzer.html#dictionaries |
| dictionary_target = "${target_name}_dictionary" |
| dictionary_output = "${target_gen_dir}/${base_output_name}/dictionary" |
| if (defined(invoker.dictionary)) { |
| copy(dictionary_target) { |
| sources = [ invoker.dictionary ] |
| outputs = [ dictionary_output ] |
| } |
| } else { |
| generated_file(dictionary_target) { |
| contents = [ "# empty dictionary" ] |
| outputs = [ dictionary_output ] |
| } |
| } |
| |
| # Generate options. See https://llvm.org/docs/LibFuzzer.html#options |
| options_target = "${target_name}_options" |
| options_output = "${target_gen_dir}/${base_output_name}/options" |
| generated_file(options_target) { |
| contents = [] |
| if (defined(invoker.options)) { |
| contents = invoker.options |
| } |
| outputs = [ options_output ] |
| } |
| |
| # Generate a unit test for the fuzzer. |
| test_target = "${target_name}_test" |
| test_output = "${base_output_name}_test" |
| test(test_target) { |
| output_name = test_output |
| sources = [] |
| deps = [] |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "cmx", |
| "corpus", |
| "dictionary", |
| "options", |
| "output_name", |
| "target_name", |
| "visibility", |
| ]) |
| |
| # Explicitly forward visibility for nested scopes. |
| forward_variables_from(invoker, [ "visibility" ]) |
| configs -= fuzzer_engine_configs |
| deps += [ |
| ":$corpus_target", |
| "//sdk/lib/syslog/cpp:backend_legacy", |
| "//src/lib/fuzzing/cpp:fuzzer_test", |
| ] |
| } |
| |
| # Generate the fuzzer test component manifest |
| test_cmx_target = "${test_target}_cmx" |
| test_cmx_output = "$target_gen_dir/${test_output}.cmx" |
| generated_file(test_cmx_target) { |
| contents = { |
| program = { |
| binary = "test/$test_output" |
| args = [ |
| # Suppress warnings about missing directories |
| "-q", |
| |
| # Seed corpus |
| "pkg/data/${base_output_name}/corpus", |
| |
| # Live corpus |
| "data/${base_output_name}/corpus", |
| ] |
| } |
| sandbox = { |
| services = [ "fuchsia.logger.LogSink" ] |
| } |
| } |
| outputs = [ test_cmx_output ] |
| output_conversion = "json" |
| deps = [] |
| metadata = { |
| fuzz_spec = [ |
| { |
| label = _label |
| fuzzer_test = test_output |
| test_manifest = get_path_info(test_cmx_output, "file") |
| }, |
| ] |
| } |
| } |
| } |
| |
| set_defaults("fuzzer") { |
| configs = default_executable_configs + fuzzer_engine_configs |
| } |