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