Support "memtag_heap" sanitizer.

Memtag_heap adds an ELF note that enables MTE heap tagging in
bionic/scudo. Ignored on non-executables. With diagnostic
(diag:{memtag_heap:true}) enables the SYNC mode, otherwise - ASYNC mode.

Memtag_heap defaults to set (with diag) on cc_test targets, unset
otherwise. Ignored on non MTE-compatible hardware.

Bug: b/135772972
Test: soong tests

Change-Id: I88fd0f159e609e17bd13487749980a1ba02cb91c
diff --git a/cc/cc.go b/cc/cc.go
index c0c85f9..ca2bd4f 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -420,6 +420,7 @@
 type ModuleContextIntf interface {
 	static() bool
 	staticBinary() bool
+	testBinary() bool
 	header() bool
 	binary() bool
 	object() bool
@@ -1261,6 +1262,10 @@
 	return ctx.mod.staticBinary()
 }
 
+func (ctx *moduleContextImpl) testBinary() bool {
+	return ctx.mod.testBinary()
+}
+
 func (ctx *moduleContextImpl) header() bool {
 	return ctx.mod.Header()
 }
@@ -2961,6 +2966,15 @@
 	return false
 }
 
+func (c *Module) testBinary() bool {
+	if test, ok := c.linker.(interface {
+		testBinary() bool
+	}); ok {
+		return test.testBinary()
+	}
+	return false
+}
+
 // Header returns true if the module is a header-only variant. (See cc/library.go header()).
 func (c *Module) Header() bool {
 	if h, ok := c.linker.(interface {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 3502d5f..1ab1b82 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -4461,3 +4461,105 @@
 		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
 	}
 }
+
+func checkHasImplicitDep(t *testing.T, m android.TestingModule, name string) {
+	implicits := m.Rule("ld").Implicits
+	for _, lib := range implicits {
+		if strings.Contains(lib.Rel(), name) {
+			return
+		}
+	}
+
+	t.Errorf("%q is not found in implicit deps of module %q", name, m.Module().(*Module).Name())
+}
+
+func checkDoesNotHaveImplicitDep(t *testing.T, m android.TestingModule, name string) {
+	implicits := m.Rule("ld").Implicits
+	for _, lib := range implicits {
+		if strings.Contains(lib.Rel(), name) {
+			t.Errorf("%q is found in implicit deps of module %q", name, m.Module().(*Module).Name())
+		}
+	}
+}
+
+func TestSanitizeMemtagHeap(t *testing.T) {
+	ctx := testCc(t, `
+		cc_library_static {
+			name: "libstatic",
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_library_shared {
+			name: "libshared",
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_library {
+			name: "libboth",
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_binary {
+			name: "binary",
+			shared_libs: [ "libshared" ],
+			static_libs: [ "libstatic" ],
+		}
+
+		cc_binary {
+			name: "binary_true",
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_binary {
+			name: "binary_true_sync",
+			sanitize: { memtag_heap: true, diag: { memtag_heap: true }, },
+		}
+
+		cc_binary {
+			name: "binary_false",
+			sanitize: { memtag_heap: false },
+		}
+
+		cc_test {
+			name: "test",
+			gtest: false,
+		}
+
+		cc_test {
+			name: "test_true",
+			gtest: false,
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_test {
+			name: "test_false",
+			gtest: false,
+			sanitize: { memtag_heap: false },
+		}
+
+		cc_test {
+			name: "test_true_async",
+			gtest: false,
+			sanitize: { memtag_heap: true, diag: { memtag_heap: false }  },
+		}
+
+		`)
+
+	variant := "android_arm64_armv8-a"
+	note_async := "note_memtag_heap_async"
+	note_sync := "note_memtag_heap_sync"
+	note_any := "note_memtag_"
+
+	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("libshared", "android_arm64_armv8-a_shared"), note_any)
+	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("libboth", "android_arm64_armv8-a_shared"), note_any)
+
+	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("binary", variant), note_any)
+	checkHasImplicitDep(t, ctx.ModuleForTests("binary_true", variant), note_async)
+	checkHasImplicitDep(t, ctx.ModuleForTests("binary_true_sync", variant), note_sync)
+	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("binary_false", variant), note_any)
+
+	checkHasImplicitDep(t, ctx.ModuleForTests("test", variant), note_sync)
+	checkHasImplicitDep(t, ctx.ModuleForTests("test_true", variant), note_async)
+	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("test_false", variant), note_any)
+	checkHasImplicitDep(t, ctx.ModuleForTests("test_true_async", variant), note_async)
+}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 5992611..13e1780 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -89,6 +89,7 @@
 	cfi
 	scs
 	Fuzzer
+	memtag_heap
 )
 
 // Name of the sanitizer variation for this sanitizer type
@@ -106,6 +107,8 @@
 		return "cfi"
 	case scs:
 		return "scs"
+	case memtag_heap:
+		return "memtag_heap"
 	case Fuzzer:
 		return "fuzzer"
 	default:
@@ -120,6 +123,8 @@
 		return "address"
 	case hwasan:
 		return "hwaddress"
+	case memtag_heap:
+		return "memtag_heap"
 	case tsan:
 		return "thread"
 	case intOverflow:
