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