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# These are mostly applied only when the fuzzer is being run using the 24# libfuzzer fuzzing engine, but a few of these may be interpreted by 25# other fuzzing engines too 26# - centipede_options - options for the fuzzer (e.g. shmem_size_mb=N) 27# when running using the centipede fuzzing engine. 28# - asan_options - AddressSanitizer options (e.g. allow_user_segv_handler=1). 29# - msan_options - MemorySanitizer options. 30# - ubsan_options - UndefinedBehaviorSanitizer options. 31# - seed_corpus - a directory with seed corpus. 32# - seed_corpus_deps - dependencies for generating the seed corpus. 33# - grammar_options - defines a grammar used by a grammar based mutator. 34# - exclude_main - if you're going to provide your own 'main' function 35# - high_end_job_required - whether the fuzzer requires bigger machines to run. 36# Optional argument. 37# 38# If use_libfuzzer gn flag is defined, then proper fuzzer would be build. 39# Without use_libfuzzer or use_afl a unit-test style binary would be built on 40# linux and the whole target is a no-op otherwise. 41# 42# The template wraps test() target with appropriate dependencies. 43# If any test run-time options are present (dict or libfuzzer_options), then a 44# config (.options file) file would be generated or modified in root output 45# dir (next to test). 46template("fuzzer_test") { 47 _high_end_job_required = false 48 if (defined(invoker.high_end_job_required)) { 49 _high_end_job_required = invoker.high_end_job_required 50 } 51 52 # If the job is a high_end job and that we are currently building a high_end 53 # target, we should compile this fuzzer_test in. Otherwise, for now, we just 54 # compile everything in. 55 # TODO(crbug.com/333831251): Once CF supports high end jobs, do not compile 56 # high end targets except for high end jobs. 57 _should_build = (_high_end_job_required || !high_end_fuzzer_targets) && 58 !disable_libfuzzer && use_fuzzing_engine 59 if (_should_build) { 60 assert(defined(invoker.sources), "Need sources in $target_name.") 61 62 test_deps = [] 63 if (defined(invoker.exclude_main) && invoker.exclude_main) { 64 test_deps += [ "//testing/libfuzzer:fuzzing_engine_no_main" ] 65 } else { 66 test_deps += [ "//testing/libfuzzer:fuzzing_engine_main" ] 67 } 68 test_data_deps = [] 69 70 if (defined(invoker.deps)) { 71 test_deps += invoker.deps 72 } 73 if (defined(invoker.data_deps)) { 74 test_data_deps += invoker.data_deps 75 } 76 77 supporting_file_test_deps = [] 78 supporting_file_test_data_deps = [] 79 80 if (defined(invoker.seed_corpus) || defined(invoker.seed_corpuses)) { 81 assert(!(defined(invoker.seed_corpus) && defined(invoker.seed_corpuses)), 82 "Do not use both seed_corpus and seed_corpuses for $target_name.") 83 84 out = "$root_build_dir/$target_name" + "_seed_corpus.zip" 85 86 seed_corpus_deps = [] 87 88 if (defined(invoker.seed_corpus_deps)) { 89 seed_corpus_deps += invoker.seed_corpus_deps 90 } 91 92 action(target_name + "_seed_corpus") { 93 script = "//testing/libfuzzer/archive_corpus.py" 94 95 testonly = true 96 97 args = [ 98 "--output", 99 rebase_path(out, root_build_dir), 100 ] 101 102 if (defined(invoker.seed_corpus)) { 103 args += [ rebase_path(invoker.seed_corpus, root_build_dir) ] 104 } 105 106 if (defined(invoker.seed_corpuses)) { 107 foreach(seed_corpus_path, invoker.seed_corpuses) { 108 args += [ rebase_path(seed_corpus_path, root_build_dir) ] 109 } 110 } 111 112 outputs = [ out ] 113 114 deps = [ "//testing/libfuzzer:seed_corpus" ] + seed_corpus_deps 115 } 116 117 if (archive_seed_corpus) { 118 supporting_file_test_deps += [ ":" + target_name + "_seed_corpus" ] 119 } 120 } 121 122 if (defined(invoker.dict) || defined(invoker.libfuzzer_options) || 123 defined(invoker.centipede_options) || defined(invoker.asan_options) || 124 defined(invoker.msan_options) || defined(invoker.ubsan_options) || 125 defined(invoker.environment_variables) || 126 defined(invoker.grammar_options)) { 127 if (defined(invoker.dict)) { 128 # Copy dictionary to output. 129 copy(target_name + "_dict_copy") { 130 sources = [ invoker.dict ] 131 outputs = [ "$root_build_dir/" + target_name + ".dict" ] 132 } 133 supporting_file_test_deps += [ ":" + target_name + "_dict_copy" ] 134 } 135 136 fuzzer_name = target_name 137 138 # Generate .options file. 139 config_file_name = target_name + ".options" 140 action(config_file_name) { 141 script = "//testing/libfuzzer/gen_fuzzer_config.py" 142 args = [ 143 "--config", 144 rebase_path("$root_build_dir/" + config_file_name, root_build_dir), 145 ] 146 147 if (defined(invoker.dict)) { 148 args += [ 149 "--dict", 150 rebase_path("$root_build_dir/" + fuzzer_name + ".dict", 151 root_build_dir), 152 ] 153 } else { 154 not_needed([ "fuzzer_name" ]) 155 } 156 157 if (defined(invoker.libfuzzer_options)) { 158 args += [ "--libfuzzer_options" ] 159 args += invoker.libfuzzer_options 160 } 161 162 if (defined(invoker.centipede_options)) { 163 args += [ "--centipede_options" ] 164 args += invoker.centipede_options 165 } 166 167 if (defined(invoker.asan_options)) { 168 args += [ "--asan_options" ] 169 args += invoker.asan_options 170 } 171 172 if (defined(invoker.msan_options)) { 173 args += [ "--msan_options" ] 174 args += invoker.msan_options 175 } 176 177 if (defined(invoker.ubsan_options)) { 178 args += [ "--ubsan_options" ] 179 args += invoker.ubsan_options 180 } 181 182 if (defined(invoker.environment_variables)) { 183 args += [ "--environment_variables" ] 184 args += invoker.environment_variables 185 } 186 187 if (defined(invoker.grammar_options)) { 188 args += [ "--grammar_options" ] 189 args += invoker.grammar_options 190 } 191 192 outputs = [ "$root_build_dir/$config_file_name" ] 193 } 194 test_data_deps += [ ":" + config_file_name ] 195 supporting_file_test_deps += [ ":" + config_file_name ] 196 } 197 198 if (generate_fuzzer_owners) { 199 # Generating owners files is slow, only enable when fuzzing engine is 200 # used. 201 owners_file_name = target_name + ".owners" 202 action(owners_file_name) { 203 script = "//testing/libfuzzer/gen_fuzzer_owners.py" 204 pool = "//testing/libfuzzer:fuzzer_owners_pool" 205 args = [ 206 "--owners", 207 rebase_path("$root_build_dir/" + owners_file_name, root_build_dir), 208 ] 209 210 if (defined(invoker.sources) && invoker.sources != []) { 211 args += [ "--sources" ] + rebase_path(invoker.sources, "//") 212 } else if (defined(invoker.deps) && invoker.deps != []) { 213 _full_deps = [] 214 foreach(_dep, invoker.deps) { 215 _full_deps += [ get_label_info(_dep, "dir") + ":" + 216 get_label_info(_dep, "name") ] 217 } 218 args += [ 219 "--build-dir", 220 rebase_path("$root_build_dir/", root_build_dir), 221 "--deps", 222 ] + _full_deps 223 } 224 225 outputs = [ "$root_build_dir/$owners_file_name" ] 226 } 227 supporting_file_test_data_deps += [ ":" + owners_file_name ] 228 } 229 230 # Copy to executable folder and codesign for catalyst 231 if (is_ios) { 232 # iOS only supports fuzzer in catalyst environment. 233 assert(target_environment == "catalyst") 234 235 # Loop over two kinds of deps to codesign these in two |action_foreach|s. 236 # Use the "test_deps", "test_data_deps" as identifiers in for loop. 237 foreach(_deps_type_label, 238 [ 239 "test_deps", 240 "test_data_deps", 241 ]) { 242 _dep_list = [] 243 if (_deps_type_label == "test_deps") { 244 _dep_list = supporting_file_test_deps 245 } else { 246 _dep_list = supporting_file_test_data_deps 247 } 248 _files_to_sign = [] 249 foreach(dep, _dep_list) { 250 _files_to_sign += get_target_outputs(dep) 251 } 252 if (_files_to_sign != []) { 253 _codesign_action_name = 254 target_name + "_codesign_supporting_files_" + _deps_type_label 255 action_foreach(_codesign_action_name) { 256 testonly = true 257 script = "//build/config/ios/codesign.py" 258 sources = _files_to_sign 259 _codesign_output_path = 260 "${root_build_dir}/codesign/{{source_file_part}}" 261 outputs = [ _codesign_output_path ] 262 args = [ 263 "code-sign-file", 264 "--identity=" + ios_code_signing_identity, 265 "--output=" + rebase_path(_codesign_output_path, root_build_dir), 266 "{{source}}", 267 ] 268 deps = _dep_list 269 } 270 _bundle_data_name = target_name + "_bundle_data_" + _deps_type_label 271 bundle_data(_bundle_data_name) { 272 testonly = true 273 sources = get_target_outputs(":${_codesign_action_name}") 274 outputs = [ "{{bundle_executable_dir}}/{{source_file_part}}" ] 275 public_deps = [ ":${_codesign_action_name}" ] 276 } 277 if (_deps_type_label == "test_deps") { 278 test_deps += [ ":${_bundle_data_name}" ] 279 } else { 280 test_data_deps += [ ":${_bundle_data_name}" ] 281 } 282 } 283 } 284 } else { 285 test_deps += supporting_file_test_deps 286 test_data_deps += supporting_file_test_data_deps 287 } 288 289 test(target_name) { 290 forward_variables_from(invoker, 291 [ 292 "cflags", 293 "cflags_cc", 294 "check_includes", 295 "defines", 296 "include_dirs", 297 "output_name", 298 "sources", 299 "libs", 300 "frameworks", 301 ]) 302 deps = test_deps 303 data_deps = test_data_deps 304 305 if (defined(invoker.additional_configs)) { 306 configs += invoker.additional_configs 307 } 308 configs += [ "//testing/libfuzzer:fuzzer_test_config" ] 309 310 # Used by WebRTC to suppress some Clang warnings in their codebase. 311 if (defined(invoker.suppressed_configs)) { 312 configs -= invoker.suppressed_configs 313 } 314 315 if (defined(invoker.generated_sources)) { 316 sources += invoker.generated_sources 317 } 318 319 if (is_ios) { 320 info_plist = 321 "//testing/libfuzzer/fuzzer_support_ios/fuzzer-engine-Info.plist" 322 } 323 324 if (is_mac) { 325 sources += [ "//testing/libfuzzer/libfuzzer_exports.h" ] 326 } 327 } 328 } else { 329 # noop on unsupported platforms. 330 # mark attributes as used. 331 not_needed(invoker, "*") 332 not_needed(invoker, 333 [ 334 "deps", 335 "seed_corpus", 336 "sources", 337 ]) 338 339 group(target_name) { 340 } 341 } 342} 343