• 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# - 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