• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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