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