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