• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2014 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
5import("//build/config/cast.gni")
6import("//build/config/chrome_build.gni")
7import("//build/config/clang/clang.gni")
8import("//build/config/rust.gni")
9import("//build/config/sanitizers/sanitizers.gni")
10import("//build/toolchain/toolchain.gni")
11import("//build_overrides/build.gni")
12
13if (is_ios) {
14  import("//build/config/ios/ios_sdk.gni")
15}
16
17# Contains the dependencies needed for sanitizers to link into executables and
18# shared_libraries.
19group("deps") {
20  if (using_sanitizer) {
21    public_configs = [
22      # Even when a target removes default_sanitizer_flags, it may be depending
23      # on a library that did not remove default_sanitizer_flags. Thus, we need
24      # to add the ldflags here as well as in default_sanitizer_flags.
25      ":default_sanitizer_ldflags",
26    ]
27    deps = []
28    if (!is_fuchsia) {
29      if (is_win) {
30        exe = ".exe"
31      } else {
32        exe = ""
33      }
34      data = [
35        "//tools/valgrind/asan/",
36        "$clang_base_path/bin/llvm-symbolizer${exe}",
37      ]
38    }
39    if (is_asan || is_lsan || is_msan || is_tsan || is_ubsan || is_ubsan_vptr ||
40        is_ubsan_security) {
41      public_configs += [ ":sanitizer_options_link_helper" ]
42      deps += [ ":options_sources" ]
43    }
44    if (use_prebuilt_instrumented_libraries ||
45        use_locally_built_instrumented_libraries) {
46      deps += [ "//third_party/instrumented_libraries:deps" ]
47    }
48  }
49  if (fail_on_san_warnings) {
50    data += [ "//tools/memory/sanitizer/escalate_sanitizer_warnings.py" ]
51  }
52  if (is_asan) {
53    if (is_win || is_apple) {
54      data_deps = [ ":copy_asan_runtime" ]
55    }
56    if (is_apple) {
57      public_deps = [ ":asan_runtime_bundle_data" ]
58    }
59  }
60  if (use_centipede || enable_fuzztest_fuzz) {
61    # For executables which aren't actual fuzzers, we need stubs for
62    # the sanitizer coverage symbols, because we'll still be generating
63    # .o files which depend on them.
64    deps += [ "//third_party/fuzztest:centipede_weak_sancov_stubs" ]
65  }
66}
67
68assert(!(is_win && is_asan && current_cpu == "x86"),
69       "ASan is only supported in 64-bit builds on Windows.")
70
71if ((is_apple || is_win) && is_asan) {
72  if (is_mac || (is_ios && target_environment == "catalyst")) {
73    _clang_rt_dso_path = "darwin/libclang_rt.asan_osx_dynamic.dylib"
74  } else if (is_ios) {
75    _clang_rt_dso_path = "darwin/libclang_rt.asan_iossim_dynamic.dylib"
76  } else if (is_win && current_cpu == "x64") {
77    _clang_rt_dso_path = "windows/clang_rt.asan_dynamic-x86_64.dll"
78  }
79
80  _clang_rt_dso_full_path =
81      "$clang_base_path/lib/clang/$clang_version/lib/$_clang_rt_dso_path"
82
83  if (!is_ios) {
84    copy("copy_asan_runtime") {
85      sources = [ _clang_rt_dso_full_path ]
86      outputs = [ "$root_out_dir/{{source_file_part}}" ]
87    }
88  } else {
89    # On iOS, the runtime library need to be code signed (adhoc signature)
90    # starting with Xcode 8, so use an action instead of a copy on iOS.
91    action("copy_asan_runtime") {
92      script = "//build/config/ios/codesign.py"
93      sources = [ _clang_rt_dso_full_path ]
94      outputs = [ "$root_out_dir/" + get_path_info(sources[0], "file") ]
95      args = [
96        "code-sign-file",
97        "--identity=" + ios_code_signing_identity,
98        "--output=" + rebase_path(outputs[0], root_build_dir),
99        rebase_path(sources[0], root_build_dir),
100      ]
101    }
102  }
103
104  if (is_apple) {
105    bundle_data("asan_runtime_bundle_data") {
106      sources = get_target_outputs(":copy_asan_runtime")
107      outputs = [ "{{bundle_executable_dir}}/{{source_file_part}}" ]
108      public_deps = [ ":copy_asan_runtime" ]
109    }
110  }
111}
112
113config("sanitizer_options_link_helper") {
114  if (is_apple) {
115    ldflags = [ "-Wl,-u,__sanitizer_options_link_helper" ]
116  } else if (!is_win) {
117    ldflags = [ "-Wl,-u_sanitizer_options_link_helper" ]
118  }
119}
120
121static_library("options_sources") {
122  # This is a static_library instead of a source_set, as it shouldn't be
123  # unconditionally linked into targets.
124  visibility = [
125    ":deps",
126    "//:gn_all",
127  ]
128  sources = [ "//build/sanitizers/sanitizer_options.cc" ]
129
130  # Don't compile this target with any sanitizer code. It can be called from
131  # the sanitizer runtimes, so instrumenting these functions could cause
132  # recursive calls into the runtime if there is an error.
133  configs -= [ "//build/config/sanitizers:default_sanitizer_flags" ]
134
135  if (is_asan) {
136    if (!defined(asan_suppressions_file)) {
137      asan_suppressions_file = "//build/sanitizers/asan_suppressions.cc"
138    }
139    sources += [ asan_suppressions_file ]
140  }
141
142  if (is_lsan) {
143    if (!defined(lsan_suppressions_file)) {
144      lsan_suppressions_file = "//build/sanitizers/lsan_suppressions.cc"
145    }
146    sources += [ lsan_suppressions_file ]
147  }
148
149  if (is_tsan) {
150    if (!defined(tsan_suppressions_file)) {
151      tsan_suppressions_file = "//build/sanitizers/tsan_suppressions.cc"
152    }
153    sources += [ tsan_suppressions_file ]
154  }
155}
156
157# Applies linker flags necessary when either :deps or :default_sanitizer_flags
158# are used.
159config("default_sanitizer_ldflags") {
160  visibility = [
161    ":default_sanitizer_flags",
162    ":deps",
163
164    # https://crbug.com/360158.
165    "//tools/ipc_fuzzer/fuzzer:ipc_fuzzer",
166  ]
167
168  if (is_posix || is_fuchsia) {
169    sanitizers = []  # sanitizers applicable to both clang and rustc
170    ldflags = []
171    rustflags = []
172    if (is_asan) {
173      sanitizers += [ "address" ]
174    }
175    if (is_hwasan) {
176      sanitizers += [ "hwaddress" ]
177    }
178    if (is_lsan) {
179      # In Chromium, is_lsan always implies is_asan. ASAN includes LSAN.
180      # It seems harmless to pass both options to clang, but it doesn't
181      # work on rustc, so apply this option to clang only.
182      ldflags += [ "-fsanitize=leak" ]
183    }
184    if (is_tsan) {
185      sanitizers += [ "thread" ]
186    }
187    if (is_msan) {
188      sanitizers += [ "memory" ]
189    }
190    if (is_ubsan || is_ubsan_security) {
191      ldflags += [ "-fsanitize=undefined" ]
192    }
193    if (is_ubsan_vptr) {
194      ldflags += [ "-fsanitize=vptr" ]
195    }
196    foreach(sanitizer, sanitizers) {
197      ldflags += [ "-fsanitize=$sanitizer" ]
198      rustflags += [ "-Zsanitizer=$sanitizer" ]
199    }
200
201    if (use_sanitizer_coverage) {
202      if (use_libfuzzer) {
203        ldflags += [ "-fsanitize=fuzzer-no-link" ]
204        if (is_mac) {
205          # TODO(crbug.com/926588): on macOS, dead code stripping does not work
206          # well with `pc-table` instrumentation enabled by `fuzzer-no-link`.
207          ldflags += [ "-fno-sanitize-coverage=pc-table" ]
208        }
209      } else {
210        ldflags += [ "-fsanitize-coverage=$sanitizer_coverage_flags" ]
211      }
212    }
213
214    if (is_cfi && current_toolchain == default_toolchain) {
215      ldflags += [ "-fsanitize=cfi-vcall" ]
216      if (use_cfi_cast) {
217        ldflags += [
218          "-fsanitize=cfi-derived-cast",
219          "-fsanitize=cfi-unrelated-cast",
220        ]
221      }
222      if (use_cfi_icall) {
223        ldflags += [ "-fsanitize=cfi-icall" ]
224      }
225      if (use_cfi_diag) {
226        ldflags += [ "-fno-sanitize-trap=cfi" ]
227        if (use_cfi_recover) {
228          ldflags += [ "-fsanitize-recover=cfi" ]
229        }
230      }
231    }
232  } else if (is_win) {
233    # Windows directly calls link.exe instead of the compiler driver when
234    # linking.  Hence, pass the runtime libraries instead of -fsanitize=address
235    # or -fsanitize=fuzzer.
236    if (is_asan && is_component_build) {
237      # In the static-library build, ASan libraries are different for
238      # executables and dlls, see link_executable and link_shared_library below.
239      # This here handles only the component build.
240      assert(current_cpu == "x64", "WinASan unsupported architecture")
241      libs = [
242        "clang_rt.asan_dynamic-x86_64.lib",
243        "clang_rt.asan_dynamic_runtime_thunk-x86_64.lib",
244      ]
245    }
246    if (use_libfuzzer) {
247      assert(current_cpu == "x64", "LibFuzzer unsupported architecture")
248      assert(!is_component_build,
249             "LibFuzzer only supports non-component builds on Windows")
250
251      # Incremental linking causes padding that messes up SanitizerCoverage.
252      # Don't do it.
253      ldflags = [ "/INCREMENTAL:NO" ]
254    }
255  }
256}
257
258config("common_sanitizer_flags") {
259  cflags = []
260
261  if (using_sanitizer) {
262    assert(is_clang, "sanitizers only supported with clang")
263
264    # Allow non-default toolchains to enable sanitizers in toolchain_args even
265    # in official builds.
266    assert(current_toolchain != default_toolchain || !is_official_build,
267           "sanitizers not supported in official builds")
268
269    cflags += [
270      # Column info in debug data confuses Visual Studio's debugger, so don't
271      # use this by default.  However, clusterfuzz needs it for good
272      # attribution of reports to CLs, so turn it on there.
273      "-gcolumn-info",
274    ]
275
276    # Frame pointers are controlled in //build/config/compiler:default_stack_frames
277  }
278}
279
280config("asan_flags") {
281  cflags = []
282  if (is_asan) {
283    cflags += [ "-fsanitize=address" ]
284    if (!is_win && !is_apple && !is_fuchsia) {
285      # TODO(crbug.com/1459233, crbug.com/1462248): This causes asan
286      # odr-violation errors in rust code, and link failures for cros/asan.
287      # Clang recently turned it on by default for all ELF targets (it was
288      # already on for Fuchsia). Pass the flag to turn it back off.
289      cflags += [ "-fno-sanitize-address-globals-dead-stripping" ]
290    }
291    if (is_win) {
292      if (!defined(asan_win_blocklist_path)) {
293        asan_win_blocklist_path =
294            rebase_path("//tools/memory/asan/blocklist_win.txt", root_build_dir)
295      }
296      cflags += [ "-fsanitize-ignorelist=$asan_win_blocklist_path" ]
297    }
298  }
299}
300
301config("link_executable") {
302  if (is_asan && is_win && !is_component_build) {
303    assert(current_cpu == "x64", "WinASan unsupported architecture")
304    ldflags = [ "-wholearchive:clang_rt.asan-x86_64.lib" ]
305  }
306}
307
308config("link_shared_library") {
309  if (is_asan && is_win && !is_component_build) {
310    assert(current_cpu == "x64", "WinASan unsupported architecture")
311    libs = [ "clang_rt.asan_dll_thunk-x86_64.lib" ]
312  }
313}
314
315config("cfi_flags") {
316  cflags = []
317  rustflags = []
318  if (is_cfi && current_toolchain == default_toolchain) {
319    if (!defined(cfi_ignorelist_path)) {
320      cfi_ignorelist_path =
321          rebase_path("//tools/cfi/ignores.txt", root_build_dir)
322    }
323    cflags += [
324      "-fsanitize=cfi-vcall",
325      "-fsanitize-ignorelist=$cfi_ignorelist_path",
326    ]
327
328    if (toolchain_supports_rust_thin_lto) {
329      # sanitize=cfi implies -fsplit-lto-unit, and Rust needs to match
330      # behaviour.  Rust needs to know the linker will be doing LTO in this case
331      # or it rejects the Zsplit-lto-unit flag.
332      # TODO(crbug.com/1442331): Add -Zsanitize=cfi instead.
333      rustflags += [
334        "-Zsplit-lto-unit",
335        "-Clinker-plugin-lto=yes",
336      ]
337    } else {
338      # Don't include bitcode if it won't be used.
339      rustflags += [ "-Cembed-bitcode=no" ]
340    }
341
342    if (use_cfi_cast) {
343      cflags += [
344        "-fsanitize=cfi-derived-cast",
345        "-fsanitize=cfi-unrelated-cast",
346      ]
347    }
348
349    if (use_cfi_icall) {
350      cflags += [ "-fsanitize=cfi-icall" ]
351      # TODO(crbug.com/1442331): Add cflags += [
352      # "-fsanitize-cfi-icall-experimental-normalize-integers" ]
353      # TODO(crbug.com/1442331): Add rustflags += [
354      # "-Zsanitizer-cfi-normalize-integers" ].
355    }
356
357    if (use_cfi_diag) {
358      cflags += [ "-fno-sanitize-trap=cfi" ]
359      if (is_win) {
360        cflags += [
361          "/Oy-",
362          "/Ob0",
363        ]
364      } else {
365        cflags += [
366          "-fno-inline-functions",
367          "-fno-inline",
368          "-fno-omit-frame-pointer",
369          "-O1",
370        ]
371      }
372      if (use_cfi_recover) {
373        cflags += [ "-fsanitize-recover=cfi" ]
374      }
375    }
376  }
377}
378
379# crbug.com/785442: Fix cfi-icall failures for code that casts pointer argument
380# types in function pointer type signatures.
381config("cfi_icall_generalize_pointers") {
382  if (is_clang && is_cfi && use_cfi_icall) {
383    cflags = [ "-fsanitize-cfi-icall-generalize-pointers" ]
384  }
385}
386
387config("cfi_icall_disable") {
388  if (is_clang && is_cfi && use_cfi_icall) {
389    cflags = [ "-fno-sanitize=cfi-icall" ]
390  }
391}
392
393config("coverage_flags") {
394  cflags = []
395  if (use_sanitizer_coverage) {
396    # Used by sandboxing code to allow coverage dump to be written on the disk.
397    defines = [ "SANITIZER_COVERAGE" ]
398
399    if (use_libfuzzer) {
400      cflags += [ "-fsanitize=fuzzer-no-link" ]
401      if (is_mac) {
402        # TODO(crbug.com/926588): on macOS, dead code stripping does not work
403        # well with `pc-table` instrumentation enabled by `fuzzer-no-link`.
404        cflags += [ "-fno-sanitize-coverage=pc-table" ]
405      }
406    } else {
407      cflags += [
408        "-fsanitize-coverage=$sanitizer_coverage_flags",
409        "-mllvm",
410        "-sanitizer-coverage-prune-blocks=1",
411      ]
412      if (current_cpu == "arm") {
413        # http://crbug.com/517105
414        cflags += [
415          "-mllvm",
416          "-sanitizer-coverage-block-threshold=0",
417        ]
418      }
419    }
420  }
421}
422
423config("hwasan_flags") {
424  if (is_hwasan) {
425    asmflags = [ "-fsanitize=hwaddress" ]
426    cflags = [ "-fsanitize=hwaddress" ]
427  }
428}
429
430config("lsan_flags") {
431  if (is_lsan) {
432    cflags = [ "-fsanitize=leak" ]
433  }
434}
435
436config("msan_flags") {
437  if (is_msan) {
438    assert(is_linux || is_chromeos,
439           "msan only supported on linux x86_64/ChromeOS")
440    if (!defined(msan_ignorelist_path)) {
441      msan_ignorelist_path =
442          rebase_path("//tools/msan/ignorelist.txt", root_build_dir)
443    }
444    cflags = [
445      "-fsanitize=memory",
446      "-fsanitize-memory-track-origins=$msan_track_origins",
447      "-fsanitize-ignorelist=$msan_ignorelist_path",
448
449      # TODO(https://crbug.com/1317909): evaluate and possibly enable
450      "-fno-sanitize-memory-use-after-dtor",
451
452      # TODO(https://crbug.com/1369167): evaluate and possibly enable
453      "-fno-sanitize-memory-param-retval",
454    ]
455  }
456}
457
458config("tsan_flags") {
459  if (is_tsan) {
460    assert(is_linux || is_chromeos, "tsan only supported on linux x86_64")
461    if (!defined(tsan_ignorelist_path)) {
462      tsan_ignorelist_path =
463          rebase_path("//tools/memory/tsan_v2/ignores.txt", root_build_dir)
464    }
465    cflags = [
466      "-fsanitize=thread",
467      "-fsanitize-ignorelist=$tsan_ignorelist_path",
468    ]
469  }
470}
471
472config("ubsan_flags") {
473  cflags = []
474  if (is_ubsan) {
475    if (!defined(ubsan_ignorelist_path)) {
476      ubsan_ignorelist_path =
477          rebase_path("//tools/ubsan/ignorelist.txt", root_build_dir)
478    }
479    # TODO(crbug.com/1502579): Enable all of -fsanitize=undefined. Note that
480    # both this list and Clang's defaults omit -fsanitize=float-divide-by-zero.
481    # C and C++ leave it undefined to accommodate non-IEEE floating point, but
482    # we assume the compiler implements IEEE floating point, which does define
483    # division by zero.
484    cflags += [
485      "-fsanitize=alignment",
486      "-fsanitize=bool",
487      "-fsanitize=bounds",
488      "-fsanitize=builtin",
489      "-fsanitize=integer-divide-by-zero",
490      "-fsanitize=null",
491      "-fsanitize=nonnull-attribute",
492      "-fsanitize=object-size",
493      "-fsanitize=return",
494      "-fsanitize=returns-nonnull-attribute",
495      "-fsanitize=shift-exponent",
496      "-fsanitize=signed-integer-overflow",
497      "-fsanitize=unreachable",
498      "-fsanitize=vla-bound",
499      "-fsanitize-ignorelist=$ubsan_ignorelist_path",
500    ]
501
502    # Chromecast ubsan builds fail to compile with these
503    # experimental flags, so only add them to non-chromecast ubsan builds.
504    if (!is_castos && !is_cast_android) {
505      cflags += [
506        # Employ the experimental PBQP register allocator to avoid slow
507        # compilation on files with too many basic blocks.
508        # See http://crbug.com/426271.
509        "-mllvm",
510        "-regalloc=pbqp",
511
512        # Speculatively use coalescing to slightly improve the code generated
513        # by PBQP regallocator. May increase compile time.
514        "-mllvm",
515        "-pbqp-coalescing",
516      ]
517    }
518  }
519}
520
521config("ubsan_no_recover") {
522  if (is_ubsan_no_recover) {
523    cflags = [ "-fno-sanitize-recover=undefined" ]
524  }
525}
526
527config("ubsan_security_flags") {
528  if (is_ubsan_security) {
529    if (!defined(ubsan_security_ignorelist_path)) {
530      ubsan_security_ignorelist_path =
531          rebase_path("//tools/ubsan/security_ignorelist.txt", root_build_dir)
532    }
533    cflags = [
534      "-fsanitize=function",
535      "-fsanitize=shift",
536      "-fsanitize=signed-integer-overflow",
537      "-fsanitize=vla-bound",
538      "-fsanitize-ignorelist=$ubsan_security_ignorelist_path",
539    ]
540  }
541}
542
543config("ubsan_vptr_flags") {
544  if (is_ubsan_vptr) {
545    if (!defined(ubsan_vptr_ignorelist_path)) {
546      ubsan_vptr_ignorelist_path =
547          rebase_path("//tools/ubsan/vptr_ignorelist.txt", root_build_dir)
548    }
549    cflags = [
550      "-fsanitize=vptr",
551      "-fsanitize-ignorelist=$ubsan_vptr_ignorelist_path",
552    ]
553  }
554}
555
556config("fuzzing_build_mode") {
557  if (use_fuzzing_engine) {
558    defines = [ "FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION" ]
559  }
560}
561
562all_sanitizer_configs = [
563  ":common_sanitizer_flags",
564  ":coverage_flags",
565  ":default_sanitizer_ldflags",
566  ":asan_flags",
567  ":cfi_flags",
568  ":hwasan_flags",
569  ":lsan_flags",
570  ":msan_flags",
571  ":tsan_flags",
572  ":ubsan_flags",
573  ":ubsan_no_recover",
574  ":ubsan_security_flags",
575  ":ubsan_vptr_flags",
576  ":fuzzing_build_mode",
577]
578
579# This config is applied by default to all targets. It sets the compiler flags
580# for sanitizer usage, or, if no sanitizer is set, does nothing.
581#
582# This needs to be in a separate config so that targets can opt out of
583# sanitizers (by removing the config) if they desire. Even if a target
584# removes this config, executables & shared libraries should still depend on
585# :deps if any of their dependencies have not opted out of sanitizers.
586# Keep this list in sync with default_sanitizer_flags_but_ubsan_vptr.
587config("default_sanitizer_flags") {
588  configs = all_sanitizer_configs
589
590  if (use_sanitizer_configs_without_instrumentation) {
591    configs = []
592  }
593}
594
595# This config is equivalent to default_sanitizer_flags, but excludes ubsan_vptr.
596# This allows to selectively disable ubsan_vptr, when needed. In particular,
597# if some third_party code is required to be compiled without rtti, which
598# is a requirement for ubsan_vptr.
599config("default_sanitizer_flags_but_ubsan_vptr") {
600  configs = all_sanitizer_configs - [ ":ubsan_vptr_flags" ]
601
602  if (use_sanitizer_configs_without_instrumentation) {
603    configs = []
604  }
605}
606
607config("default_sanitizer_flags_but_coverage") {
608  configs = all_sanitizer_configs - [ ":coverage_flags" ]
609
610  if (use_sanitizer_configs_without_instrumentation) {
611    configs = []
612  }
613}
614
615# This config is used by parts of code that aren't targeted in fuzzers and
616# therefore don't need coverage instrumentation and possibly wont need
617# sanitizer instrumentation either. The config also tells the compiler to
618# perform additional optimizations on the configured code and ensures that
619# linking it to the rest of the binary which is instrumented with sanitizers
620# works. The config only does anything if the build is a fuzzing build.
621config("not_fuzzed") {
622  if (use_fuzzing_engine) {
623    # Since we aren't instrumenting with coverage, code size is less of a
624    # concern, so use a more aggressive optimization level than
625    # optimize_for_fuzzing (-O1). When given multiple optimization flags, clang
626    # obeys the last one, so as long as this flag comes after -O1, it should work.
627    # Since this config will always be depended on after
628    # "//build/config/compiler:default_optimization" (which adds -O1 when
629    # optimize_for_fuzzing is true), -O2 should always be the second flag. Even
630    # though this sounds fragile, it isn't a big deal if it breaks, since proto
631    # fuzzers will still work, they will just be slightly slower.
632    cflags = [ "-O2" ]
633
634    # We need to include this config when we remove default_sanitizer_flags or
635    # else there will be linking errors. We would remove default_sanitizer_flags
636    # here as well, but gn doesn't permit this.
637    if (!is_msan) {
638      # We don't actually remove sanitization when MSan is being used so there
639      # is no need to add default_sanitizer_ldflags in that case
640      configs = [ ":default_sanitizer_ldflags" ]
641    }
642  }
643}
644