rust: Add HWASan build support

HWASan for static Rust executables is not supported yet.

Bug: 180495975
Test: build local test app with HWASan
Change-Id: I46e851c82a16943586ec3a789f09a58651d036e3
diff --git a/rust/androidmk.go b/rust/androidmk.go
index b0e6967..dea32a3 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -70,6 +70,11 @@
 		// If the compiler is disabled, this is a SourceProvider.
 		mod.SubAndroidMk(&ret, mod.sourceProvider)
 	}
+
+	if mod.sanitize != nil {
+		mod.SubAndroidMk(&ret, mod.sanitize)
+	}
+
 	ret.SubName += mod.Properties.SubName
 
 	return []android.AndroidMkEntries{ret}
diff --git a/rust/sanitize.go b/rust/sanitize.go
index 2498aa1..394f4c5 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -23,11 +23,12 @@
 )
 
 type SanitizeProperties struct {
-	// enable AddressSanitizer, ThreadSanitizer, or UndefinedBehaviorSanitizer
+	// enable AddressSanitizer, HWAddressSanitizer, and others.
 	Sanitize struct {
-		Address *bool `android:"arch_variant"`
-		Fuzzer  *bool `android:"arch_variant"`
-		Never   *bool `android:"arch_variant"`
+		Address   *bool `android:"arch_variant"`
+		Hwaddress *bool `android:"arch_variant"`
+		Fuzzer    *bool `android:"arch_variant"`
+		Never     *bool `android:"arch_variant"`
 	}
 	SanitizerEnabled bool `blueprint:"mutated"`
 	SanitizeDep      bool `blueprint:"mutated"`
@@ -57,6 +58,11 @@
 	"-Z sanitizer=address",
 }
 
+var hwasanFlags = []string{
+	"-Z sanitizer=hwaddress",
+	"-C target-feature=+tagged-globals",
+}
+
 func boolPtr(v bool) *bool {
 	if v {
 		return &v
@@ -83,6 +89,15 @@
 	if ctx.Os() == android.Android && Bool(s.Address) {
 		sanitize.Properties.SanitizerEnabled = true
 	}
+
+	// HWASan requires AArch64 hardware feature (top-byte-ignore).
+	if ctx.Arch().ArchType != android.Arm64 {
+		s.Hwaddress = nil
+	}
+
+	if ctx.Os() == android.Android && Bool(s.Hwaddress) {
+		sanitize.Properties.SanitizerEnabled = true
+	}
 }
 
 type sanitize struct {
@@ -99,6 +114,9 @@
 	if Bool(sanitize.Properties.Sanitize.Address) {
 		flags.RustFlags = append(flags.RustFlags, asanFlags...)
 	}
+	if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+		flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
+	}
 	return flags, deps
 }
 
@@ -111,11 +129,38 @@
 		if !mod.Enabled() {
 			return
 		}
+
+		variations := mctx.Target().Variations()
+		var depTag blueprint.DependencyTag
+		var deps []string
+
 		if Bool(mod.sanitize.Properties.Sanitize.Fuzzer) || Bool(mod.sanitize.Properties.Sanitize.Address) {
-			mctx.AddFarVariationDependencies(append(mctx.Target().Variations(), []blueprint.Variation{
-				{Mutator: "link", Variation: "shared"},
-			}...), cc.SharedDepTag(), config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan"))
+			variations = append(variations,
+				blueprint.Variation{Mutator: "link", Variation: "shared"})
+			depTag = cc.SharedDepTag()
+			deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan")}
+		} else if mod.IsSanitizerEnabled(cc.Hwasan) {
+			// 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) {
+					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")}
+			}
 		}
+
+		mctx.AddFarVariationDependencies(variations, depTag, deps...)
 	}
 }
 
@@ -128,6 +173,9 @@
 	case cc.Asan:
 		sanitize.Properties.Sanitize.Address = boolPtr(b)
 		sanitizerSet = true
+	case cc.Hwasan:
+		sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
+		sanitizerSet = true
 	default:
 		panic(fmt.Errorf("setting unsupported sanitizerType %d", t))
 	}
@@ -169,11 +217,23 @@
 		return sanitize.Properties.Sanitize.Fuzzer
 	case cc.Asan:
 		return sanitize.Properties.Sanitize.Address
+	case cc.Hwasan:
+		return sanitize.Properties.Sanitize.Hwaddress
 	default:
 		return nil
 	}
 }
 
+func (sanitize *sanitize) AndroidMk(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+	// Add a suffix for hwasan rlib libraries to allow surfacing both the sanitized and
+	// non-sanitized variants to make without a name conflict.
+	if entries.Class == "RLIB_LIBRARIES" || entries.Class == "STATIC_LIBRARIES" {
+		if sanitize.isSanitizerEnabled(cc.Hwasan) {
+			entries.SubName += ".hwasan"
+		}
+	}
+}
+
 func (mod *Module) SanitizerSupported(t cc.SanitizerType) bool {
 	if mod.Host() {
 		return false
@@ -183,6 +243,8 @@
 		return true
 	case cc.Asan:
 		return true
+	case cc.Hwasan:
+		return true
 	default:
 		return false
 	}