rust: Support global sanitizers

This CL adds Rust support for the SANITIZE_TARGET options.

This CL includes a couple small fixes to related to HWASAN, ASAN,
ensuring that the Never sanitize property is respected. Notably,
additional llvm-args are passed to ensure that HWASAN-ified Rust/C
interop works correctly.

Bug: 170672854
Bug: 204915322
Test: SANITIZE_TARGET globally applies hwasan to Rust targets
Change-Id: Ia904d07b4618f72cdc95c51f88961905c240ac53
diff --git a/cc/sanitize.go b/cc/sanitize.go
index f6a9d5b..6054252 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -1303,6 +1303,10 @@
 func sanitizerMutator(t SanitizerType) func(android.BottomUpMutatorContext) {
 	return func(mctx android.BottomUpMutatorContext) {
 		if c, ok := mctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
+
+			// Make sure we're not setting CFI to any value if it's not supported.
+			cfiSupported := mctx.Module().(PlatformSanitizeable).SanitizerSupported(cfi)
+
 			if c.Binary() && c.IsSanitizerEnabled(t) {
 				modules := mctx.CreateVariations(t.variationName())
 				modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
@@ -1323,7 +1327,6 @@
 					// is redirected to the sanitized variant of the dependent module.
 					defaultVariation := t.variationName()
 					// Not all PlatformSanitizeable modules support the CFI sanitizer
-					cfiSupported := mctx.Module().(PlatformSanitizeable).SanitizerSupported(cfi)
 					mctx.SetDefaultDependencyVariation(&defaultVariation)
 
 					modules := mctx.CreateVariations("", t.variationName())
@@ -1370,7 +1373,7 @@
 						modules[0].(PlatformSanitizeable).SetInSanitizerDir()
 					}
 
-					if mctx.Device() && t.incompatibleWithCfi() {
+					if mctx.Device() && t.incompatibleWithCfi() && cfiSupported {
 						// TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
 						// are incompatible with cfi
 						modules[0].(PlatformSanitizeable).SetSanitizer(cfi, false)
diff --git a/rust/fuzz.go b/rust/fuzz.go
index a628b61..44ac68a 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -36,7 +36,7 @@
 	fuzzPackagedModule fuzz.FuzzPackagedModule
 }
 
-var _ compiler = (*binaryDecorator)(nil)
+var _ compiler = (*fuzzDecorator)(nil)
 
 // rust_binary produces a binary that is runnable on a device.
 func RustFuzzFactory() android.Module {
diff --git a/rust/sanitize.go b/rust/sanitize.go
index baa383d..5b7597e 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -15,11 +15,15 @@
 package rust
 
 import (
+	"fmt"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/rust/config"
-	"fmt"
-	"github.com/google/blueprint"
 )
 
 type SanitizeProperties struct {
@@ -59,9 +63,18 @@
 	"-Z sanitizer=address",
 }
 
+// See cc/sanitize.go's hwasanGlobalOptions for global hwasan options.
 var hwasanFlags = []string{
 	"-Z sanitizer=hwaddress",
 	"-C target-feature=+tagged-globals",
+
+	// Flags from cc/sanitize.go hwasanFlags
+	"-C llvm-args=--aarch64-enable-global-isel-at-O=-1",
+	"-C llvm-args=-fast-isel=false",
+	"-C llvm-args=-instcombine-lower-dbg-declare=0",
+
+	// Additional flags for HWASAN-ified Rust/C interop
+	"-C llvm-args=--hwasan-with-ifunc",
 }
 
 func boolPtr(v bool) *bool {
@@ -79,7 +92,46 @@
 }
 
 func (sanitize *sanitize) begin(ctx BaseModuleContext) {
-	s := sanitize.Properties.Sanitize
+	s := &sanitize.Properties.Sanitize
+
+	// Never always wins.
+	if Bool(s.Never) {
+		return
+	}
+
+	var globalSanitizers []string
+
+	if ctx.Host() {
+		if !ctx.Windows() {
+			globalSanitizers = ctx.Config().SanitizeHost()
+		}
+	} else {
+		arches := ctx.Config().SanitizeDeviceArch()
+		if len(arches) == 0 || android.InList(ctx.Arch().ArchType.Name, arches) {
+			globalSanitizers = ctx.Config().SanitizeDevice()
+		}
+	}
+
+	if len(globalSanitizers) > 0 {
+		var found bool
+
+		// Global Sanitizers
+		if found, globalSanitizers = android.RemoveFromList("hwaddress", globalSanitizers); found && s.Hwaddress == nil {
+			// TODO(b/180495975): HWASan for static Rust binaries isn't supported yet.
+			if !ctx.RustModule().StaticExecutable() {
+				s.Hwaddress = proptools.BoolPtr(true)
+			}
+		}
+
+		if found, globalSanitizers = android.RemoveFromList("address", globalSanitizers); found && s.Address == nil {
+			s.Address = proptools.BoolPtr(true)
+		}
+
+		if found, globalSanitizers = android.RemoveFromList("fuzzer", globalSanitizers); found && s.Fuzzer == nil {
+			s.Fuzzer = proptools.BoolPtr(true)
+		}
+
+	}
 
 	// TODO:(b/178369775)
 	// For now sanitizing is only supported on devices
@@ -96,7 +148,17 @@
 		s.Hwaddress = nil
 	}
 
-	if ctx.Os() == android.Android && Bool(s.Hwaddress) {
+	// HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
+	// Keep libc instrumented so that ramdisk / vendor_ramdisk / recovery can run hwasan-instrumented code if necessary.
+	if (ctx.RustModule().InRamdisk() || ctx.RustModule().InVendorRamdisk() || ctx.RustModule().InRecovery()) && !strings.HasPrefix(ctx.ModuleDir(), "bionic/libc") {
+		s.Hwaddress = nil
+	}
+
+	if Bool(s.Hwaddress) {
+		s.Address = nil
+	}
+
+	if ctx.Os() == android.Android && (Bool(s.Hwaddress) || Bool(s.Address)) {
 		sanitize.Properties.SanitizerEnabled = true
 	}
 }
@@ -149,23 +211,18 @@
 		} else if mod.IsSanitizerEnabled(cc.Hwasan) ||
 			(mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType == android.Arm64) {
 			// TODO(b/180495975): HWASan for static Rust binaries isn't supported yet.
-			if binary, ok := mod.compiler.(*binaryDecorator); ok {
-				if Bool(binary.Properties.Static_executable) {
+			if binary, ok := mod.compiler.(binaryInterface); ok {
+				if binary.staticallyLinked() {
 					mctx.ModuleErrorf("HWASan is not supported for static Rust executables yet.")
 				}
 			}
 
-			if mod.StaticallyLinked() {
-				variations = append(variations,
-					blueprint.Variation{Mutator: "link", Variation: "static"})
-				depTag = cc.StaticDepTag(false)
-				deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "hwasan_static")}
-			} else {
-				variations = append(variations,
-					blueprint.Variation{Mutator: "link", Variation: "shared"})
-				depTag = cc.SharedDepTag()
-				deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "hwasan")}
-			}
+			// Always link against the shared library -- static binaries will pull in the static
+			// library during final link if necessary
+			variations = append(variations,
+				blueprint.Variation{Mutator: "link", Variation: "shared"})
+			depTag = cc.SharedDepTag()
+			deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "hwasan")}
 		}
 
 		mctx.AddFarVariationDependencies(variations, depTag, deps...)
@@ -268,6 +325,10 @@
 	case cc.Asan:
 		return true
 	case cc.Hwasan:
+		// TODO(b/180495975): HWASan for static Rust binaries isn't supported yet.
+		if mod.StaticExecutable() {
+			return false
+		}
 		return true
 	default:
 		return false
diff --git a/rust/testing.go b/rust/testing.go
index 94cdd9d..abcf235 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -151,7 +151,12 @@
 			no_libcrt: true,
 			nocrt: true,
 			system_shared_libs: [],
-			export_include_dirs: ["libprotobuf-cpp-full-includes"],
+		}
+		cc_library {
+			name: "libclang_rt.hwasan_static-aarch64-android",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
 		}
 		rust_library {
 			name: "libstd",
@@ -246,5 +251,8 @@
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 	ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
+	})
 	registerRustSnapshotModules(ctx)
 }