Target sanitize properties can disable the global sanitizer.

With this change, sanitize: { address: false } disables
SANITIZE_TARGET=address for one target.

Also rename SafeStack to Safestack, because the former can not be
used as a target property.

Bug: 27729263
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 27d778e..76d6618 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -25,6 +25,14 @@
 
 type sanitizerType int
 
+func boolPtr(v bool) *bool {
+	if v {
+		return &v
+	} else {
+		return nil
+	}
+}
+
 func init() {
 	pctx.StaticVariable("clangAsanLibDir", "${clangPath}/lib64/clang/3.8/lib/linux")
 }
@@ -51,15 +59,15 @@
 		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"`
 
 		// local sanitizers
-		Undefined      bool     `android:"arch_variant"`
-		All_undefined  bool     `android:"arch_variant"`
+		Undefined      *bool    `android:"arch_variant"`
+		All_undefined  *bool    `android:"arch_variant"`
 		Misc_undefined []string `android:"arch_variant"`
-		Coverage       bool     `android:"arch_variant"`
-		SafeStack      bool     `android:"arch_variant"`
+		Coverage       *bool    `android:"arch_variant"`
+		Safestack      *bool    `android:"arch_variant"`
 
 		// value to pass to -fsantitize-recover=
 		Recover []string
@@ -82,20 +90,18 @@
 }
 
 func (sanitize *sanitize) begin(ctx BaseModuleContext) {
+	s := &sanitize.Properties.Sanitize
+
 	// Don't apply sanitizers to NDK code.
 	if ctx.sdk() {
-		sanitize.Properties.Sanitize.Never = true
+		s.Never = true
 	}
 
 	// Never always wins.
-	if sanitize.Properties.Sanitize.Never {
+	if s.Never {
 		return
 	}
 
-	if ctx.ContainsProperty("sanitize") {
-		sanitize.Properties.SanitizerEnabled = true
-	}
-
 	var globalSanitizers []string
 	if ctx.clang() {
 		if ctx.Host() {
@@ -105,48 +111,54 @@
 		}
 	}
 
-	// The sanitizer specified by the environment wins over the module.
 	if len(globalSanitizers) > 0 {
-		// wipe the enabled sanitizers
-		sanitize.Properties = SanitizeProperties{}
 		var found bool
-		if found, globalSanitizers = removeFromList("undefined", globalSanitizers); found {
-			sanitize.Properties.Sanitize.All_undefined = true
-		} else if found, globalSanitizers = removeFromList("default-ub", globalSanitizers); found {
-			sanitize.Properties.Sanitize.Undefined = true
+		if found, globalSanitizers = removeFromList("undefined", globalSanitizers); found && s.All_undefined == nil {
+			s.All_undefined = boolPtr(true)
 		}
 
-		if found, globalSanitizers = removeFromList("address", globalSanitizers); found {
-			sanitize.Properties.Sanitize.Address = true
+		if found, globalSanitizers = removeFromList("default-ub", globalSanitizers); found && s.Undefined == nil {
+			s.Undefined = boolPtr(true)
 		}
 
-		if found, globalSanitizers = removeFromList("thread", globalSanitizers); found {
-			sanitize.Properties.Sanitize.Thread = true
+		if found, globalSanitizers = removeFromList("address", globalSanitizers); found && s.Address == nil {
+			s.Address = boolPtr(true)
 		}
 
-		if found, globalSanitizers = removeFromList("coverage", globalSanitizers); found {
-			sanitize.Properties.Sanitize.Coverage = true
+		if found, globalSanitizers = removeFromList("thread", globalSanitizers); found && s.Thread == nil {
+			s.Thread = boolPtr(true)
 		}
 
-		if found, globalSanitizers = removeFromList("safe-stack", globalSanitizers); found {
-			sanitize.Properties.Sanitize.SafeStack = true
+		if found, globalSanitizers = removeFromList("coverage", globalSanitizers); found && s.Coverage == nil {
+			s.Coverage = boolPtr(true)
+		}
+
+		if found, globalSanitizers = removeFromList("safe-stack", globalSanitizers); found && s.Safestack == nil {
+			s.Safestack = boolPtr(true)
 		}
 
 		if len(globalSanitizers) > 0 {
 			ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
 		}
+	}
+	if Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) ||
+		Bool(s.Thread) || Bool(s.Coverage) || Bool(s.Safestack) {
 		sanitize.Properties.SanitizerEnabled = true
 	}
 
+	if Bool(s.All_undefined) {
+		s.Undefined = nil
+	}
+
 	if !ctx.toolchain().Is64Bit() {
 		// TSAN and SafeStack are not supported on 32-bit architectures
-		sanitize.Properties.Sanitize.Thread = false
-		sanitize.Properties.Sanitize.SafeStack = false
+		s.Thread = nil
+		s.Safestack = nil
 		// TODO(ccross): error for compile_multilib = "32"?
 	}
 
-	if sanitize.Properties.Sanitize.Coverage {
-		if !sanitize.Properties.Sanitize.Address {
+	if Bool(s.Coverage) {
+		if !Bool(s.Address) {
 			ctx.ModuleErrorf(`Use of "coverage" also requires "address"`)
 		}
 	}
@@ -159,7 +171,7 @@
 
 	if ctx.Device() {
 		deps.SharedLibs = append(deps.SharedLibs, "libdl")
-		if sanitize.Properties.Sanitize.Address {
+		if Bool(sanitize.Properties.Sanitize.Address) {
 			deps.StaticLibs = append(deps.StaticLibs, "libasan")
 		}
 	}
@@ -178,13 +190,13 @@
 
 	var sanitizers []string
 
-	if sanitize.Properties.Sanitize.All_undefined {
+	if Bool(sanitize.Properties.Sanitize.All_undefined) {
 		sanitizers = append(sanitizers, "undefined")
 		if ctx.Device() {
 			ctx.ModuleErrorf("ubsan is not yet supported on the device")
 		}
 	} else {
-		if sanitize.Properties.Sanitize.Undefined {
+		if Bool(sanitize.Properties.Sanitize.Undefined) {
 			sanitizers = append(sanitizers,
 				"bool",
 				"integer-divide-by-zero",
@@ -212,7 +224,7 @@
 		sanitizers = append(sanitizers, sanitize.Properties.Sanitize.Misc_undefined...)
 	}
 
-	if sanitize.Properties.Sanitize.Address {
+	if Bool(sanitize.Properties.Sanitize.Address) {
 		if ctx.Arch().ArchType == android.Arm {
 			// Frame pointer based unwinder in ASan requires ARM frame setup.
 			// TODO: put in flags?
@@ -241,11 +253,11 @@
 		sanitizers = append(sanitizers, "address")
 	}
 
-	if sanitize.Properties.Sanitize.Coverage {
+	if Bool(sanitize.Properties.Sanitize.Coverage) {
 		flags.CFlags = append(flags.CFlags, "-fsanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp")
 	}
 
-	if sanitize.Properties.Sanitize.SafeStack {
+	if Bool(sanitize.Properties.Sanitize.Safestack) {
 		sanitizers = append(sanitizers, "safe-stack")
 	}
 
@@ -262,7 +274,7 @@
 			flags.LdFlags = append(flags.LdFlags, sanitizeArg)
 			flags.LdFlags = append(flags.LdFlags, "-lrt", "-ldl")
 		} else {
-			if !sanitize.Properties.Sanitize.Address {
+			if !Bool(sanitize.Properties.Sanitize.Address) {
 				flags.CFlags = append(flags.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
 			}
 		}
@@ -288,9 +300,9 @@
 
 	switch t {
 	case asan:
-		return sanitize.Properties.Sanitize.Address
+		return Bool(sanitize.Properties.Sanitize.Address)
 	case tsan:
-		return sanitize.Properties.Sanitize.Thread
+		return Bool(sanitize.Properties.Sanitize.Thread)
 	default:
 		panic(fmt.Errorf("unknown sanitizerType %d", t))
 	}
@@ -299,9 +311,9 @@
 func (sanitize *sanitize) SetSanitizer(t sanitizerType, b bool) {
 	switch t {
 	case asan:
-		sanitize.Properties.Sanitize.Address = b
+		sanitize.Properties.Sanitize.Address = boolPtr(b)
 	case tsan:
-		sanitize.Properties.Sanitize.Thread = b
+		sanitize.Properties.Sanitize.Thread = boolPtr(b)
 	default:
 		panic(fmt.Errorf("unknown sanitizerType %d", t))
 	}