Add "hwaddress" sanitizer.

This is a new sanitizer similar to ASan, but with a few differences
from the build system perspective:
* Only runs on AArch64.
* Supports static binaries.
* Bionic itself will be built with HWASan.
* Does not have any "if a library is sanitized than the binary must
  be, too" requirements unlike ASan. Even better, individual static
  libraries can be freely sanitized or not. We propagate "nosanitize"
  from binaries to static libraries anyway, because otherwise there
  is no good way to disable hwasan on a binary as a whole.

Same a CFI, we export a list of sanitized static libraries to make.
In fact, we export separate lists for vendor and regular libraries,
because it is possible for one to be sanitized without the other
(i.e. there can be mylib.hwasan.vendor w/o mylib.hwasan or vice
versa).

Bug: 112438058, 112709969
Test: manual, part of a bigger patch set

Change-Id: Ie4fdeb522ac03cf9684526882e84dfee3807b6a7
diff --git a/cc/cc.go b/cc/cc.go
index bde8041..7f65640 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -46,6 +46,9 @@
 		ctx.TopDown("asan_deps", sanitizerDepsMutator(asan))
 		ctx.BottomUp("asan", sanitizerMutator(asan)).Parallel()
 
+		ctx.TopDown("hwasan_deps", sanitizerDepsMutator(hwasan))
+		ctx.BottomUp("hwasan", sanitizerMutator(hwasan)).Parallel()
+
 		ctx.TopDown("cfi_deps", sanitizerDepsMutator(cfi))
 		ctx.BottomUp("cfi", sanitizerMutator(cfi)).Parallel()
 
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index a360a17..39b5df4 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -226,6 +226,14 @@
 	return SanitizerRuntimeLibrary(t, "asan")
 }
 
+func HWAddressSanitizerRuntimeLibrary(t Toolchain) string {
+	return SanitizerRuntimeLibrary(t, "hwasan")
+}
+
+func HWAddressSanitizerStaticLibrary(t Toolchain) string {
+	return SanitizerRuntimeLibrary(t, "hwasan_static")
+}
+
 func UndefinedBehaviorSanitizerRuntimeLibrary(t Toolchain) string {
 	return SanitizerRuntimeLibrary(t, "ubsan_standalone")
 }
