# Copyright 2015 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Defines fuzzer_test. # import("//build/config/features.gni") import("//build/config/sanitizers/sanitizers.gni") import("//testing/test.gni") # fuzzer_test is used to define individual libfuzzer tests. # # Supported attributes: # - (required) sources - fuzzer test source files # - deps - test dependencies # - libs - Additional libraries to link. # - frameworks - Apple-only. Additional frameworks to link. # - additional_configs - additional configs to be used for compilation # - dict - a dictionary file for the fuzzer. # - environment_variables - certain whitelisted environment variables for the # fuzzer (AFL_DRIVER_DONT_DEFER is the only one allowed currently). # - libfuzzer_options - options for the fuzzer (e.g. -close_fd_mask=N). # - asan_options - AddressSanitizer options (e.g. allow_user_segv_handler=1). # - msan_options - MemorySanitizer options. # - ubsan_options - UndefinedBehaviorSanitizer options. # - seed_corpus - a directory with seed corpus. # - seed_corpus_deps - dependencies for generating the seed corpus. # - grammar_options - defines a grammar used by a grammar based mutator. # # If use_libfuzzer gn flag is defined, then proper fuzzer would be build. # Without use_libfuzzer or use_afl a unit-test style binary would be built on # linux and the whole target is a no-op otherwise. # # The template wraps test() target with appropriate dependencies. # If any test run-time options are present (dict or libfuzzer_options), then a # config (.options file) file would be generated or modified in root output # dir (next to test). template("fuzzer_test") { if (!disable_libfuzzer && use_fuzzing_engine) { assert(defined(invoker.sources), "Need sources in $target_name.") test_deps = [ "//testing/libfuzzer:libfuzzer_main" ] test_data_deps = [] if (defined(invoker.deps)) { test_deps += invoker.deps } if (defined(invoker.data_deps)) { test_data_deps += invoker.data_deps } supporting_file_test_deps = [] supporting_file_test_data_deps = [] if (defined(invoker.seed_corpus) || defined(invoker.seed_corpuses)) { assert(!(defined(invoker.seed_corpus) && defined(invoker.seed_corpuses)), "Do not use both seed_corpus and seed_corpuses for $target_name.") out = "$root_build_dir/$target_name" + "_seed_corpus.zip" seed_corpus_deps = [] if (defined(invoker.seed_corpus_deps)) { seed_corpus_deps += invoker.seed_corpus_deps } action(target_name + "_seed_corpus") { script = "//testing/libfuzzer/archive_corpus.py" testonly = true args = [ "--output", rebase_path(out, root_build_dir), ] if (defined(invoker.seed_corpus)) { args += [ rebase_path(invoker.seed_corpus, root_build_dir) ] } if (defined(invoker.seed_corpuses)) { foreach(seed_corpus_path, invoker.seed_corpuses) { args += [ rebase_path(seed_corpus_path, root_build_dir) ] } } outputs = [ out ] deps = [ "//testing/libfuzzer:seed_corpus" ] + seed_corpus_deps } if (archive_seed_corpus) { supporting_file_test_deps += [ ":" + target_name + "_seed_corpus" ] } } if (defined(invoker.dict) || defined(invoker.libfuzzer_options) || defined(invoker.asan_options) || defined(invoker.msan_options) || defined(invoker.ubsan_options) || defined(invoker.environment_variables) || defined(invoker.grammar_options)) { if (defined(invoker.dict)) { # Copy dictionary to output. copy(target_name + "_dict_copy") { sources = [ invoker.dict ] outputs = [ "$root_build_dir/" + target_name + ".dict" ] } supporting_file_test_deps += [ ":" + target_name + "_dict_copy" ] } # Generate .options file. config_file_name = target_name + ".options" action(config_file_name) { script = "//testing/libfuzzer/gen_fuzzer_config.py" args = [ "--config", rebase_path("$root_build_dir/" + config_file_name, root_build_dir), ] if (defined(invoker.dict)) { args += [ "--dict", rebase_path("$root_build_dir/" + invoker.target_name + ".dict", root_build_dir), ] } if (defined(invoker.libfuzzer_options)) { args += [ "--libfuzzer_options" ] args += invoker.libfuzzer_options } if (defined(invoker.asan_options)) { args += [ "--asan_options" ] args += invoker.asan_options } if (defined(invoker.msan_options)) { args += [ "--msan_options" ] args += invoker.msan_options } if (defined(invoker.ubsan_options)) { args += [ "--ubsan_options" ] args += invoker.ubsan_options } if (defined(invoker.environment_variables)) { args += [ "--environment_variables" ] args += invoker.environment_variables } if (defined(invoker.grammar_options)) { args += [ "--grammar_options" ] args += invoker.grammar_options } outputs = [ "$root_build_dir/$config_file_name" ] } supporting_file_test_deps += [ ":" + config_file_name ] } if (generate_fuzzer_owners) { # Generating owners files is slow, only enable when fuzzing engine is # used. owners_file_name = target_name + ".owners" action(owners_file_name) { script = "//testing/libfuzzer/gen_fuzzer_owners.py" pool = "//testing/libfuzzer:fuzzer_owners_pool" args = [ "--owners", rebase_path("$root_build_dir/" + owners_file_name, root_build_dir), ] if (defined(invoker.sources) && invoker.sources != []) { args += [ "--sources" ] + rebase_path(invoker.sources, "//") } else if (defined(invoker.deps) && invoker.deps != []) { _full_deps = [] foreach(_dep, invoker.deps) { _full_deps += [ get_label_info(_dep, "dir") + ":" + get_label_info(_dep, "name") ] } args += [ "--build-dir", rebase_path("$root_build_dir/", root_build_dir), "--deps", ] + _full_deps } outputs = [ "$root_build_dir/$owners_file_name" ] } supporting_file_test_data_deps += [ ":" + owners_file_name ] } # Copy to executable folder and codesign for catalyst if (is_ios) { # iOS only supports fuzzer in catalyst environment. assert(target_environment == "catalyst") # Loop over two kinds of deps to codesign these in two |action_foreach|s. # Use the "test_deps", "test_data_deps" as identifiers in for loop. foreach(_deps_type_label, [ "test_deps", "test_data_deps", ]) { _dep_list = [] if (_deps_type_label == "test_deps") { _dep_list = supporting_file_test_deps } else { _dep_list = supporting_file_test_data_deps } _files_to_sign = [] foreach(dep, _dep_list) { _files_to_sign += get_target_outputs(dep) } if (_files_to_sign != []) { _codesign_action_name = target_name + "_codesign_supporting_files_" + _deps_type_label action_foreach(_codesign_action_name) { testonly = true script = "//build/config/ios/codesign.py" sources = _files_to_sign _codesign_output_path = "${root_build_dir}/codesign/{{source_file_part}}" outputs = [ _codesign_output_path ] args = [ "code-sign-file", "--identity=" + ios_code_signing_identity, "--output=" + rebase_path(_codesign_output_path, root_build_dir), "{{source}}", ] deps = _dep_list } _bundle_data_name = target_name + "_bundle_data_" + _deps_type_label bundle_data(_bundle_data_name) { testonly = true sources = get_target_outputs(":${_codesign_action_name}") outputs = [ "{{bundle_executable_dir}}/{{source_file_part}}" ] public_deps = [ ":${_codesign_action_name}" ] } if (_deps_type_label == "test_deps") { test_deps += [ ":${_bundle_data_name}" ] } else { test_data_deps += [ ":${_bundle_data_name}" ] } } } } else { test_deps += supporting_file_test_deps test_data_deps += supporting_file_test_data_deps } test(target_name) { forward_variables_from(invoker, [ "cflags", "cflags_cc", "check_includes", "defines", "include_dirs", "sources", "libs", "frameworks", ]) deps = test_deps data_deps = test_data_deps if (defined(invoker.additional_configs)) { configs += invoker.additional_configs } configs += [ "//testing/libfuzzer:fuzzer_test_config" ] # Used by WebRTC to suppress some Clang warnings in their codebase. if (defined(invoker.suppressed_configs)) { configs -= invoker.suppressed_configs } if (defined(invoker.generated_sources)) { sources += invoker.generated_sources } if (is_ios) { info_plist = "//testing/libfuzzer/fuzzer_support_ios/libfuzzer-Info.plist" } if (is_mac) { sources += [ "//testing/libfuzzer/libfuzzer_exports.h" ] } } } else { # noop on unsupported platforms. # mark attributes as used. not_needed(invoker, "*") group(target_name) { } } }