@@ -179,6 +184,7 @@
 	Integer_overflow *bool    `android:"arch_variant"`
 	Scudo            *bool    `android:"arch_variant"`
 	Scs              *bool    `android:"arch_variant"`
+	Memtag_heap      *bool    `android:"arch_variant"`
 
 	// A modifier for ASAN and HWASAN for write only instrumentation
 	Writeonly *bool `android:"arch_variant"`
@@ -190,6 +196,7 @@
 		Undefined        *bool    `android:"arch_variant"`
 		Cfi              *bool    `android:"arch_variant"`
 		Integer_overflow *bool    `android:"arch_variant"`
+		Memtag_heap      *bool    `android:"arch_variant"`
 		Misc_undefined   []string `android:"arch_variant"`
 		No_recover       []string `android:"arch_variant"`
 	} `android:"arch_variant"`
@@ -330,6 +337,9 @@
 			}
 			s.Writeonly = boolPtr(true)
 		}
+		if found, globalSanitizers = removeFromList("memtag_heap", globalSanitizers); found && s.Memtag_heap == nil {
+			s.Memtag_heap = boolPtr(true)
+		}
 
 		if len(globalSanitizers) > 0 {
 			ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
@@ -351,6 +361,12 @@
 		}
 	}
 
+	// cc_test targets default to SYNC MemTag.
+	if ctx.testBinary() && s.Memtag_heap == nil {
+		s.Memtag_heap = boolPtr(true)
+		s.Diag.Memtag_heap = boolPtr(true)
+	}
+
 	// Enable CFI for all components in the include paths (for Aarch64 only)
 	if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
 		s.Cfi = boolPtr(true)
@@ -381,6 +397,11 @@
 		s.Scs = nil
 	}
 
+	// memtag_heap is only implemented on AArch64.
+	if ctx.Arch().ArchType != android.Arm64 {
+		s.Memtag_heap = nil
+	}
+
 	// Also disable CFI if ASAN is enabled.
 	if Bool(s.Address) || Bool(s.Hwaddress) {
 		s.Cfi = boolPtr(false)
@@ -435,7 +456,7 @@
 
 	if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
 		Bool(s.Fuzzer) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
-		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs)) {
+		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap)) {
 		sanitize.Properties.SanitizerEnabled = true
 	}
 
@@ -717,6 +738,8 @@
 		return sanitize.Properties.Sanitize.Cfi
 	case scs:
 		return sanitize.Properties.Sanitize.Scs
+	case memtag_heap:
+		return sanitize.Properties.Sanitize.Memtag_heap
 	case Fuzzer:
 		return sanitize.Properties.Sanitize.Fuzzer
 	default:
@@ -731,6 +754,7 @@
 		!sanitize.isSanitizerEnabled(tsan) &&
 		!sanitize.isSanitizerEnabled(cfi) &&
 		!sanitize.isSanitizerEnabled(scs) &&
+		!sanitize.isSanitizerEnabled(memtag_heap) &&
 		!sanitize.isSanitizerEnabled(Fuzzer)
 }
 
@@ -756,6 +780,8 @@
 		sanitize.Properties.Sanitize.Cfi = boolPtr(b)
 	case scs:
 		sanitize.Properties.Sanitize.Scs = boolPtr(b)
+	case memtag_heap:
+		sanitize.Properties.Sanitize.Memtag_heap = boolPtr(b)
 	case Fuzzer:
 		sanitize.Properties.Sanitize.Fuzzer = boolPtr(b)
 	default:
@@ -1032,6 +1058,20 @@
 			sanitizers = append(sanitizers, "shadow-call-stack")
 		}
 
+		if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.binary() {
+			noteDep := "note_memtag_heap_async"
+			if Bool(c.sanitize.Properties.Sanitize.Diag.Memtag_heap) {
+				noteDep = "note_memtag_heap_sync"
+			}
+			depTag := libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: true}
+			variations := append(mctx.Target().Variations(),
+				blueprint.Variation{Mutator: "link", Variation: "static"})
+			if c.Device() {
+				variations = append(variations, c.ImageVariation())
+			}
+			mctx.AddFarVariationDependencies(variations, depTag, noteDep)
+		}
+
 		if Bool(c.sanitize.Properties.Sanitize.Fuzzer) {
 			sanitizers = append(sanitizers, "fuzzer-no-link")
 		}
diff --git a/cc/test.go b/cc/test.go
index 4ff5bf6..f715a8d 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -236,6 +236,10 @@
 	return BoolDefault(test.Properties.Gtest, true)
 }
 
+func (test *testDecorator) testBinary() bool {
+	return true
+}
+
 func (test *testDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
 	if !test.gtest() {
 		return flags
diff --git a/cc/testing.go b/cc/testing.go
index 8d92ea2..903f76c 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -445,6 +445,14 @@
 			stl: "none",
 			system_shared_libs: [],
 		}
+
+		cc_library_static {
+			name: "note_memtag_heap_async",
+		}
+
+		cc_library_static {
+			name: "note_memtag_heap_sync",
+		}
 	`
 
 	supportLinuxBionic := false