xref: /aosp_15_r20/build/soong/rust/sanitize.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1// Copyright 2020 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package rust
16
17import (
18	"fmt"
19	"strings"
20
21	"github.com/google/blueprint"
22	"github.com/google/blueprint/proptools"
23
24	"android/soong/android"
25	"android/soong/cc"
26	"android/soong/rust/config"
27)
28
29// TODO: When Rust has sanitizer-parity with CC, deduplicate this struct
30type SanitizeProperties struct {
31	// enable AddressSanitizer, HWAddressSanitizer, and others.
32	Sanitize struct {
33		Address   *bool `android:"arch_variant"`
34		Hwaddress *bool `android:"arch_variant"`
35
36		// Memory-tagging, only available on arm64
37		// if diag.memtag unset or false, enables async memory tagging
38		Memtag_heap *bool `android:"arch_variant"`
39		Fuzzer      *bool `android:"arch_variant"`
40		Never       *bool `android:"arch_variant"`
41
42		// Sanitizers to run in the diagnostic mode (as opposed to the release mode).
43		// Replaces abort() on error with a human-readable error message.
44		// Address and Thread sanitizers always run in diagnostic mode.
45		Diag struct {
46			// Memory-tagging, only available on arm64
47			// requires sanitizer.memtag: true
48			// if set, enables sync memory tagging
49			Memtag_heap *bool `android:"arch_variant"`
50		}
51	}
52	SanitizerEnabled bool `blueprint:"mutated"`
53
54	// Used when we need to place libraries in their own directory, such as ASAN.
55	InSanitizerDir bool `blueprint:"mutated"`
56}
57
58var fuzzerFlags = []string{
59	"-Z external-clangrt=true",
60
61	"-C passes='sancov-module'",
62
63	"--cfg fuzzing",
64	"-C llvm-args=-sanitizer-coverage-level=3",
65	"-C llvm-args=-sanitizer-coverage-trace-compares",
66	"-C llvm-args=-sanitizer-coverage-inline-8bit-counters",
67	"-C llvm-args=-sanitizer-coverage-pc-table",
68
69	// See https://github.com/rust-fuzz/cargo-fuzz/pull/193
70	"-C link-dead-code",
71
72	// Sancov breaks with lto
73	// TODO: Remove when https://bugs.llvm.org/show_bug.cgi?id=41734 is resolved and sancov-module works with LTO
74	"-C lto=no",
75}
76
77var asanFlags = []string{
78	"-Z external-clangrt=true",
79	"-Z sanitizer=address",
80}
81
82// See cc/sanitize.go's hwasanGlobalOptions for global hwasan options.
83var hwasanFlags = []string{
84	"-Z external-clangrt=true",
85	"-Z sanitizer=hwaddress",
86	"-C target-feature=+tagged-globals",
87
88	// Flags from cc/sanitize.go hwasanFlags
89	"-C llvm-args=--aarch64-enable-global-isel-at-O=-1",
90	"-C llvm-args=-fast-isel=false",
91	"-C llvm-args=-instcombine-lower-dbg-declare=0",
92
93	// Additional flags for HWASAN-ified Rust/C interop
94	"-C llvm-args=--hwasan-with-ifunc",
95}
96
97func init() {
98}
99func (sanitize *sanitize) props() []interface{} {
100	return []interface{}{&sanitize.Properties}
101}
102
103func (sanitize *sanitize) begin(ctx BaseModuleContext) {
104	s := &sanitize.Properties.Sanitize
105
106	// Disable sanitizers for musl x86 modules, rustc does not support any sanitizers.
107	if ctx.Os() == android.LinuxMusl && ctx.Arch().ArchType == android.X86 {
108		s.Never = proptools.BoolPtr(true)
109	}
110
111	// Never always wins.
112	if Bool(s.Never) {
113		return
114	}
115
116	// rust_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {Memtag_heap}).
117	if binary, ok := ctx.RustModule().compiler.(binaryInterface); ok && binary.testBinary() {
118		if s.Memtag_heap == nil {
119			s.Memtag_heap = proptools.BoolPtr(true)
120		}
121		if s.Diag.Memtag_heap == nil {
122			s.Diag.Memtag_heap = proptools.BoolPtr(true)
123		}
124	}
125
126	var globalSanitizers []string
127	var globalSanitizersDiag []string
128
129	if ctx.Host() {
130		if !ctx.Windows() {
131			globalSanitizers = ctx.Config().SanitizeHost()
132		}
133	} else {
134		arches := ctx.Config().SanitizeDeviceArch()
135		if len(arches) == 0 || android.InList(ctx.Arch().ArchType.Name, arches) {
136			globalSanitizers = ctx.Config().SanitizeDevice()
137			globalSanitizersDiag = ctx.Config().SanitizeDeviceDiag()
138		}
139	}
140
141	if len(globalSanitizers) > 0 {
142		var found bool
143
144		// Global Sanitizers
145		if found, globalSanitizers = android.RemoveFromList("hwaddress", globalSanitizers); found && s.Hwaddress == nil {
146			// TODO(b/204776996): HWASan for static Rust binaries isn't supported yet.
147			if !ctx.RustModule().StaticExecutable() {
148				s.Hwaddress = proptools.BoolPtr(true)
149			}
150		}
151
152		if found, globalSanitizers = android.RemoveFromList("memtag_heap", globalSanitizers); found && s.Memtag_heap == nil {
153			if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) {
154				s.Memtag_heap = proptools.BoolPtr(true)
155			}
156		}
157
158		if found, globalSanitizers = android.RemoveFromList("address", globalSanitizers); found && s.Address == nil {
159			s.Address = proptools.BoolPtr(true)
160		}
161
162		if found, globalSanitizers = android.RemoveFromList("fuzzer", globalSanitizers); found && s.Fuzzer == nil {
163			// TODO(b/204776996): HWASan for static Rust binaries isn't supported yet, and fuzzer enables HWAsan
164			if !ctx.RustModule().StaticExecutable() {
165				s.Fuzzer = proptools.BoolPtr(true)
166			}
167		}
168
169		// Global Diag Sanitizers
170		if found, globalSanitizersDiag = android.RemoveFromList("memtag_heap", globalSanitizersDiag); found &&
171			s.Diag.Memtag_heap == nil && Bool(s.Memtag_heap) {
172			s.Diag.Memtag_heap = proptools.BoolPtr(true)
173		}
174	}
175
176	// Enable Memtag for all components in the include paths (for Aarch64 only)
177	if ctx.Arch().ArchType == android.Arm64 && ctx.Os().Bionic() {
178		if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
179			if s.Memtag_heap == nil {
180				s.Memtag_heap = proptools.BoolPtr(true)
181			}
182			if s.Diag.Memtag_heap == nil {
183				s.Diag.Memtag_heap = proptools.BoolPtr(true)
184			}
185		} else if ctx.Config().MemtagHeapAsyncEnabledForPath(ctx.ModuleDir()) {
186			if s.Memtag_heap == nil {
187				s.Memtag_heap = proptools.BoolPtr(true)
188			}
189		}
190	}
191
192	// HWASan requires AArch64 hardware feature (top-byte-ignore).
193	if ctx.Arch().ArchType != android.Arm64 || !ctx.Os().Bionic() {
194		s.Hwaddress = nil
195	}
196
197	// HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
198	// Keep libc instrumented so that ramdisk / vendor_ramdisk / recovery can run hwasan-instrumented code if necessary.
199	if (ctx.RustModule().InRamdisk() || ctx.RustModule().InVendorRamdisk() || ctx.RustModule().InRecovery()) && !strings.HasPrefix(ctx.ModuleDir(), "bionic/libc") {
200		s.Hwaddress = nil
201	}
202
203	if Bool(s.Hwaddress) {
204		s.Address = nil
205	}
206
207	// Memtag_heap is only implemented on AArch64.
208	if ctx.Arch().ArchType != android.Arm64 || !ctx.Os().Bionic() {
209		s.Memtag_heap = nil
210	}
211
212	// TODO:(b/178369775)
213	// For now sanitizing is only supported on non-windows targets
214	if ctx.Os() != android.Windows && (Bool(s.Hwaddress) || Bool(s.Address) || Bool(s.Memtag_heap) || Bool(s.Fuzzer)) {
215		sanitize.Properties.SanitizerEnabled = true
216	}
217}
218
219type sanitize struct {
220	Properties SanitizeProperties
221}
222
223func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
224	if !sanitize.Properties.SanitizerEnabled {
225		return flags, deps
226	}
227
228	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
229		flags.RustFlags = append(flags.RustFlags, fuzzerFlags...)
230	}
231
232	if Bool(sanitize.Properties.Sanitize.Hwaddress) {
233		flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
234	}
235
236	if Bool(sanitize.Properties.Sanitize.Address) {
237		flags.RustFlags = append(flags.RustFlags, asanFlags...)
238		if ctx.Host() {
239			// -nodefaultlibs (provided with libc++) prevents the driver from linking
240			// libraries needed with -fsanitize=address. http://b/18650275 (WAI)
241			flags.LinkFlags = append(flags.LinkFlags, []string{"-Wl,--no-as-needed"}...)
242		}
243	}
244	return flags, deps
245}
246
247func (sanitize *sanitize) deps(ctx BaseModuleContext, deps Deps) Deps {
248	return deps
249}
250
251func rustSanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) {
252	if mod, ok := mctx.Module().(*Module); ok && mod.sanitize != nil {
253		if !mod.Enabled(mctx) {
254			return
255		}
256
257		if Bool(mod.sanitize.Properties.Sanitize.Memtag_heap) && mod.Binary() {
258			noteDep := "note_memtag_heap_async"
259			if Bool(mod.sanitize.Properties.Sanitize.Diag.Memtag_heap) {
260				noteDep = "note_memtag_heap_sync"
261			}
262			depTag := cc.StaticDepTag(true)
263			variations := append(mctx.Target().Variations(),
264				blueprint.Variation{Mutator: "link", Variation: "static"})
265			if mod.Device() {
266				variations = append(variations, mod.ImageVariation())
267			}
268			mctx.AddFarVariationDependencies(variations, depTag, noteDep)
269		}
270
271		variations := mctx.Target().Variations()
272		var depTag blueprint.DependencyTag
273		var deps []string
274
275		if mod.IsSanitizerEnabled(cc.Asan) {
276			if mod.Host() {
277				variations = append(variations,
278					blueprint.Variation{Mutator: "link", Variation: "static"})
279				depTag = cc.StaticDepTag(false)
280				deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan.static")}
281			} else {
282				variations = append(variations,
283					blueprint.Variation{Mutator: "link", Variation: "shared"})
284				depTag = cc.SharedDepTag()
285				deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan")}
286			}
287		} else if mod.IsSanitizerEnabled(cc.Hwasan) {
288			// TODO(b/204776996): HWASan for static Rust binaries isn't supported yet.
289			if binary, ok := mod.compiler.(binaryInterface); ok {
290				if binary.staticallyLinked() {
291					mctx.ModuleErrorf("HWASan is not supported for static Rust executables yet.")
292				}
293			}
294
295			// Always link against the shared library -- static binaries will pull in the static
296			// library during final link if necessary
297			variations = append(variations,
298				blueprint.Variation{Mutator: "link", Variation: "shared"})
299			depTag = cc.SharedDepTag()
300			deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "hwasan")}
301		}
302
303		if len(deps) > 0 {
304			mctx.AddFarVariationDependencies(variations, depTag, deps...)
305		}
306	}
307}
308
309func (sanitize *sanitize) SetSanitizer(t cc.SanitizerType, b bool) {
310	sanitizerSet := false
311	switch t {
312	case cc.Fuzzer:
313		sanitize.Properties.Sanitize.Fuzzer = proptools.BoolPtr(b)
314		sanitizerSet = true
315	case cc.Asan:
316		sanitize.Properties.Sanitize.Address = proptools.BoolPtr(b)
317		sanitizerSet = true
318	case cc.Hwasan:
319		sanitize.Properties.Sanitize.Hwaddress = proptools.BoolPtr(b)
320		sanitizerSet = true
321	case cc.Memtag_heap:
322		sanitize.Properties.Sanitize.Memtag_heap = proptools.BoolPtr(b)
323		sanitizerSet = true
324	default:
325		panic(fmt.Errorf("setting unsupported sanitizerType %d", t))
326	}
327	if b && sanitizerSet {
328		sanitize.Properties.SanitizerEnabled = true
329	}
330}
331
332func (m *Module) UbsanRuntimeNeeded() bool {
333	return false
334}
335
336func (m *Module) MinimalRuntimeNeeded() bool {
337	return false
338}
339
340func (m *Module) UbsanRuntimeDep() bool {
341	return false
342}
343
344func (m *Module) MinimalRuntimeDep() bool {
345	return false
346}
347
348// Check if the sanitizer is explicitly disabled (as opposed to nil by
349// virtue of not being set).
350func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t cc.SanitizerType) bool {
351	if sanitize == nil {
352		return false
353	}
354	if Bool(sanitize.Properties.Sanitize.Never) {
355		return true
356	}
357	sanitizerVal := sanitize.getSanitizerBoolPtr(t)
358	return sanitizerVal != nil && *sanitizerVal == false
359}
360
361// There isn't an analog of the method above (ie:isSanitizerExplicitlyEnabled)
362// because enabling a sanitizer either directly (via the blueprint) or
363// indirectly (via a mutator) sets the bool ptr to true, and you can't
364// distinguish between the cases. It isn't needed though - both cases can be
365// treated identically.
366func (sanitize *sanitize) isSanitizerEnabled(t cc.SanitizerType) bool {
367	if sanitize == nil || !sanitize.Properties.SanitizerEnabled {
368		return false
369	}
370
371	sanitizerVal := sanitize.getSanitizerBoolPtr(t)
372	return sanitizerVal != nil && *sanitizerVal == true
373}
374
375func (sanitize *sanitize) getSanitizerBoolPtr(t cc.SanitizerType) *bool {
376	switch t {
377	case cc.Fuzzer:
378		return sanitize.Properties.Sanitize.Fuzzer
379	case cc.Asan:
380		return sanitize.Properties.Sanitize.Address
381	case cc.Hwasan:
382		return sanitize.Properties.Sanitize.Hwaddress
383	case cc.Memtag_heap:
384		return sanitize.Properties.Sanitize.Memtag_heap
385	default:
386		return nil
387	}
388}
389
390func (sanitize *sanitize) AndroidMk(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
391	// Add a suffix for hwasan rlib libraries to allow surfacing both the sanitized and
392	// non-sanitized variants to make without a name conflict.
393	if entries.Class == "RLIB_LIBRARIES" || entries.Class == "STATIC_LIBRARIES" {
394		if sanitize.isSanitizerEnabled(cc.Hwasan) {
395			entries.SubName += ".hwasan"
396		}
397	}
398}
399
400func (mod *Module) SanitizerSupported(t cc.SanitizerType) bool {
401	// Sanitizers are not supported on Windows targets.
402	if mod.Os() == android.Windows {
403		return false
404	}
405	switch t {
406	case cc.Fuzzer:
407		return true
408	case cc.Asan:
409		return true
410	case cc.Hwasan:
411		// TODO(b/180495975): HWASan for static Rust binaries isn't supported yet.
412		if mod.StaticExecutable() {
413			return false
414		}
415		return true
416	case cc.Memtag_heap:
417		return true
418	default:
419		return false
420	}
421}
422
423func (mod *Module) IsSanitizerEnabled(t cc.SanitizerType) bool {
424	return mod.sanitize.isSanitizerEnabled(t)
425}
426
427func (mod *Module) IsSanitizerExplicitlyDisabled(t cc.SanitizerType) bool {
428	// Sanitizers are not supported on Windows targets.
429	if mod.Os() == android.Windows {
430		return true
431	}
432
433	return mod.sanitize.isSanitizerExplicitlyDisabled(t)
434}
435
436func (mod *Module) SetSanitizer(t cc.SanitizerType, b bool) {
437	if !Bool(mod.sanitize.Properties.Sanitize.Never) {
438		mod.sanitize.SetSanitizer(t, b)
439	}
440}
441
442func (mod *Module) StaticallyLinked() bool {
443	if lib, ok := mod.compiler.(libraryInterface); ok {
444		return lib.rlib() || lib.static()
445	} else if binary, ok := mod.compiler.(binaryInterface); ok {
446		return binary.staticallyLinked()
447	}
448	return false
449}
450
451func (mod *Module) SetInSanitizerDir() {
452	mod.sanitize.Properties.InSanitizerDir = true
453}
454
455func (mod *Module) SanitizeNever() bool {
456	return Bool(mod.sanitize.Properties.Sanitize.Never)
457}
458
459var _ cc.PlatformSanitizeable = (*Module)(nil)
460
461func IsSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
462	switch t := tag.(type) {
463	case dependencyTag:
464		return t.library
465	default:
466		return cc.IsSanitizableDependencyTag(tag)
467	}
468}
469
470func (m *Module) SanitizableDepTagChecker() cc.SantizableDependencyTagChecker {
471	return IsSanitizableDependencyTag
472}
473