1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
  310
  311
  312
  313
  314
  315
  316
  317
  318
  319
  320
  321
  322
  323
  324
  325
  326
  327
  328
  329
  330
  331
  332
  333
  334
  335
  336
  337
  338
  339
  340
  341
  342
  343
  344
  345
  346
  347
  348
  349
  350
  351
  352
  353
  354
  355
  356
  357
  358
  359
  360
  361
  362
  363
  364
  365
  366
  367
  368
  369
  370
  371
  372
  373
  374
  375
  376
  377
  378
  379
  380
  381
  382
  383
  384
  385
  386
  387
  388
  389
  390
  391
  392
  393
  394
  395
  396
  397
  398
  399
  400
  401
  402
  403
  404
  405
  406
  407
  408
  409
  410
  411
  412

build / config / sanitizers / sanitizers.gni [blame]

# Copyright 2015 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import("//build/config/cast.gni")
import("//build/config/chrome_build.gni")
import("//build/config/chromeos/args.gni")
import("//build/config/chromeos/ui_mode.gni")
import("//build/config/profiling/profiling.gni")
import("//build/toolchain/toolchain.gni")

declare_args() {
  # Compile for Address Sanitizer to find memory bugs.
  is_asan = false

  # Compile for Hardware-Assisted Address Sanitizer to find memory bugs
  # (android/arm64 only).
  # See http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
  is_hwasan = false

  # Compile for Leak Sanitizer to find leaks.
  is_lsan = false

  # Compile for Memory Sanitizer to find uninitialized reads.
  is_msan = false

  # Compile for Thread Sanitizer to find threading bugs.
  is_tsan = false

  # Compile for Undefined Behaviour Sanitizer to find various types of
  # undefined behaviour (excludes vptr checks).
  is_ubsan = false

  # Halt the program if a problem is detected.
  is_ubsan_no_recover = false

  # Track where uninitialized memory originates from. From fastest to slowest:
  # 0 - no tracking, 1 - track only the initial allocation site, 2 - track the
  # chain of stores leading from allocation site to use site.
  msan_track_origins = 2

  # Enables "param-retval" mode, which finds more uses of uninitialized data and
  # reduces code size. Behind a flag as there are a number of previously
  # undetected violations that still need to be fixed.
  # TODO(crbug.com/40240570): Default this to true and remove.
  msan_eager_checks = true

  # TODO(crbug.com/40222690): Enable everywhere.
  msan_check_use_after_dtor = is_linux

  # Use dynamic libraries instrumented by one of the sanitizers instead of the
  # standard system libraries. Set this flag to build the libraries from source.
  use_locally_built_instrumented_libraries = false

  # Compile with Control Flow Integrity to protect virtual calls and casts.
  # See http://clang.llvm.org/docs/ControlFlowIntegrity.html
  #
  # TODO(pcc): Remove this flag if/when CFI is enabled in all official builds.
  is_cfi = is_official_build && is_clang &&
           ((target_os == "linux" && target_cpu == "x64") ||
            (is_chromeos && is_chromeos_device))

  # Enable checks for indirect function calls via a function pointer.
  # TODO(pcc): remove this when we're ready to add these checks by default.
  # https://crbug.com/701919
  use_cfi_icall =
      target_os == "linux" && target_cpu == "x64" && is_official_build

  # Print detailed diagnostics when Control Flow Integrity detects a violation.
  use_cfi_diag = false

  # Let Control Flow Integrity continue execution instead of crashing when
  # printing diagnostics (use_cfi_diag = true).
  use_cfi_recover = false

  # Compile for fuzzing with LLVM LibFuzzer.
  # See http://www.chromium.org/developers/testing/libfuzzer
  use_libfuzzer = false

  # Compile for fuzzing with centipede.
  # See https://github.com/google/centipede
  use_centipede = false

  # Compile for fuzzing with AFL.
  use_afl = false

  # Compile for fuzzing with Fuzzilli.
  use_fuzzilli = false

  # Compile for fuzzing with an external engine (e.g., Grammarinator).
  use_external_fuzzing_engine = false

  # Enables core ubsan security features. Will later be removed once it matches
  # is_ubsan.
  is_ubsan_security = false

  # Helper variable for testing builds with disabled libfuzzer.
  # Not for client use.
  disable_libfuzzer = false

  # Value for -fsanitize-coverage flag. Setting this causes
  # use_sanitizer_coverage to be enabled.
  # This flag is not used for libFuzzer (use_libfuzzer=true). Instead, we use:
  #     -fsanitize=fuzzer-no-link
  # Default value when unset and use_fuzzing_engine=true:
  #     trace-pc-guard
  # Default value when unset and use_sanitizer_coverage=true:
  #     trace-pc-guard,indirect-calls
  sanitizer_coverage_flags = ""

  # A sanitizer coverage allowlist, specifying exactly which
  # files or symbol names should be instrumented, rather than all of them.
  sanitizer_coverage_allowlist = ""

  # When enabled, only relevant sanitizer defines are set, but compilation
  # happens with no extra flags. This is useful when in component build
  # enabling sanitizers only in some of the components.
  use_sanitizer_configs_without_instrumentation = false

  # When true, seed corpora archives are built.
  archive_seed_corpus = true

  # When true, only builds fuzzer targets that require high end machines to run.
  # Otherwise, builds all the targets.
  # TODO(paulsemel): once we have everything implemented on the recipe side, we
  # can change the behaviour for the false case, and only build the non high-end
  # jobs, so that they do not appear in the zip. As for now, this behaviour
  # ensures nothing breaks.
  high_end_fuzzer_targets = false
}

