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