diff --git a/cc/makevars.go b/cc/makevars.go
index 88d4639..072821c 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -300,6 +300,8 @@
 
 		if target.Os.Class == android.Device {
 			ctx.Strict(secondPrefix+"ADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.AddressSanitizerRuntimeLibrary(toolchain), ".so"))
+			ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerRuntimeLibrary(toolchain), ".so"))
+			ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_STATIC_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerStaticLibrary(toolchain), ".a"))
 			ctx.Strict(secondPrefix+"UBSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain), ".so"))
 			ctx.Strict(secondPrefix+"UBSAN_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), ".a"))
 			ctx.Strict(secondPrefix+"TSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.ThreadSanitizerRuntimeLibrary(toolchain), ".so"))
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 1037181..a522a60 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -36,12 +36,15 @@
 	asanLdflags = []string{"-Wl,-u,__asan_preinit"}
 	asanLibs    = []string{"libasan"}
 
+	hwasanCflags = []string{"-mllvm", "-hwasan-with-ifunc=0", "-fno-omit-frame-pointer", "-Wno-frame-larger-than="}
+
 	cfiCflags = []string{"-flto", "-fsanitize-cfi-cross-dso",
 		"-fsanitize-blacklist=external/compiler-rt/lib/cfi/cfi_blacklist.txt"}
 	cfiLdflags = []string{"-flto", "-fsanitize-cfi-cross-dso", "-fsanitize=cfi",
 		"-Wl,-plugin-opt,O1"}
-	cfiExportsMapPath  = "build/soong/cc/config/cfi_exports.map"
-	cfiStaticLibsMutex sync.Mutex
+	cfiExportsMapPath     = "build/soong/cc/config/cfi_exports.map"
+	cfiStaticLibsMutex    sync.Mutex
+	hwasanStaticLibsMutex sync.Mutex
 
 	intOverflowCflags   = []string{"-fsanitize-blacklist=build/soong/cc/config/integer_overflow_blacklist.txt"}
 	minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer", "-fno-sanitize-recover=integer"}
@@ -59,6 +62,7 @@
 
 const (
 	asan sanitizerType = iota + 1
+	hwasan
 	tsan
 	intOverflow
 	cfi
@@ -68,6 +72,8 @@
 	switch t {
 	case asan:
 		return "asan"
+	case hwasan:
+		return "hwasan"
 	case tsan:
 		return "tsan"
 	case intOverflow:
@@ -85,8 +91,9 @@
 		Never *bool `android:"arch_variant"`
 
 		// main sanitizers
-		Address *bool `android:"arch_variant"`
-		Thread  *bool `android:"arch_variant"`
+		Address   *bool `android:"arch_variant"`
+		Thread    *bool `android:"arch_variant"`
+		Hwaddress *bool `android:"arch_variant"`
 
 		// local sanitizers
 		Undefined        *bool    `android:"arch_variant"`
@@ -131,6 +138,7 @@
 
 func init() {
 	android.RegisterMakeVarsProvider(pctx, cfiMakeVarsProvider)
+	android.RegisterMakeVarsProvider(pctx, hwasanMakeVarsProvider)
 }
 
 func (sanitize *sanitize) props() []interface{} {
@@ -216,6 +224,10 @@
 			s.Scudo = boolPtr(true)
 		}
 
+		if found, globalSanitizers = removeFromList("hwaddress", globalSanitizers); found && s.Hwaddress == nil {
+			s.Hwaddress = boolPtr(true)
+		}
+
 		if len(globalSanitizers) > 0 {
 			ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
 		}
@@ -256,8 +268,13 @@
 		s.Diag.Cfi = nil
 	}
 
+	// HWASan requires AArch64 hardware feature (top-byte-ignore).
+	if ctx.Arch().ArchType != android.Arm64 {
+		s.Hwaddress = nil
+	}
+
 	// Also disable CFI if ASAN is enabled.
-	if Bool(s.Address) {
+	if Bool(s.Address) || Bool(s.Hwaddress) {
 		s.Cfi = nil
 		s.Diag.Cfi = nil
 	}
@@ -278,6 +295,11 @@
 		s.Diag.Cfi = nil
 	}
 
+	// HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
+	if ctx.inRecovery() {
+		s.Hwaddress = nil
+	}
+
 	if ctx.staticBinary() {
 		s.Address = nil
 		s.Coverage = nil
@@ -297,15 +319,20 @@
 
 	if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
 		Bool(s.Coverage) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
-		Bool(s.Scudo)) {
+		Bool(s.Scudo) || Bool(s.Hwaddress)) {
 		sanitize.Properties.SanitizerEnabled = true
 	}
 
 	// Disable Scudo if ASan or TSan is enabled.
-	if Bool(s.Address) || Bool(s.Thread) {
+	if Bool(s.Address) || Bool(s.Thread) || Bool(s.Hwaddress) {
 		s.Scudo = nil
 	}
 
+	if Bool(s.Hwaddress) {
+		s.Address = nil
+		s.Thread = nil
+	}
+
 	if Bool(s.Coverage) {
 		if !Bool(s.Address) {
 			ctx.ModuleErrorf(`Use of "coverage" also requires "address"`)
@@ -407,6 +434,11 @@
 		diagSanitizers = append(diagSanitizers, "address")
 	}
 
+	if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+		flags.CFlags = append(flags.CFlags, hwasanCflags...)
+		sanitizers = append(sanitizers, "hwaddress")
+	}
+
 	if Bool(sanitize.Properties.Sanitize.Thread) {
 		sanitizers = append(sanitizers, "thread")
 	}
@@ -494,6 +526,8 @@
 	runtimeLibrary := ""
 	if Bool(sanitize.Properties.Sanitize.Address) {
 		runtimeLibrary = config.AddressSanitizerRuntimeLibrary(ctx.toolchain())
+	} else if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+		runtimeLibrary = config.HWAddressSanitizerRuntimeLibrary(ctx.toolchain())
 	} else if Bool(sanitize.Properties.Sanitize.Thread) {
 		runtimeLibrary = config.ThreadSanitizerRuntimeLibrary(ctx.toolchain())
 	} else if Bool(sanitize.Properties.Sanitize.Scudo) {
@@ -542,6 +576,9 @@
 	if ret.Class == "STATIC_LIBRARIES" && Bool(sanitize.Properties.Sanitize.Cfi) {
 		ret.SubName += ".cfi"
 	}
+	if ret.Class == "STATIC_LIBRARIES" && Bool(sanitize.Properties.Sanitize.Hwaddress) {
+		ret.SubName += ".hwasan"
+	}
 }
 
 func (sanitize *sanitize) inSanitizerDir() bool {
@@ -552,6 +589,8 @@
 	switch t {
 	case asan:
 		return sanitize.Properties.Sanitize.Address
+	case hwasan:
+		return sanitize.Properties.Sanitize.Hwaddress
 	case tsan:
 		return sanitize.Properties.Sanitize.Thread
 	case intOverflow:
@@ -565,12 +604,14 @@
 
 func (sanitize *sanitize) isUnsanitizedVariant() bool {
 	return !sanitize.isSanitizerEnabled(asan) &&
+		!sanitize.isSanitizerEnabled(hwasan) &&
 		!sanitize.isSanitizerEnabled(tsan) &&
 		!sanitize.isSanitizerEnabled(cfi)
 }
 
 func (sanitize *sanitize) isVariantOnProductionDevice() bool {
 	return !sanitize.isSanitizerEnabled(asan) &&
+		!sanitize.isSanitizerEnabled(hwasan) &&
 		!sanitize.isSanitizerEnabled(tsan)
 }
 
@@ -581,6 +622,8 @@
 		if !b {
 			sanitize.Properties.Sanitize.Coverage = nil
 		}
+	case hwasan:
+		sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
 	case tsan:
 		sanitize.Properties.Sanitize.Thread = boolPtr(b)
 	case intOverflow:
@@ -625,7 +668,7 @@
 	return ok && t.library || t == reuseObjTag
 }
 
-// Propagate asan requirements down from binaries
+// Propagate sanitizer requirements down from binaries
 func sanitizerDepsMutator(t sanitizerType) func(android.TopDownMutatorContext) {
 	return func(mctx android.TopDownMutatorContext) {
 		if c, ok := mctx.Module().(*Module); ok && c.sanitize.isSanitizerEnabled(t) {
@@ -636,7 +679,11 @@
 				if d, ok := child.(*Module); ok && d.sanitize != nil &&
 					!Bool(d.sanitize.Properties.Sanitize.Never) &&
 					!d.sanitize.isSanitizerExplicitlyDisabled(t) {
-					if (t == cfi && d.static()) || t != cfi {
+					if t == cfi || t == hwasan {
+						if d.static() {
+							d.sanitize.Properties.SanitizeDep = true
+						}
+					} else {
 						d.sanitize.Properties.SanitizeDep = true
 					}
 				}
@@ -728,6 +775,34 @@
 						modules[1].(*Module).Properties.PreventInstall = true
 						modules[1].(*Module).Properties.HideFromMake = true
 					}
+				} else if t == hwasan {
+					if mctx.Device() {
+						// CFI and HWASAN are currently mutually exclusive so disable
+						// CFI if this is an HWASAN variant.
+						modules[1].(*Module).sanitize.SetSanitizer(cfi, false)
+					}
+
+					if c.static() {
+						if c.useVndk() {
+							hwasanVendorStaticLibs := hwasanVendorStaticLibs(mctx.Config())
+							hwasanStaticLibsMutex.Lock()
+							*hwasanVendorStaticLibs = append(*hwasanVendorStaticLibs, c.Name())
+							hwasanStaticLibsMutex.Unlock()
+						} else {
+							hwasanStaticLibs := hwasanStaticLibs(mctx.Config())
+							hwasanStaticLibsMutex.Lock()
+							*hwasanStaticLibs = append(*hwasanStaticLibs, c.Name())
+							hwasanStaticLibsMutex.Unlock()
+						}
+					} else {
+						if isSanitizerEnabled {
+							modules[0].(*Module).Properties.PreventInstall = true
+							modules[0].(*Module).Properties.HideFromMake = true
+						} else {
+							modules[1].(*Module).Properties.PreventInstall = true
+							modules[1].(*Module).Properties.HideFromMake = true
+						}
+					}
 				}
 			}
 			c.sanitize.Properties.SanitizeDep = false
@@ -741,8 +816,21 @@
 	}).(*[]string)
 }
 
+func hwasanStaticLibs(config android.Config) *[]string {
+	return config.Once("hwasanStaticLibs", func() interface{} {
+		return &[]string{}
+	}).(*[]string)
+}
+
+func hwasanVendorStaticLibs(config android.Config) *[]string {
+	return config.Once("hwasanVendorStaticLibs", func() interface{} {
+		return &[]string{}
+	}).(*[]string)
+}
+
 func enableMinimalRuntime(sanitize *sanitize) bool {
 	if !Bool(sanitize.Properties.Sanitize.Address) &&
+		!Bool(sanitize.Properties.Sanitize.Hwaddress) &&
 		!Bool(sanitize.Properties.Sanitize.Scudo) &&
 		(Bool(sanitize.Properties.Sanitize.Integer_overflow) ||
 			len(sanitize.Properties.Sanitize.Misc_undefined) > 0) &&
@@ -759,3 +847,13 @@
 	sort.Strings(*cfiStaticLibs)
 	ctx.Strict("SOONG_CFI_STATIC_LIBRARIES", strings.Join(*cfiStaticLibs, " "))
 }
+
+func hwasanMakeVarsProvider(ctx android.MakeVarsContext) {
+	hwasanStaticLibs := hwasanStaticLibs(ctx.Config())
+	sort.Strings(*hwasanStaticLibs)
+	ctx.Strict("SOONG_HWASAN_STATIC_LIBRARIES", strings.Join(*hwasanStaticLibs, " "))
+
+	hwasanVendorStaticLibs := hwasanVendorStaticLibs(ctx.Config())
+	sort.Strings(*hwasanVendorStaticLibs)
+	ctx.Strict("SOONG_HWASAN_VENDOR_STATIC_LIBRARIES", strings.Join(*hwasanVendorStaticLibs, " "))
+}