declare_args() {
  # Enable checks for bad casts: derived cast and unrelated cast.
  # TODO(krasin): remove this, when we're ready to add these checks by default.
  # https://crbug.com/626794
  use_cfi_cast = is_cfi && is_chromeos

  # Compile for Undefined Behaviour Sanitizer's vptr checks.
  is_ubsan_vptr = is_ubsan_security || is_ubsan
}

# Whether we are doing a fuzzer build. Normally this should be checked instead
# of checking "use_libfuzzer || use_afl" because often developers forget to
# check for "use_afl", and "use_centipede" is new.
use_fuzzing_engine = use_libfuzzer || use_afl || use_centipede ||
                     use_external_fuzzing_engine || use_fuzzilli

# Whether any UBSan subset is enabled.
# TODO(crbug.com/40248746): Unify these under a single UBSan option.
is_ubsan_any = is_ubsan || is_ubsan_vptr || is_ubsan_security

declare_args() {
  # Builds fuzztest test executables such that they support the
  # --fuzz= argument, which requires some sanitizer coverage.
  # We want to enable this only when we're *NOT* using a fuzzing
  # engine such as libfuzzer or centipede. It's generally a
  # useful option, but it requires sanitizer coverage, and that
  # could conceivably disrupt normal unit testing workflows, so we'll
  # enable it by default only in sanitizer builds.
  # Also be sure not to enable this on non-Chromium builds where
  # the required //third_party/fuzztest dependency may be absent.
  # TODO(crbug.com/40286621): enable on component builds
  enable_fuzztest_fuzz =
      !(use_libfuzzer || use_afl || use_centipede ||
        use_external_fuzzing_engine || use_fuzzilli) &&
      (is_asan || is_hwasan || is_lsan || is_tsan || is_msan || is_ubsan_any) &&
      !is_component_build && is_linux && build_with_chromium

  # Whether FUZZ_TESTs should be registered as gtests in Chromium's mainstream
  # test suites. This will cause the to run for 1 second in normal testing
  # environments such as CI. Within Chromium, we want to do this only on
  # platforms where we have ClusterFuzz support (Windows, Mac, Linux) because
  # we want ClusterFuzz to find the majority of problems rather than CI.
  # This gn arg is typically respected by general-purpose test suite
  # initialization code; targets created specifically for fuzztests alone
  # would not normally be disabled by a setting of 'false' here.
  register_fuzztests_in_test_suites =
      is_linux || is_mac || is_win || use_libfuzzer || use_afl ||
      use_centipede || use_external_fuzzing_engine
}

declare_args() {
  use_sanitizer_coverage =
      !use_clang_coverage && (use_fuzzing_engine || enable_fuzztest_fuzz ||
                              sanitizer_coverage_flags != "")
}

assert(!is_hwasan || (target_os == "android" && target_cpu == "arm64"),
       "HWASan only supported on Android ARM64 builds.")

assert(
    !(enable_fuzztest_fuzz && use_libfuzzer),
    "Can't specify enable_fuzztest_fuzz and use_libfuzzer. When libfuzzer is enabled, fuzztest executables automatically support --fuzz but provide output that's libfuzzer compatible.")

assert(
    !(enable_fuzztest_fuzz && use_centipede),
    "Can't specify enable_fuzztest_fuzz and use_centipede. The same binaries are built in a different mode to add centipede support.")

assert(
    !(enable_fuzztest_fuzz && is_component_build),
    "Can't specify enable_fuzztest_fuzz in component builds; fuzztest doesn't yet support it. Consider using use_libfuzzer=true instead which provides superficially similar functionality.")

