1# Copyright 2015 The Chromium Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5# Defines fuzzer_test. 6# 7import("//build/config/features.gni") 8import("//build/config/sanitizers/sanitizers.gni") 9import("//testing/test.gni") 10 11# fuzzer_test is used to define individual libfuzzer tests. 12# 13# Supported attributes: 14# - (required) sources - fuzzer test source files 15# - deps - test dependencies 16# - libs - Additional libraries to link. 17# - frameworks - Apple-only. Additional frameworks to link. 18# - additional_configs - additional configs to be used for compilation 19# - dict - a dictionary file for the fuzzer. 20# - environment_variables - certain whitelisted environment variables for the 21# fuzzer (AFL_DRIVER_DONT_DEFER is the only one allowed currently). 22# - libfuzzer_options - options for the fuzzer (e.g. -close_fd_mask=N). 23# - asan_options - AddressSanitizer options (e.g. allow_user_segv_handler=1). 24# - msan_options - MemorySanitizer options. 25# - ubsan_options - UndefinedBehaviorSanitizer options. 26# - seed_corpus - a directory with seed corpus. 27# - seed_corpus_deps - dependencies for generating the seed corpus. 28# - grammar_options - defines a grammar used by a grammar based mutator. 29# - exclude_main - if you're going to provide your own 'main' function 30# 31# If use_libfuzzer gn flag is defined, then proper fuzzer would be build. 32# Without use_libfuzzer or use_afl a unit-test style binary would be built on 33# linux and the whole target is a no-op otherwise. 34# 35# The template wraps test() target with appropriate dependencies. 36# If any test run-time options are present (dict or libfuzzer_options), then a 37# config (.options file) file would be generated or modified in root output 38# dir (next to test). 39template("fuzzer_test") { 40 if (!disable_libfuzzer && use_fuzzing_engine) { 41 assert(defined(invoker.sources), "Need sources in $target_name.") 42 43 test_deps = [] 44 if (defined(invoker.exclude_main) && invoker.exclude_main) { 45 test_deps += [ "//testing/libfuzzer:fuzzing_engine_no_main" ] 46 } else { 47 test_deps += [ "//testing/libfuzzer:fuzzing_engine_main" ] 48 } 49 test_data_deps = [] 50 51 if (defined(invoker.deps)) { 52 test_deps += invoker.deps 53 } 54 if (defined(invoker.data_deps)) { 55 test_data_deps += invoker.data_deps 56 } 57 58 supporting_file_test_deps = [] 59 supporting_file_test_data_deps = [] 60 61 if (defined(invoker.seed_corpus) || defined(invoker.seed_corpuses)) { 62 assert(!(defined(invoker.seed_corpus) && defined(invoker.seed_corpuses)), 63 "Do not use both seed_corpus and seed_corpuses for $target_name.") 64 65 out = "$root_build_dir/$target_name" + "_seed_corpus.zip" 66 67 seed_corpus_deps = [] 68 69 if (defined(invoker.seed_corpus_deps)) { 70 seed_corpus_deps += invoker.seed_corpus_deps 71 } 72 73 action(target_name + "_seed_corpus") { 74 script = "//testing/libfuzzer/archive_corpus.py" 75 76 testonly = true 77 78 args = [ 79 "--output", 80 rebase_path(out, root_build_dir), 81 ] 82 83 if (defined(invoker.seed_corpus)) { 84 args += [ rebase_path(invoker.seed_corpus, root_build_dir) ] 85 } 86 87 if (defined(invoker.seed_corpuses)) { 88 foreach(seed_corpus_path, invoker.seed_corpuses) { 89 args += [ rebase_path(seed_corpus_path, root_build_dir) ] 90 } 91 } 92 93 outputs = [ out ] 94 95 deps = [ "//testing/libfuzzer:seed_corpus" ] + seed_corpus_deps 96 } 97 98 if (archive_seed_corpus) { 99 supporting_file_test_deps += [ ":" + target_name + "_seed_corpus" ] 100 } 101 } 102 103 if (defined(invoker.dict) || defined(invoker.libfuzzer_options) || 104 defined(invoker.asan_options) || defined(invoker.msan_options) || 105 defined(invoker.ubsan_options) || 106 defined(invoker.environment_variables) || 107 defined(invoker.grammar_options)) { 108 if (defined(invoker.dict)) { 109 # Copy dictionary to output. 110 copy(target_name + "_dict_copy") { 111 sources = [ invoker.dict ] 112 outputs = [ "$root_build_dir/" + target_name + ".dict" ] 113 } 114 supporting_file_test_deps += [ ":" + target_name + "_dict_copy" ] 115 } 116 117 # Generate .options file. 118 config_file_name = target_name + ".options" 119 action(config_file_name) { 120 script = "//testing/libfuzzer/gen_fuzzer_config.py" 121 args = [ 122 "--config", 123 rebase_path("$root_build_dir/" + config_file_name, root_build_dir), 124 ] 125 126 if (defined(invoker.dict)) { 127 args += [ 128 "--dict", 129 rebase_path("$root_build_dir/" + invoker.target_name + ".dict", 130 root_build_dir), 131 ] 132 } 133 134 if (defined(invoker.libfuzzer_options)) { 135 args += [ "--libfuzzer_options" ] 136 args += invoker.libfuzzer_options 137 } 138 139 if (defined(invoker.asan_options)) { 140 args += [ "--asan_options" ] 141 args += invoker.asan_options 142 } 143 144 if (defined(invoker.msan_options)) { 145 args += [ "--msan_options" ] 146 args += invoker.msan_options 147 } 148 149 if (defined(invoker.ubsan_options)) { 150 args += [ "--ubsan_options" ] 151 args += invoker.ubsan_options 152 } 153 154 if (defined(invoker.environment_variables)) { 155 args += [ "--environment_variables" ] 156 args += invoker.environment_variables 157 } 158 159 if (defined(invoker.grammar_options)) { 160 args += [ "--grammar_options" ] 161 args += invoker.grammar_options 162 } 163 164 outputs = [ "$root_build_dir/$config_file_name" ] 165 } 166 supporting_file_test_deps += [ ":" + config_file_name ] 167 } 168 169 if (generate_fuzzer_owners) { 170 # Generating owners files is slow, only enable when fuzzing engine is 171 # used. 172 owners_file_name = target_name + ".owners" 173 action(owners_file_name) { 174 script = "//testing/libfuzzer/gen_fuzzer_owners.py" 175 pool = "//testing/libfuzzer:fuzzer_owners_pool" 176 args = [ 177 "--owners", 178 rebase_path("$root_build_dir/" + owners_file_name, root_build_dir), 179 ] 180 181 if (defined(invoker.sources) && invoker.sources != []) { 182 args += [ "--sources" ] + rebase_path(invoker.sources, "//") 183 } else if (defined(invoker.deps) && invoker.deps != []) { 184 _full_deps = [] 185 foreach(_dep, invoker.deps) { 186 _full_deps += [ get_label_info(_dep, "dir") + ":" + 187 get_label_info(_dep, "name") ] 188 } 189 args += [ 190 "--build-dir", 191 rebase_path("$root_build_dir/", root_build_dir), 192 "--deps", 193 ] + _full_deps 194 } 195 196 outputs = [ "$root_build_dir/$owners_file_name" ] 197 } 198 supporting_file_test_data_deps += [ ":" + owners_file_name ] 199 } 200 201 # Copy to executable folder and codesign for catalyst 202 if (is_ios) { 203 # iOS only supports fuzzer in catalyst environment. 204 assert(target_environment == "catalyst") 205 206 # Loop over two kinds of deps to codesign these in two |action_foreach|s. 207 # Use the "test_deps", "test_data_deps" as identifiers in for loop. 208 foreach(_deps_type_label, 209 [ 210 "test_deps", 211 "test_data_deps", 212 ]) { 213 _dep_list = [] 214 if (_deps_type_label == "test_deps") { 215 _dep_list = supporting_file_test_deps 216 } else { 217 _dep_list = supporting_file_test_data_deps 218 } 219 _files_to_sign = [] 220 foreach(dep, _dep_list) { 221 _files_to_sign += get_target_outputs(dep) 222 } 223 if (_files_to_sign != []) { 224 _codesign_action_name = 225 target_name + "_codesign_supporting_files_" + _deps_type_label 226 action_foreach(_codesign_action_name) { 227 testonly = true 228 script = "//build/config/ios/codesign.py" 229 sources = _files_to_sign 230 _codesign_output_path = 231 "${root_build_dir}/codesign/{{source_file_part}}" 232 outputs = [ _codesign_output_path ] 233 args = [ 234 "code-sign-file", 235 "--identity=" + ios_code_signing_identity, 236 "--output=" + rebase_path(_codesign_output_path, root_build_dir), 237 "{{source}}", 238 ] 239 deps = _dep_list 240 } 241 _bundle_data_name = target_name + "_bundle_data_" + _deps_type_label 242 bundle_data(_bundle_data_name) { 243 testonly = true 244 sources = get_target_outputs(":${_codesign_action_name}") 245 outputs = [ "{{bundle_executable_dir}}/{{source_file_part}}" ] 246 public_deps = [ ":${_codesign_action_name}" ] 247 } 248 if (_deps_type_label == "test_deps") { 249 test_deps += [ ":${_bundle_data_name}" ] 250 } else { 251 test_data_deps += [ ":${_bundle_data_name}" ] 252 } 253 } 254 } 255 } else { 256 test_deps += supporting_file_test_deps 257 test_data_deps += supporting_file_test_data_deps 258 } 259 260 test(target_name) { 261 forward_variables_from(invoker, 262 [ 263 "cflags", 264 "cflags_cc", 265 "check_includes", 266 "defines", 267 "include_dirs", 268 "sources", 269 "libs", 270 "frameworks", 271 ]) 272 deps = test_deps 273 data_deps = test_data_deps 274 275 if (defined(invoker.additional_configs)) { 276 configs += invoker.additional_configs 277 } 278 configs += [ "//testing/libfuzzer:fuzzer_test_config" ] 279 280 # Used by WebRTC to suppress some Clang warnings in their codebase. 281 if (defined(invoker.suppressed_configs)) { 282 configs -= invoker.suppressed_configs 283 } 284 285 if (defined(invoker.generated_sources)) { 286 sources += invoker.generated_sources 287 } 288 289 if (is_ios) { 290 info_plist = 291 "//testing/libfuzzer/fuzzer_support_ios/fuzzer-engine-Info.plist" 292 } 293 294 if (is_mac) { 295 sources += [ "//testing/libfuzzer/libfuzzer_exports.h" ] 296 } 297 } 298 } else { 299 # noop on unsupported platforms. 300 # mark attributes as used. 301 not_needed(invoker, "*") 302 303 group(target_name) { 304 } 305 } 306} 307