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" ]