# Disable sanitizers for non-target toolchains, and for the toolchain using
# the prebuilt Rust stdlib which has no sanitizer support with it.
if (!is_a_target_toolchain || toolchain_for_rust_host_build_tools) {
  is_asan = false
  is_cfi = false
  is_hwasan = false
  is_lsan = false
  is_msan = false
  is_tsan = false
  is_ubsan = false
  is_ubsan_no_recover = false
  is_ubsan_security = false
  is_ubsan_vptr = false
  is_ubsan_any = false
  msan_track_origins = 0
  sanitizer_coverage_flags = ""
  use_afl = false
  use_centipede = false
  use_cfi_diag = false
  use_cfi_recover = false
  use_libfuzzer = false
  use_locally_built_instrumented_libraries = false
  use_sanitizer_coverage = false
  enable_fuzztest_fuzz = false
} else if (current_cpu != "arm64") {
  is_hwasan = false
}

# Whether we are linking against a sanitizer runtime library. Among other
# things, this changes the default symbol level and other settings in order to
# prepare to create stack traces "live" using the sanitizer runtime.
using_sanitizer = is_asan || is_hwasan || is_lsan || is_tsan || is_msan ||
                  is_ubsan_any || use_sanitizer_coverage || use_cfi_diag

# Use dynamic libraries instrumented by one of the sanitizers instead of the
# standard system libraries. We have instrumented system libraries for msan,
# which requires them to prevent false positives.
# TODO(thakis): Maybe remove this variable.
use_prebuilt_instrumented_libraries = is_msan

# Whether the current fuzzing engine supports libprotobuf_mutator.
use_fuzzing_engine_with_lpm = use_libfuzzer || use_centipede

# Whether the fuzzing engine supports fuzzers which supply their own
# "main" function.
fuzzing_engine_supports_custom_main =
    use_libfuzzer || use_centipede || use_fuzzilli

# Args that are in turn dependent on other args must be in a separate
# declare_args block. User overrides are only applied at the end of a
# declare_args block.
declare_args() {
  # When true, sanitizer warnings will cause test case failures.
  fail_on_san_warnings = using_sanitizer

  # Generates an owners file for each fuzzer test.
  # TODO(crbug.com/40175535): Remove this arg when finding OWNERS is faster.
  generate_fuzzer_owners = use_fuzzing_engine

  # https://crbug.com/1002058: Code coverage works inside the sandbox via the
  # help of several helper IPCs. Unfortunately, the sandbox-only path does not
  # work well for fuzzing builds. Since fuzzing builds already disable the
  # sandbox when dumping coverage, limit the sandbox-only path to non-fuzzing
  # builds.
  # Everything is IPC on Fuchsia, so this workaround for code coverage inside
  # the sandbox does not apply.
  use_clang_profiling_inside_sandbox =
      use_clang_profiling && !use_fuzzing_engine && !is_fuchsia
}

if (sanitizer_coverage_flags == "") {
  if (enable_fuzztest_fuzz) {
    # ./fuzztest_executable --fuzz=
    # requires only this single type of coverage
    sanitizer_coverage_flags = "inline-8bit-counters"
  } else if (use_fuzzing_engine) {
    sanitizer_coverage_flags = "trace-pc-guard"
    if (use_centipede) {
      # Centipede's minimal flags are listed in //third_party/fuzztest/src/centipede/clang-flags.txt.
      # But, for users like Chromium using an up-to-date clang, we can also
      # enable extra optional types of coverage which may make Centipede more
      # effective. This list is not currently documented and has been derived
      # from discussion with centipede creators (though one is warned about at
      # https://github.com/google/centipede/blob/main/centipede_callbacks.cc#L68)
      sanitizer_coverage_flags = sanitizer_coverage_flags +
                                 ",pc-table,trace-cmp,control-flow,trace-loads"
    }
  } else if (use_sanitizer_coverage) {
    sanitizer_coverage_flags = "trace-pc-guard,indirect-calls"
  }
}

assert(!using_sanitizer || is_clang,
       "Sanitizers (is_*san) require setting is_clang = true in 'gn args'")

assert(!is_cfi || is_clang,
       "is_cfi requires setting is_clang = true in 'gn args'")

prebuilt_instrumented_libraries_available =
    is_msan && (msan_track_origins == 0 || msan_track_origins == 2)

if (use_libfuzzer && (is_linux || is_chromeos)) {
  if (is_asan) {
    # We do leak checking with libFuzzer on Linux. Set is_lsan for code that
    # relies on LEAK_SANITIZER define to avoid false positives.
    is_lsan = true
  }
}

