| # Copyright 2015 The Chromium Authors |
| # 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). |
| # These are mostly applied only when the fuzzer is being run using the |
| # libfuzzer fuzzing engine, but a few of these may be interpreted by |
| # other fuzzing engines too |
| # - centipede_options - options for the fuzzer (e.g. shmem_size_mb=N) |
| # when running using the centipede fuzzing engine. |
| # - 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. |
| # - exclude_main - if you're going to provide your own 'main' function |
| # - high_end_job_required - whether the fuzzer requires bigger machines to run. |
| # Optional argument. |
| # - is_fuzzilli_compatible - whether the fuzzer is compatible with fuzzilli. |
| # |
| # 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") { |
| _high_end_job_required = false |
| if (defined(invoker.high_end_job_required)) { |
| _high_end_job_required = invoker.high_end_job_required |
| } |
| |
| # If the job is a high_end job and that we are currently building a high_end |
| # target, we should compile this fuzzer_test in. Otherwise, for now, we just |
| # compile everything in. |
| # TODO(crbug.com/333831251): Once CF supports high end jobs, do not compile |
| # high end targets except for high end jobs. |
| _should_build = (_high_end_job_required || !high_end_fuzzer_targets) && |
| !disable_libfuzzer && use_fuzzing_engine && |
| ((defined(invoker.is_fuzzilli_compatible) && |
| invoker.is_fuzzilli_compatible) || !use_fuzzilli) |
| if (_should_build) { |
| assert(defined(invoker.sources), "Need sources in $target_name.") |
| |
| test_deps = [] |
| if (defined(invoker.exclude_main) && invoker.exclude_main) { |
| test_deps += [ "//testing/libfuzzer:fuzzing_engine_no_main" ] |
| } else { |
| test_deps += [ "//testing/libfuzzer:fuzzing_engine_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.centipede_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" ] |
| } |
| |
| fuzzer_name = target_name |
| |
| # 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/" + fuzzer_name + ".dict", |
| root_build_dir), |
| ] |
| } else { |
| not_needed([ "fuzzer_name" ]) |
| } |
| |
| if (defined(invoker.libfuzzer_options)) { |
| args += [ "--libfuzzer_options" ] |
| args += invoker.libfuzzer_options |
| } |
| |
| if (defined(invoker.centipede_options)) { |
| args += [ "--centipede_options" ] |
| args += invoker.centipede_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" ] |
| } |
| test_data_deps += [ ":" + 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/apple/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", |
| "output_name", |
| "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/fuzzer-engine-Info.plist" |
| } |
| |
| if (is_mac) { |
| sources += [ "//testing/libfuzzer/libfuzzer_exports.h" ] |
| } |
| } |
| } else { |
| # noop on unsupported platforms. |
| # mark attributes as used. |
| not_needed(invoker, "*") |
| not_needed(invoker, |
| [ |
| "deps", |
| "seed_corpus", |
| "sources", |
| ]) |
| |
| group(target_name) { |
| } |
| } |
| } |