# MSan only links Chrome properly in release builds (brettw -- 9/1/2015). The
# same is possibly true for the other non-ASan sanitizers. But regardless of
# whether it links, one would normally never run a sanitizer in debug mode.
# Running in debug mode probably indicates you forgot to set the "is_debug =
# false" flag in the build args. ASan seems to run fine in debug mode.
#
# If you find a use-case where you want to compile a sanitizer in debug mode
# and have verified it works, ask brettw and we can consider removing it from
# this condition. We may also be able to find another way to enable your case
# without having people accidentally get broken builds by compiling an
# unsupported or unadvisable configurations.
#
# For one-off testing, just comment this assertion out.
assert(!is_debug || !(is_msan || is_ubsan || is_ubsan_vptr),
       "Sanitizers should generally be used in release (set is_debug=false).")

assert(!is_msan || ((is_linux || is_chromeos) && current_cpu == "x64"),
       "MSan currently only works on 64-bit Linux and ChromeOS builds.")

assert(!is_lsan || is_asan, "is_lsan = true requires is_asan = true also.")

# ASAN build on Windows is not working in debug mode. Intercepting memory
# allocation functions is hard on Windows and not yet implemented in LLVM.
assert(!is_win || !is_debug || !is_asan,
       "ASan on Windows doesn't work in debug (set is_debug=false).")

# libFuzzer targets can fail to build or behave incorrectly when built without
# ASAN on Windows.
assert(!is_win || !use_libfuzzer || is_asan,
       "use_libfuzzer on Windows requires setting is_asan = true")

# Make sure that if we recover on detection (i.e. not crash), diagnostics are
# printed.
assert(!use_cfi_recover || use_cfi_diag,
       "Only use CFI recovery together with diagnostics.")

# TODO(crbug.com/40534102): the use_sanitizer_coverage arg is currently
# not supported by the Chromium mac_clang_x64 toolchain on iOS distribution.
# The coverage works with iOS toolchain but it is broken when the mac
# toolchain is used as a secondary one on iOS distribution. E.g., it should be
# possible to build the "net" target for iOS with the sanitizer coverage
# enabled.
assert(
    !(use_sanitizer_coverage && is_mac && target_os == "ios"),
    "crbug.com/753445: use_sanitizer_coverage=true is not supported by the " +
        "Chromium mac_clang_x64 toolchain on iOS distribution. Please set " +
        "the argument value to false.")

assert(
    sanitizer_coverage_allowlist == "" || use_sanitizer_coverage,
    "Can't specify a sanitizer coverage allowlist without using sanitizer coverage.")

# Use these lists of configs to disable instrumenting code that is part of a
# fuzzer, but which isn't being targeted (such as libprotobuf-mutator, *.pb.cc
# and libprotobuf when they are built as part of a proto fuzzer). Adding or
# removing these lists does not have any effect if use_libfuzzer or use_afl are
# not passed as arguments to gn.
not_fuzzed_remove_configs = []
not_fuzzed_remove_nonasan_configs = []

if (use_fuzzing_engine) {
  # Removing coverage should always just work.
  not_fuzzed_remove_configs += [ "//build/config/coverage:default_coverage" ]
  not_fuzzed_remove_nonasan_configs +=
      [ "//build/config/coverage:default_coverage" ]

  if (!is_msan) {
    # Allow sanitizer instrumentation to be removed if we are not using MSan
    # since binaries cannot be partially instrumented with MSan.
    not_fuzzed_remove_configs +=
        [ "//build/config/sanitizers:default_sanitizer_flags" ]

    # Certain parts of binaries must be instrumented with ASan if the rest of
    # the binary is. For these, only remove non-ASan sanitizer instrumentation.
    if (!is_asan) {
      not_fuzzed_remove_nonasan_configs +=
          [ "//build/config/sanitizers:default_sanitizer_flags" ]

      assert(not_fuzzed_remove_nonasan_configs == not_fuzzed_remove_configs)
    }
  }
}

# Options common to different fuzzer engines.
# Engine should be compiled without coverage (infinite loop in trace_cmp).
fuzzing_engine_remove_configs = [
  "//build/config/coverage:default_coverage",
  "//build/config/sanitizers:default_sanitizer_flags",
]

# Add any sanitizer flags back. In MSAN builds, instrumenting libfuzzer with
# MSAN is necessary since all parts of the binary need to be instrumented for it
# to work. ASAN builds are more subtle: libfuzzer depends on features from the
# C++ STL. If it were not instrumented, templates would be insantiated without
# ASAN from libfuzzer and with ASAN in other TUs. The linker might merge
# instrumented template instantiations with non-instrumented ones (which could
# have a different ABI) in the final binary, which is problematic for TUs
# expecting one particular ABI (https://crbug.com/915422). The other sanitizers
# are added back for the same reason.
fuzzing_engine_add_configs =
    [ "//build/config/sanitizers:default_sanitizer_flags_but_coverage" ]