Merge "Print error code when bazel invocation fails"
diff --git a/android/testing.go b/android/testing.go
index 8fcf440..29af71f 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -203,6 +203,10 @@
 	ctx.PreArchMutators(f)
 }
 
+func (ctx *TestContext) ModuleProvider(m blueprint.Module, p blueprint.ProviderKey) interface{} {
+	return ctx.Context.ModuleProvider(m, p)
+}
+
 func (ctx *TestContext) PreDepsMutators(f RegisterMutatorFunc) {
 	ctx.preDeps = append(ctx.preDeps, f)
 }
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 64bb7dd..3da7651 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -301,7 +301,6 @@
 	baseInstallerPath := "fuzz"
 
 	binary.baseInstaller = NewBaseInstaller(baseInstallerPath, baseInstallerPath, InstallInData)
-	module.sanitize.SetSanitizer(Fuzzer, true)
 
 	fuzzBin := &fuzzBinary{
 		binaryDecorator: binary,
@@ -315,7 +314,11 @@
 
 	// The fuzzer runtime is not present for darwin host modules, disable cc_fuzz modules when targeting darwin.
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		disableDarwinAndLinuxBionic := struct {
+
+		extraProps := struct {
+			Sanitize struct {
+				Fuzzer *bool
+			}
 			Target struct {
 				Darwin struct {
 					Enabled *bool
@@ -325,9 +328,10 @@
 				}
 			}
 		}{}
-		disableDarwinAndLinuxBionic.Target.Darwin.Enabled = BoolPtr(false)
-		disableDarwinAndLinuxBionic.Target.Linux_bionic.Enabled = BoolPtr(false)
-		ctx.AppendProperties(&disableDarwinAndLinuxBionic)
+		extraProps.Sanitize.Fuzzer = BoolPtr(true)
+		extraProps.Target.Darwin.Enabled = BoolPtr(false)
+		extraProps.Target.Linux_bionic.Enabled = BoolPtr(false)
+		ctx.AppendProperties(&extraProps)
 
 		targetFramework := fuzz.GetFramework(ctx, fuzz.Cc)
 		if !fuzz.IsValidFrameworkForModule(targetFramework, fuzz.Cc, fuzzBin.fuzzPackagedModule.FuzzProperties.Fuzzing_frameworks) {
diff --git a/cc/genrule.go b/cc/genrule.go
index 4ef990c..d1c4c2a 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -48,6 +48,8 @@
 //
 //	CC_NATIVE_BRIDGE  the name of the subdirectory that native bridge libraries are stored in if
 //	                  the architecture has native bridge enabled, empty if it is disabled.
+//
+//	CC_OS             the name of the OS the command is being executed for.
 func GenRuleFactory() android.Module {
 	module := genrule.NewGenRule()
 
@@ -68,8 +70,9 @@
 func genruleCmdModifier(ctx android.ModuleContext, cmd string) string {
 	target := ctx.Target()
 	arch := target.Arch.ArchType
-	return fmt.Sprintf("CC_ARCH=%s CC_NATIVE_BRIDGE=%s CC_MULTILIB=%s && %s",
-		arch.Name, target.NativeBridgeRelativePath, arch.Multilib, cmd)
+	osName := target.Os.Name
+	return fmt.Sprintf("CC_ARCH=%s CC_NATIVE_BRIDGE=%s CC_MULTILIB=%s CC_OS=%s && %s",
+		arch.Name, target.NativeBridgeRelativePath, arch.Multilib, osName, cmd)
 }
 
 var _ android.ImageInterface = (*GenruleExtraProperties)(nil)
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 1842e5a..ba21004 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -759,10 +759,10 @@
 	if sanitize == nil {
 		return nil
 	}
-	if Bool(sanitize.Properties.Sanitize.Address) && sanitized.Address.Srcs != nil {
+	if sanitize.isSanitizerEnabled(Asan) && sanitized.Address.Srcs != nil {
 		return sanitized.Address.Srcs
 	}
-	if Bool(sanitize.Properties.Sanitize.Hwaddress) && sanitized.Hwaddress.Srcs != nil {
+	if sanitize.isSanitizerEnabled(Hwasan) && sanitized.Hwaddress.Srcs != nil {
 		return sanitized.Hwaddress.Srcs
 	}
 	return sanitized.None.Srcs
diff --git a/cc/sanitize.go b/cc/sanitize.go
index d3fc221..12ff9ba 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -171,6 +171,20 @@
 	}
 }
 
+// shouldPropagateToSharedLibraryDeps returns whether a sanitizer type should propagate to share
+// dependencies. In most cases, sanitizers only propagate to static dependencies; however, some
+// sanitizers also must be enabled for shared libraries for linking.
+func (t SanitizerType) shouldPropagateToSharedLibraryDeps() bool {
+	switch t {
+	case Fuzzer:
+		// Typically, shared libs are not split. However, for fuzzer, we split even for shared libs
+		// because a library sanitized for fuzzer can't be linked from a library that isn't sanitized
+		// for fuzzer.
+		return true
+	default:
+		return false
+	}
+}
 func (*Module) SanitizerSupported(t SanitizerType) bool {
 	switch t {
 	case Asan:
@@ -286,15 +300,72 @@
 	Blocklist *string
 }
 
+type sanitizeMutatedProperties struct {
+	// Whether sanitizers can be enabled on this module
+	Never *bool `blueprint:"mutated"`
+
+	// Whether ASan (Address sanitizer) is enabled for this module.
+	// Hwaddress sanitizer takes precedence over this sanitizer.
+	Address *bool `blueprint:"mutated"`
+	// Whether TSan (Thread sanitizer) is enabled for this module
+	Thread *bool `blueprint:"mutated"`
+	// Whether HWASan (Hardware Address sanitizer) is enabled for this module
+	Hwaddress *bool `blueprint:"mutated"`
+
+	// Whether Undefined behavior sanitizer is enabled for this module
+	All_undefined *bool `blueprint:"mutated"`
+	// Whether undefined behavior sanitizer subset is enabled for this module
+	Undefined *bool `blueprint:"mutated"`
+	// List of specific undefined behavior sanitizers enabled for this module
+	Misc_undefined []string `blueprint:"mutated"`
+	// Whether Fuzzeris enabled for this module
+	Fuzzer *bool `blueprint:"mutated"`
+	// whether safe-stack sanitizer is enabled for this module
+	Safestack *bool `blueprint:"mutated"`
+	// Whether cfi sanitizer is enabled for this module
+	Cfi *bool `blueprint:"mutated"`
+	// Whether signed/unsigned integer overflow sanitizer is enabled for this module
+	Integer_overflow *bool `blueprint:"mutated"`
+	// Whether scudo sanitizer is enabled for this module
+	Scudo *bool `blueprint:"mutated"`
+	// Whether shadow-call-stack sanitizer is enabled for this module.
+	Scs *bool `blueprint:"mutated"`
+	// Whether Memory-tagging is enabled for this module
+	Memtag_heap *bool `blueprint:"mutated"`
+	// Whether Memory-tagging stack instrumentation is enabled for this module
+	Memtag_stack *bool `blueprint:"mutated"`
+
+	// Whether a modifier for ASAN and HWASAN for write only instrumentation is enabled for this
+	// module
+	Writeonly *bool `blueprint:"mutated"`
+
+	// Sanitizers to run in the diagnostic mode (as opposed to the release mode).
+	Diag struct {
+		// Whether Undefined behavior sanitizer, diagnostic mode is enabled for this module
+		Undefined *bool `blueprint:"mutated"`
+		// Whether cfi sanitizer, diagnostic mode is enabled for this module
+		Cfi *bool `blueprint:"mutated"`
+		// Whether signed/unsigned integer overflow sanitizer, diagnostic mode is enabled for this
+		// module
+		Integer_overflow *bool `blueprint:"mutated"`
+		// Whether Memory-tagging, diagnostic mode is enabled for this module
+		Memtag_heap *bool `blueprint:"mutated"`
+		// List of specific undefined behavior sanitizers enabled in diagnostic mode
+		Misc_undefined []string `blueprint:"mutated"`
+	} `blueprint:"mutated"`
+}
+
 type SanitizeProperties struct {
-	Sanitize          SanitizeUserProps `android:"arch_variant"`
-	SanitizerEnabled  bool              `blueprint:"mutated"`
-	MinimalRuntimeDep bool              `blueprint:"mutated"`
-	BuiltinsDep       bool              `blueprint:"mutated"`
-	UbsanRuntimeDep   bool              `blueprint:"mutated"`
-	InSanitizerDir    bool              `blueprint:"mutated"`
-	Sanitizers        []string          `blueprint:"mutated"`
-	DiagSanitizers    []string          `blueprint:"mutated"`
+	Sanitize        SanitizeUserProps         `android:"arch_variant"`
+	SanitizeMutated sanitizeMutatedProperties `blueprint:"mutated"`
+
+	SanitizerEnabled  bool     `blueprint:"mutated"`
+	MinimalRuntimeDep bool     `blueprint:"mutated"`
+	BuiltinsDep       bool     `blueprint:"mutated"`
+	UbsanRuntimeDep   bool     `blueprint:"mutated"`
+	InSanitizerDir    bool     `blueprint:"mutated"`
+	Sanitizers        []string `blueprint:"mutated"`
+	DiagSanitizers    []string `blueprint:"mutated"`
 }
 
 type sanitize struct {
@@ -317,8 +388,42 @@
 	return []interface{}{&sanitize.Properties}
 }
 
+func (p *sanitizeMutatedProperties) copyUserPropertiesToMutated(userProps *SanitizeUserProps) {
+	p.Never = userProps.Never
+	p.Address = userProps.Address
+	p.All_undefined = userProps.All_undefined
+	p.Cfi = userProps.Cfi
+	p.Fuzzer = userProps.Fuzzer
+	p.Hwaddress = userProps.Hwaddress
+	p.Integer_overflow = userProps.Integer_overflow
+	p.Memtag_heap = userProps.Memtag_heap
+	p.Memtag_stack = userProps.Memtag_stack
+	p.Safestack = userProps.Safestack
+	p.Scs = userProps.Scs
+	p.Scudo = userProps.Scudo
+	p.Thread = userProps.Thread
+	p.Undefined = userProps.Undefined
+	p.Writeonly = userProps.Writeonly
+
+	p.Misc_undefined = make([]string, 0, len(userProps.Misc_undefined))
+	for _, v := range userProps.Misc_undefined {
+		p.Misc_undefined = append(p.Misc_undefined, v)
+	}
+
+	p.Diag.Cfi = userProps.Diag.Cfi
+	p.Diag.Integer_overflow = userProps.Diag.Integer_overflow
+	p.Diag.Memtag_heap = userProps.Diag.Memtag_heap
+	p.Diag.Undefined = userProps.Diag.Undefined
+
+	p.Diag.Misc_undefined = make([]string, 0, len(userProps.Diag.Misc_undefined))
+	for _, v := range userProps.Diag.Misc_undefined {
+		p.Diag.Misc_undefined = append(p.Diag.Misc_undefined, v)
+	}
+}
+
 func (sanitize *sanitize) begin(ctx BaseModuleContext) {
-	s := &sanitize.Properties.Sanitize
+	s := &sanitize.Properties.SanitizeMutated
+	s.copyUserPropertiesToMutated(&sanitize.Properties.Sanitize)
 
 	// Don't apply sanitizers to NDK code.
 	if ctx.useSdk() {
@@ -614,20 +719,21 @@
 	return false
 }
 
-func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
-	if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
+func (s *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
+	if !s.Properties.SanitizerEnabled && !s.Properties.UbsanRuntimeDep {
 		return flags
 	}
+	sanProps := &s.Properties.SanitizeMutated
 
 	// Currently unwinding through tagged frames for exceptions is broken, so disable memtag stack
 	// in that case, so we don't end up tagging those.
 	// TODO(b/174878242): Remove once https://r.android.com/2251926 is included in toolchain.
 	if android.InList("-fexceptions", flags.Local.CFlags) || android.InList("-fexceptions", flags.Global.CFlags) {
-		sanitize.Properties.Sanitize.Memtag_stack = nil
-		_, sanitize.Properties.Sanitizers = android.RemoveFromList("memtag-stack", sanitize.Properties.Sanitizers)
+		sanProps.Memtag_stack = nil
+		_, s.Properties.Sanitizers = android.RemoveFromList("memtag-stack", s.Properties.Sanitizers)
 	}
 
-	if Bool(sanitize.Properties.Sanitize.Address) {
+	if Bool(sanProps.Address) {
 		if ctx.Arch().ArchType == android.Arm {
 			// Frame pointer based unwinder in ASan requires ARM frame setup.
 			// TODO: put in flags?
@@ -636,7 +742,7 @@
 		flags.Local.CFlags = append(flags.Local.CFlags, asanCflags...)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, asanLdflags...)
 
-		if Bool(sanitize.Properties.Sanitize.Writeonly) {
+		if Bool(sanProps.Writeonly) {
 			flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-asan-instrument-reads=0")
 		}
 
@@ -657,7 +763,7 @@
 		}
 	}
 
-	if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+	if Bool(sanProps.Hwaddress) {
 		flags.Local.CFlags = append(flags.Local.CFlags, hwasanCflags...)
 
 		for _, flag := range hwasanCommonflags {
@@ -667,12 +773,12 @@
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,"+flag)
 		}
 
-		if Bool(sanitize.Properties.Sanitize.Writeonly) {
+		if Bool(sanProps.Writeonly) {
 			flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-hwasan-instrument-reads=0")
 		}
 	}
 
-	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
+	if Bool(sanProps.Fuzzer) {
 		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize=fuzzer-no-link")
 
 		// TODO(b/131771163): LTO and Fuzzer support is mutually incompatible.
@@ -701,7 +807,7 @@
 		flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN`)
 	}
 
-	if Bool(sanitize.Properties.Sanitize.Cfi) {
+	if Bool(sanProps.Cfi) {
 		if ctx.Arch().ArchType == android.Arm {
 			// __cfi_check needs to be built as Thumb (see the code in linker_cfi.cpp). LLVM is not set up
 			// to do this on a function basis, so force Thumb on the entire module.
@@ -710,7 +816,7 @@
 
 		flags.Local.CFlags = append(flags.Local.CFlags, cfiCflags...)
 		flags.Local.AsFlags = append(flags.Local.AsFlags, cfiAsflags...)
-		if Bool(sanitize.Properties.Sanitize.Config.Cfi_assembly_support) {
+		if Bool(s.Properties.Sanitize.Config.Cfi_assembly_support) {
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-cfi-canonical-jump-tables")
 		}
 		// Only append the default visibility flag if -fvisibility has not already been set
@@ -726,7 +832,7 @@
 		}
 	}
 
-	if Bool(sanitize.Properties.Sanitize.Memtag_stack) {
+	if Bool(sanProps.Memtag_stack) {
 		flags.Local.CFlags = append(flags.Local.CFlags, memtagStackCommonFlags...)
 		// TODO(fmayer): remove -Wno-error once https://reviews.llvm.org/D127917 is in Android toolchain.
 		flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-error=frame-larger-than")
@@ -737,20 +843,20 @@
 		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-fatal-warnings")
 	}
 
-	if (Bool(sanitize.Properties.Sanitize.Memtag_heap) || Bool(sanitize.Properties.Sanitize.Memtag_stack)) && ctx.binary() {
-		if Bool(sanitize.Properties.Sanitize.Diag.Memtag_heap) {
+	if (Bool(sanProps.Memtag_heap) || Bool(sanProps.Memtag_stack)) && ctx.binary() {
+		if Bool(sanProps.Diag.Memtag_heap) {
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=sync")
 		} else {
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=async")
 		}
 	}
 
-	if Bool(sanitize.Properties.Sanitize.Integer_overflow) {
+	if Bool(sanProps.Integer_overflow) {
 		flags.Local.CFlags = append(flags.Local.CFlags, intOverflowCflags...)
 	}
 
-	if len(sanitize.Properties.Sanitizers) > 0 {
-		sanitizeArg := "-fsanitize=" + strings.Join(sanitize.Properties.Sanitizers, ",")
+	if len(s.Properties.Sanitizers) > 0 {
+		sanitizeArg := "-fsanitize=" + strings.Join(s.Properties.Sanitizers, ",")
 		flags.Local.CFlags = append(flags.Local.CFlags, sanitizeArg)
 		flags.Local.AsFlags = append(flags.Local.AsFlags, sanitizeArg)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
@@ -774,7 +880,7 @@
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
 		}
 
-		if Bool(sanitize.Properties.Sanitize.Fuzzer) {
+		if Bool(sanProps.Fuzzer) {
 			// When fuzzing, we wish to crash with diagnostics on any bug.
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap=all", "-fno-sanitize-recover=all")
 		} else if ctx.Host() {
@@ -783,7 +889,7 @@
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
 		}
 
-		if enableMinimalRuntime(sanitize) {
+		if enableMinimalRuntime(s) {
 			flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
 		}
 
@@ -797,22 +903,22 @@
 		}
 	}
 
-	if len(sanitize.Properties.DiagSanitizers) > 0 {
-		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap="+strings.Join(sanitize.Properties.DiagSanitizers, ","))
+	if len(s.Properties.DiagSanitizers) > 0 {
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap="+strings.Join(s.Properties.DiagSanitizers, ","))
 	}
 	// FIXME: enable RTTI if diag + (cfi or vptr)
 
-	if sanitize.Properties.Sanitize.Recover != nil {
+	if s.Properties.Sanitize.Recover != nil {
 		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-recover="+
-			strings.Join(sanitize.Properties.Sanitize.Recover, ","))
+			strings.Join(s.Properties.Sanitize.Recover, ","))
 	}
 
-	if sanitize.Properties.Sanitize.Diag.No_recover != nil {
+	if s.Properties.Sanitize.Diag.No_recover != nil {
 		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-recover="+
-			strings.Join(sanitize.Properties.Sanitize.Diag.No_recover, ","))
+			strings.Join(s.Properties.Sanitize.Diag.No_recover, ","))
 	}
 
-	blocklist := android.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blocklist)
+	blocklist := android.OptionalPathForModuleSrc(ctx, s.Properties.Sanitize.Blocklist)
 	if blocklist.Valid() {
 		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-ignorelist="+blocklist.String())
 		flags.CFlagsDeps = append(flags.CFlagsDeps, blocklist.Path())
@@ -821,47 +927,47 @@
 	return flags
 }
 
-func (sanitize *sanitize) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (s *sanitize) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	// Add a suffix for cfi/hwasan/scs-enabled static/header libraries to allow surfacing
 	// both the sanitized and non-sanitized variants to make without a name conflict.
 	if entries.Class == "STATIC_LIBRARIES" || entries.Class == "HEADER_LIBRARIES" {
-		if Bool(sanitize.Properties.Sanitize.Cfi) {
+		if Bool(s.Properties.SanitizeMutated.Cfi) {
 			entries.SubName += ".cfi"
 		}
-		if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+		if Bool(s.Properties.SanitizeMutated.Hwaddress) {
 			entries.SubName += ".hwasan"
 		}
-		if Bool(sanitize.Properties.Sanitize.Scs) {
+		if Bool(s.Properties.SanitizeMutated.Scs) {
 			entries.SubName += ".scs"
 		}
 	}
 }
 
-func (sanitize *sanitize) inSanitizerDir() bool {
-	return sanitize.Properties.InSanitizerDir
+func (s *sanitize) inSanitizerDir() bool {
+	return s.Properties.InSanitizerDir
 }
 
 // getSanitizerBoolPtr returns the SanitizerTypes associated bool pointer from SanitizeProperties.
-func (sanitize *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool {
+func (s *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool {
 	switch t {
 	case Asan:
-		return sanitize.Properties.Sanitize.Address
+		return s.Properties.SanitizeMutated.Address
 	case Hwasan:
-		return sanitize.Properties.Sanitize.Hwaddress
+		return s.Properties.SanitizeMutated.Hwaddress
 	case tsan:
-		return sanitize.Properties.Sanitize.Thread
+		return s.Properties.SanitizeMutated.Thread
 	case intOverflow:
-		return sanitize.Properties.Sanitize.Integer_overflow
+		return s.Properties.SanitizeMutated.Integer_overflow
 	case cfi:
-		return sanitize.Properties.Sanitize.Cfi
+		return s.Properties.SanitizeMutated.Cfi
 	case scs:
-		return sanitize.Properties.Sanitize.Scs
+		return s.Properties.SanitizeMutated.Scs
 	case Memtag_heap:
-		return sanitize.Properties.Sanitize.Memtag_heap
+		return s.Properties.SanitizeMutated.Memtag_heap
 	case Memtag_stack:
-		return sanitize.Properties.Sanitize.Memtag_stack
+		return s.Properties.SanitizeMutated.Memtag_stack
 	case Fuzzer:
-		return sanitize.Properties.Sanitize.Fuzzer
+		return s.Properties.SanitizeMutated.Fuzzer
 	default:
 		panic(fmt.Errorf("unknown SanitizerType %d", t))
 	}
@@ -894,28 +1000,28 @@
 	}
 	switch t {
 	case Asan:
-		sanitize.Properties.Sanitize.Address = bPtr
+		sanitize.Properties.SanitizeMutated.Address = bPtr
 		// For ASAN variant, we need to disable Memtag_stack
-		sanitize.Properties.Sanitize.Memtag_stack = nil
+		sanitize.Properties.SanitizeMutated.Memtag_stack = nil
 	case Hwasan:
-		sanitize.Properties.Sanitize.Hwaddress = bPtr
+		sanitize.Properties.SanitizeMutated.Hwaddress = bPtr
 		// For HWAsan variant, we need to disable Memtag_stack
-		sanitize.Properties.Sanitize.Memtag_stack = nil
+		sanitize.Properties.SanitizeMutated.Memtag_stack = nil
 	case tsan:
-		sanitize.Properties.Sanitize.Thread = bPtr
+		sanitize.Properties.SanitizeMutated.Thread = bPtr
 	case intOverflow:
-		sanitize.Properties.Sanitize.Integer_overflow = bPtr
+		sanitize.Properties.SanitizeMutated.Integer_overflow = bPtr
 	case cfi:
-		sanitize.Properties.Sanitize.Cfi = bPtr
+		sanitize.Properties.SanitizeMutated.Cfi = bPtr
 	case scs:
-		sanitize.Properties.Sanitize.Scs = bPtr
+		sanitize.Properties.SanitizeMutated.Scs = bPtr
 	case Memtag_heap:
-		sanitize.Properties.Sanitize.Memtag_heap = bPtr
+		sanitize.Properties.SanitizeMutated.Memtag_heap = bPtr
 	case Memtag_stack:
-		sanitize.Properties.Sanitize.Memtag_stack = bPtr
+		sanitize.Properties.SanitizeMutated.Memtag_stack = bPtr
 		// We do not need to disable ASAN or HWASan here, as there is no Memtag_stack variant.
 	case Fuzzer:
-		sanitize.Properties.Sanitize.Fuzzer = bPtr
+		sanitize.Properties.SanitizeMutated.Fuzzer = bPtr
 	default:
 		panic(fmt.Errorf("unknown SanitizerType %d", t))
 	}
@@ -1127,7 +1233,8 @@
 				return s.sanitizer.variationName()
 			}
 
-			if s.sanitizer == cfi || s.sanitizer == Hwasan || s.sanitizer == scs || s.sanitizer == Asan {
+			// Some sanitizers do not propagate to shared dependencies
+			if !s.sanitizer.shouldPropagateToSharedLibraryDeps() {
 				return ""
 			}
 		}
@@ -1227,7 +1334,7 @@
 }
 
 func (c *Module) SanitizeNever() bool {
-	return Bool(c.sanitize.Properties.Sanitize.Never)
+	return Bool(c.sanitize.Properties.SanitizeMutated.Never)
 }
 
 func (c *Module) IsSanitizerExplicitlyDisabled(t SanitizerType) bool {
@@ -1295,10 +1402,12 @@
 		var sanitizers []string
 		var diagSanitizers []string
 
-		if Bool(c.sanitize.Properties.Sanitize.All_undefined) {
+		sanProps := &c.sanitize.Properties.SanitizeMutated
+
+		if Bool(sanProps.All_undefined) {
 			sanitizers = append(sanitizers, "undefined")
 		} else {
-			if Bool(c.sanitize.Properties.Sanitize.Undefined) {
+			if Bool(sanProps.Undefined) {
 				sanitizers = append(sanitizers,
 					"bool",
 					"integer-divide-by-zero",
@@ -1323,66 +1432,66 @@
 					// "object-size",
 				)
 			}
-			sanitizers = append(sanitizers, c.sanitize.Properties.Sanitize.Misc_undefined...)
+			sanitizers = append(sanitizers, sanProps.Misc_undefined...)
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Diag.Undefined) {
+		if Bool(sanProps.Diag.Undefined) {
 			diagSanitizers = append(diagSanitizers, "undefined")
 		}
 
-		diagSanitizers = append(diagSanitizers, c.sanitize.Properties.Sanitize.Diag.Misc_undefined...)
+		diagSanitizers = append(diagSanitizers, sanProps.Diag.Misc_undefined...)
 
-		if Bool(c.sanitize.Properties.Sanitize.Address) {
+		if Bool(sanProps.Address) {
 			sanitizers = append(sanitizers, "address")
 			diagSanitizers = append(diagSanitizers, "address")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Hwaddress) {
+		if Bool(sanProps.Hwaddress) {
 			sanitizers = append(sanitizers, "hwaddress")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Thread) {
+		if Bool(sanProps.Thread) {
 			sanitizers = append(sanitizers, "thread")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Safestack) {
+		if Bool(sanProps.Safestack) {
 			sanitizers = append(sanitizers, "safe-stack")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Cfi) {
+		if Bool(sanProps.Cfi) {
 			sanitizers = append(sanitizers, "cfi")
 
-			if Bool(c.sanitize.Properties.Sanitize.Diag.Cfi) {
+			if Bool(sanProps.Diag.Cfi) {
 				diagSanitizers = append(diagSanitizers, "cfi")
 			}
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Integer_overflow) {
+		if Bool(sanProps.Integer_overflow) {
 			sanitizers = append(sanitizers, "unsigned-integer-overflow")
 			sanitizers = append(sanitizers, "signed-integer-overflow")
-			if Bool(c.sanitize.Properties.Sanitize.Diag.Integer_overflow) {
+			if Bool(sanProps.Diag.Integer_overflow) {
 				diagSanitizers = append(diagSanitizers, "unsigned-integer-overflow")
 				diagSanitizers = append(diagSanitizers, "signed-integer-overflow")
 			}
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Scudo) {
+		if Bool(sanProps.Scudo) {
 			sanitizers = append(sanitizers, "scudo")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Scs) {
+		if Bool(sanProps.Scs) {
 			sanitizers = append(sanitizers, "shadow-call-stack")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.Binary() {
+		if Bool(sanProps.Memtag_heap) && c.Binary() {
 			sanitizers = append(sanitizers, "memtag-heap")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Memtag_stack) {
+		if Bool(sanProps.Memtag_stack) {
 			sanitizers = append(sanitizers, "memtag-stack")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Fuzzer) {
+		if Bool(sanProps.Fuzzer) {
 			sanitizers = append(sanitizers, "fuzzer-no-link")
 		}
 
@@ -1401,27 +1510,27 @@
 		alwaysStaticRuntime := false
 		var extraStaticDeps []string
 		toolchain := c.toolchain(mctx)
-		if Bool(c.sanitize.Properties.Sanitize.Address) {
+		if Bool(sanProps.Address) {
 			runtimeLibrary = config.AddressSanitizerRuntimeLibrary(toolchain)
-		} else if Bool(c.sanitize.Properties.Sanitize.Hwaddress) {
+		} else if Bool(sanProps.Hwaddress) {
 			if c.staticBinary() {
 				runtimeLibrary = config.HWAddressSanitizerStaticLibrary(toolchain)
 				extraStaticDeps = []string{"libdl"}
 			} else {
 				runtimeLibrary = config.HWAddressSanitizerRuntimeLibrary(toolchain)
 			}
-		} else if Bool(c.sanitize.Properties.Sanitize.Thread) {
+		} else if Bool(sanProps.Thread) {
 			runtimeLibrary = config.ThreadSanitizerRuntimeLibrary(toolchain)
-		} else if Bool(c.sanitize.Properties.Sanitize.Scudo) {
+		} else if Bool(sanProps.Scudo) {
 			if len(diagSanitizers) == 0 && !c.sanitize.Properties.UbsanRuntimeDep {
 				runtimeLibrary = config.ScudoMinimalRuntimeLibrary(toolchain)
 			} else {
 				runtimeLibrary = config.ScudoRuntimeLibrary(toolchain)
 			}
 		} else if len(diagSanitizers) > 0 || c.sanitize.Properties.UbsanRuntimeDep ||
-			Bool(c.sanitize.Properties.Sanitize.Fuzzer) ||
-			Bool(c.sanitize.Properties.Sanitize.Undefined) ||
-			Bool(c.sanitize.Properties.Sanitize.All_undefined) {
+			Bool(sanProps.Fuzzer) ||
+			Bool(sanProps.Undefined) ||
+			Bool(sanProps.All_undefined) {
 			runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)
 			if c.staticBinary() || toolchain.Musl() {
 				// Use a static runtime for static binaries.
@@ -1632,21 +1741,27 @@
 }
 
 func enableMinimalRuntime(sanitize *sanitize) bool {
-	if !Bool(sanitize.Properties.Sanitize.Address) &&
-		!Bool(sanitize.Properties.Sanitize.Hwaddress) &&
-		!Bool(sanitize.Properties.Sanitize.Fuzzer) &&
-		(Bool(sanitize.Properties.Sanitize.Integer_overflow) ||
-			len(sanitize.Properties.Sanitize.Misc_undefined) > 0 ||
-			Bool(sanitize.Properties.Sanitize.Undefined) ||
-			Bool(sanitize.Properties.Sanitize.All_undefined)) &&
-		!(Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) ||
-			Bool(sanitize.Properties.Sanitize.Diag.Cfi) ||
-			Bool(sanitize.Properties.Sanitize.Diag.Undefined) ||
-			len(sanitize.Properties.Sanitize.Diag.Misc_undefined) > 0) {
-
-		return true
+	if sanitize.isSanitizerEnabled(Asan) {
+		return false
+	} else if sanitize.isSanitizerEnabled(Hwasan) {
+		return false
+	} else if sanitize.isSanitizerEnabled(Fuzzer) {
+		return false
 	}
-	return false
+
+	if enableUbsanRuntime(sanitize) {
+		return false
+	}
+
+	sanitizeProps := &sanitize.Properties.SanitizeMutated
+	if Bool(sanitizeProps.Diag.Cfi) {
+		return false
+	}
+
+	return Bool(sanitizeProps.Integer_overflow) ||
+		len(sanitizeProps.Misc_undefined) > 0 ||
+		Bool(sanitizeProps.Undefined) ||
+		Bool(sanitizeProps.All_undefined)
 }
 
 func (m *Module) UbsanRuntimeNeeded() bool {
@@ -1658,9 +1773,10 @@
 }
 
 func enableUbsanRuntime(sanitize *sanitize) bool {
-	return Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) ||
-		Bool(sanitize.Properties.Sanitize.Diag.Undefined) ||
-		len(sanitize.Properties.Sanitize.Diag.Misc_undefined) > 0
+	sanitizeProps := &sanitize.Properties.SanitizeMutated
+	return Bool(sanitizeProps.Diag.Integer_overflow) ||
+		Bool(sanitizeProps.Diag.Undefined) ||
+		len(sanitizeProps.Diag.Misc_undefined) > 0
 }
 
 func cfiMakeVarsProvider(ctx android.MakeVarsContext) {
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index 2393f3e..580adfa 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -21,6 +21,8 @@
 	"testing"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint"
 )
 
 var prepareForAsanTest = android.FixtureAddFile("asan/Android.bp", []byte(`
@@ -29,6 +31,60 @@
 	}
 `))
 
+var prepareForTsanTest = android.FixtureAddFile("tsan/Android.bp", []byte(`
+	cc_library_shared {
+		name: "libclang_rt.tsan",
+	}
+`))
+
+type providerInterface interface {
+	ModuleProvider(blueprint.Module, blueprint.ProviderKey) interface{}
+}
+
+// expectSharedLinkDep verifies that the from module links against the to module as a
+// shared library.
+func expectSharedLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
+	t.Helper()
+	fromLink := from.Description("link")
+	toInfo := ctx.ModuleProvider(to.Module(), SharedLibraryInfoProvider).(SharedLibraryInfo)
+
+	if g, w := fromLink.OrderOnly.Strings(), toInfo.SharedLibrary.RelativeToTop().String(); !android.InList(w, g) {
+		t.Errorf("%s should link against %s, expected %q, got %q",
+			from.Module(), to.Module(), w, g)
+	}
+}
+
+// expectStaticLinkDep verifies that the from module links against the to module as a
+// static library.
+func expectStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
+	t.Helper()
+	fromLink := from.Description("link")
+	toInfo := ctx.ModuleProvider(to.Module(), StaticLibraryInfoProvider).(StaticLibraryInfo)
+
+	if g, w := fromLink.Implicits.Strings(), toInfo.StaticLibrary.RelativeToTop().String(); !android.InList(w, g) {
+		t.Errorf("%s should link against %s, expected %q, got %q",
+			from.Module(), to.Module(), w, g)
+	}
+
+}
+
+// expectInstallDep verifies that the install rule of the from module depends on the
+// install rule of the to module.
+func expectInstallDep(t *testing.T, from, to android.TestingModule) {
+	t.Helper()
+	fromInstalled := from.Description("install")
+	toInstalled := to.Description("install")
+
+	// combine implicits and order-only dependencies, host uses implicit but device uses
+	// order-only.
+	got := append(fromInstalled.Implicits.Strings(), fromInstalled.OrderOnly.Strings()...)
+	want := toInstalled.Output.String()
+	if !android.InList(want, got) {
+		t.Errorf("%s installation should depend on %s, expected %q, got %q",
+			from.Module(), to.Module(), want, got)
+	}
+}
+
 func TestAsan(t *testing.T) {
 	bp := `
 		cc_binary {
@@ -111,6 +167,7 @@
 	).RunTestWithBp(t, bp)
 
 	check := func(t *testing.T, result *android.TestResult, variant string) {
+		ctx := result.TestContext
 		asanVariant := variant + "_asan"
 		sharedVariant := variant + "_shared"
 		sharedAsanVariant := sharedVariant + "_asan"
@@ -140,85 +197,355 @@
 		libStaticAsan := result.ModuleForTests("libstatic_asan", staticAsanVariant)
 		libStaticAsanNoAsanVariant := result.ModuleForTests("libstatic_asan", staticVariant)
 
-		// expectSharedLinkDep verifies that the from module links against the to module as a
-		// shared library.
-		expectSharedLinkDep := func(from, to android.TestingModule) {
-			t.Helper()
-			fromLink := from.Description("link")
-			toLink := to.Description("strip")
+		expectSharedLinkDep(t, ctx, binWithAsan, libShared)
+		expectSharedLinkDep(t, ctx, binWithAsan, libAsan)
+		expectSharedLinkDep(t, ctx, libShared, libTransitive)
+		expectSharedLinkDep(t, ctx, libAsan, libTransitive)
 
-			if g, w := fromLink.OrderOnly.Strings(), toLink.Output.String(); !android.InList(w, g) {
-				t.Errorf("%s should link against %s, expected %q, got %q",
-					from.Module(), to.Module(), w, g)
-			}
-		}
+		expectStaticLinkDep(t, ctx, binWithAsan, libStaticAsanVariant)
+		expectStaticLinkDep(t, ctx, binWithAsan, libNoAsan)
+		expectStaticLinkDep(t, ctx, binWithAsan, libStaticAsan)
 
-		// expectStaticLinkDep verifies that the from module links against the to module as a
-		// static library.
-		expectStaticLinkDep := func(from, to android.TestingModule) {
-			t.Helper()
-			fromLink := from.Description("link")
-			toLink := to.Description("static link")
+		expectInstallDep(t, binWithAsan, libShared)
+		expectInstallDep(t, binWithAsan, libAsan)
+		expectInstallDep(t, binWithAsan, libTransitive)
+		expectInstallDep(t, libShared, libTransitive)
+		expectInstallDep(t, libAsan, libTransitive)
 
-			if g, w := fromLink.Implicits.Strings(), toLink.Output.String(); !android.InList(w, g) {
-				t.Errorf("%s should link against %s, expected %q, got %q",
-					from.Module(), to.Module(), w, g)
-			}
+		expectSharedLinkDep(t, ctx, binNoAsan, libShared)
+		expectSharedLinkDep(t, ctx, binNoAsan, libAsan)
+		expectSharedLinkDep(t, ctx, libShared, libTransitive)
+		expectSharedLinkDep(t, ctx, libAsan, libTransitive)
 
-		}
+		expectStaticLinkDep(t, ctx, binNoAsan, libStaticNoAsanVariant)
+		expectStaticLinkDep(t, ctx, binNoAsan, libNoAsan)
+		expectStaticLinkDep(t, ctx, binNoAsan, libStaticAsanNoAsanVariant)
 
-		// expectInstallDep verifies that the install rule of the from module depends on the
-		// install rule of the to module.
-		expectInstallDep := func(from, to android.TestingModule) {
-			t.Helper()
-			fromInstalled := from.Description("install")
-			toInstalled := to.Description("install")
-
-			// combine implicits and order-only dependencies, host uses implicit but device uses
-			// order-only.
-			got := append(fromInstalled.Implicits.Strings(), fromInstalled.OrderOnly.Strings()...)
-			want := toInstalled.Output.String()
-			if !android.InList(want, got) {
-				t.Errorf("%s installation should depend on %s, expected %q, got %q",
-					from.Module(), to.Module(), want, got)
-			}
-		}
-
-		expectSharedLinkDep(binWithAsan, libShared)
-		expectSharedLinkDep(binWithAsan, libAsan)
-		expectSharedLinkDep(libShared, libTransitive)
-		expectSharedLinkDep(libAsan, libTransitive)
-
-		expectStaticLinkDep(binWithAsan, libStaticAsanVariant)
-		expectStaticLinkDep(binWithAsan, libNoAsan)
-		expectStaticLinkDep(binWithAsan, libStaticAsan)
-
-		expectInstallDep(binWithAsan, libShared)
-		expectInstallDep(binWithAsan, libAsan)
-		expectInstallDep(binWithAsan, libTransitive)
-		expectInstallDep(libShared, libTransitive)
-		expectInstallDep(libAsan, libTransitive)
-
-		expectSharedLinkDep(binNoAsan, libShared)
-		expectSharedLinkDep(binNoAsan, libAsan)
-		expectSharedLinkDep(libShared, libTransitive)
-		expectSharedLinkDep(libAsan, libTransitive)
-
-		expectStaticLinkDep(binNoAsan, libStaticNoAsanVariant)
-		expectStaticLinkDep(binNoAsan, libNoAsan)
-		expectStaticLinkDep(binNoAsan, libStaticAsanNoAsanVariant)
-
-		expectInstallDep(binNoAsan, libShared)
-		expectInstallDep(binNoAsan, libAsan)
-		expectInstallDep(binNoAsan, libTransitive)
-		expectInstallDep(libShared, libTransitive)
-		expectInstallDep(libAsan, libTransitive)
+		expectInstallDep(t, binNoAsan, libShared)
+		expectInstallDep(t, binNoAsan, libAsan)
+		expectInstallDep(t, binNoAsan, libTransitive)
+		expectInstallDep(t, libShared, libTransitive)
+		expectInstallDep(t, libAsan, libTransitive)
 	}
 
 	t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
 	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
 }
 
+func TestTsan(t *testing.T) {
+	bp := `
+	cc_binary {
+		name: "bin_with_tsan",
+		host_supported: true,
+		shared_libs: [
+			"libshared",
+			"libtsan",
+		],
+		sanitize: {
+			thread: true,
+		}
+	}
+
+	cc_binary {
+		name: "bin_no_tsan",
+		host_supported: true,
+		shared_libs: [
+			"libshared",
+			"libtsan",
+		],
+	}
+
+	cc_library_shared {
+		name: "libshared",
+		host_supported: true,
+		shared_libs: ["libtransitive"],
+	}
+
+	cc_library_shared {
+		name: "libtsan",
+		host_supported: true,
+		shared_libs: ["libtransitive"],
+		sanitize: {
+			thread: true,
+		}
+	}
+
+	cc_library_shared {
+		name: "libtransitive",
+		host_supported: true,
+	}
+`
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+		prepareForTsanTest,
+	).RunTestWithBp(t, bp)
+
+	check := func(t *testing.T, result *android.TestResult, variant string) {
+		ctx := result.TestContext
+		tsanVariant := variant + "_tsan"
+		sharedVariant := variant + "_shared"
+		sharedTsanVariant := sharedVariant + "_tsan"
+
+		// The binaries, one with tsan and one without
+		binWithTsan := result.ModuleForTests("bin_with_tsan", tsanVariant)
+		binNoTsan := result.ModuleForTests("bin_no_tsan", variant)
+
+		// Shared libraries that don't request tsan
+		libShared := result.ModuleForTests("libshared", sharedVariant)
+		libTransitive := result.ModuleForTests("libtransitive", sharedVariant)
+
+		// Shared library that requests tsan
+		libTsan := result.ModuleForTests("libtsan", sharedTsanVariant)
+
+		expectSharedLinkDep(t, ctx, binWithTsan, libShared)
+		expectSharedLinkDep(t, ctx, binWithTsan, libTsan)
+		expectSharedLinkDep(t, ctx, libShared, libTransitive)
+		expectSharedLinkDep(t, ctx, libTsan, libTransitive)
+
+		expectSharedLinkDep(t, ctx, binNoTsan, libShared)
+		expectSharedLinkDep(t, ctx, binNoTsan, libTsan)
+		expectSharedLinkDep(t, ctx, libShared, libTransitive)
+		expectSharedLinkDep(t, ctx, libTsan, libTransitive)
+	}
+
+	t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
+	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
+}
+
+func TestMiscUndefined(t *testing.T) {
+	bp := `
+	cc_binary {
+		name: "bin_with_ubsan",
+		srcs: ["src.cc"],
+		host_supported: true,
+		static_libs: [
+			"libstatic",
+			"libubsan",
+		],
+		sanitize: {
+			misc_undefined: ["integer"],
+		}
+	}
+
+	cc_binary {
+		name: "bin_no_ubsan",
+		host_supported: true,
+		srcs: ["src.cc"],
+		static_libs: [
+			"libstatic",
+			"libubsan",
+		],
+	}
+
+	cc_library_static {
+		name: "libstatic",
+		host_supported: true,
+		srcs: ["src.cc"],
+		static_libs: ["libtransitive"],
+	}
+
+	cc_library_static {
+		name: "libubsan",
+		host_supported: true,
+		srcs: ["src.cc"],
+		whole_static_libs: ["libtransitive"],
+		sanitize: {
+			misc_undefined: ["integer"],
+		}
+	}
+
+	cc_library_static {
+		name: "libtransitive",
+		host_supported: true,
+		srcs: ["src.cc"],
+	}
+`
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+	).RunTestWithBp(t, bp)
+
+	check := func(t *testing.T, result *android.TestResult, variant string) {
+		ctx := result.TestContext
+		staticVariant := variant + "_static"
+
+		// The binaries, one with ubsan and one without
+		binWithUbsan := result.ModuleForTests("bin_with_ubsan", variant)
+		binNoUbsan := result.ModuleForTests("bin_no_ubsan", variant)
+
+		// Static libraries that don't request ubsan
+		libStatic := result.ModuleForTests("libstatic", staticVariant)
+		libTransitive := result.ModuleForTests("libtransitive", staticVariant)
+
+		libUbsan := result.ModuleForTests("libubsan", staticVariant)
+
+		libUbsanMinimal := result.ModuleForTests("libclang_rt.ubsan_minimal", staticVariant)
+
+		expectStaticLinkDep(t, ctx, binWithUbsan, libStatic)
+		expectStaticLinkDep(t, ctx, binWithUbsan, libUbsan)
+		expectStaticLinkDep(t, ctx, binWithUbsan, libUbsanMinimal)
+
+		miscUndefinedSanFlag := "-fsanitize=integer"
+		binWithUbsanCflags := binWithUbsan.Rule("cc").Args["cFlags"]
+		if !strings.Contains(binWithUbsanCflags, miscUndefinedSanFlag) {
+			t.Errorf("'bin_with_ubsan' Expected %q to be in flags %q, was not", miscUndefinedSanFlag, binWithUbsanCflags)
+		}
+		libStaticCflags := libStatic.Rule("cc").Args["cFlags"]
+		if strings.Contains(libStaticCflags, miscUndefinedSanFlag) {
+			t.Errorf("'libstatic' Expected %q to NOT be in flags %q, was", miscUndefinedSanFlag, binWithUbsanCflags)
+		}
+		libUbsanCflags := libUbsan.Rule("cc").Args["cFlags"]
+		if !strings.Contains(libUbsanCflags, miscUndefinedSanFlag) {
+			t.Errorf("'libubsan' Expected %q to be in flags %q, was not", miscUndefinedSanFlag, binWithUbsanCflags)
+		}
+		libTransitiveCflags := libTransitive.Rule("cc").Args["cFlags"]
+		if strings.Contains(libTransitiveCflags, miscUndefinedSanFlag) {
+			t.Errorf("'libtransitive': Expected %q to NOT be in flags %q, was", miscUndefinedSanFlag, binWithUbsanCflags)
+		}
+
+		expectStaticLinkDep(t, ctx, binNoUbsan, libStatic)
+		expectStaticLinkDep(t, ctx, binNoUbsan, libUbsan)
+	}
+
+	t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
+	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
+}
+
+func TestFuzz(t *testing.T) {
+	bp := `
+		cc_binary {
+			name: "bin_with_fuzzer",
+			host_supported: true,
+			shared_libs: [
+				"libshared",
+				"libfuzzer",
+			],
+			static_libs: [
+				"libstatic",
+				"libnofuzzer",
+				"libstatic_fuzzer",
+			],
+			sanitize: {
+				fuzzer: true,
+			}
+		}
+
+		cc_binary {
+			name: "bin_no_fuzzer",
+			host_supported: true,
+			shared_libs: [
+				"libshared",
+				"libfuzzer",
+			],
+			static_libs: [
+				"libstatic",
+				"libnofuzzer",
+				"libstatic_fuzzer",
+			],
+		}
+
+		cc_library_shared {
+			name: "libshared",
+			host_supported: true,
+			shared_libs: ["libtransitive"],
+		}
+
+		cc_library_shared {
+			name: "libfuzzer",
+			host_supported: true,
+			shared_libs: ["libtransitive"],
+			sanitize: {
+				fuzzer: true,
+			}
+		}
+
+		cc_library_shared {
+			name: "libtransitive",
+			host_supported: true,
+		}
+
+		cc_library_static {
+			name: "libstatic",
+			host_supported: true,
+		}
+
+		cc_library_static {
+			name: "libnofuzzer",
+			host_supported: true,
+			sanitize: {
+				fuzzer: false,
+			}
+		}
+
+		cc_library_static {
+			name: "libstatic_fuzzer",
+			host_supported: true,
+		}
+
+	`
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+	).RunTestWithBp(t, bp)
+
+	check := func(t *testing.T, result *android.TestResult, variant string) {
+		ctx := result.TestContext
+		fuzzerVariant := variant + "_fuzzer"
+		sharedVariant := variant + "_shared"
+		sharedFuzzerVariant := sharedVariant + "_fuzzer"
+		staticVariant := variant + "_static"
+		staticFuzzerVariant := staticVariant + "_fuzzer"
+
+		// The binaries, one with fuzzer and one without
+		binWithFuzzer := result.ModuleForTests("bin_with_fuzzer", fuzzerVariant)
+		binNoFuzzer := result.ModuleForTests("bin_no_fuzzer", variant)
+
+		// Shared libraries that don't request fuzzer
+		libShared := result.ModuleForTests("libshared", sharedVariant)
+		libTransitive := result.ModuleForTests("libtransitive", sharedVariant)
+
+		// Shared libraries that don't request fuzzer
+		libSharedFuzzer := result.ModuleForTests("libshared", sharedFuzzerVariant)
+		libTransitiveFuzzer := result.ModuleForTests("libtransitive", sharedFuzzerVariant)
+
+		// Shared library that requests fuzzer
+		libFuzzer := result.ModuleForTests("libfuzzer", sharedFuzzerVariant)
+
+		// Static library that uses an fuzzer variant for bin_with_fuzzer and a non-fuzzer variant
+		// for bin_no_fuzzer.
+		libStaticFuzzerVariant := result.ModuleForTests("libstatic", staticFuzzerVariant)
+		libStaticNoFuzzerVariant := result.ModuleForTests("libstatic", staticVariant)
+
+		// Static library that never uses fuzzer.
+		libNoFuzzer := result.ModuleForTests("libnofuzzer", staticVariant)
+
+		// Static library that specifies fuzzer
+		libStaticFuzzer := result.ModuleForTests("libstatic_fuzzer", staticFuzzerVariant)
+		libStaticFuzzerNoFuzzerVariant := result.ModuleForTests("libstatic_fuzzer", staticVariant)
+
+		expectSharedLinkDep(t, ctx, binWithFuzzer, libSharedFuzzer)
+		expectSharedLinkDep(t, ctx, binWithFuzzer, libFuzzer)
+		expectSharedLinkDep(t, ctx, libSharedFuzzer, libTransitiveFuzzer)
+		expectSharedLinkDep(t, ctx, libFuzzer, libTransitiveFuzzer)
+
+		expectStaticLinkDep(t, ctx, binWithFuzzer, libStaticFuzzerVariant)
+		expectStaticLinkDep(t, ctx, binWithFuzzer, libNoFuzzer)
+		expectStaticLinkDep(t, ctx, binWithFuzzer, libStaticFuzzer)
+
+		expectSharedLinkDep(t, ctx, binNoFuzzer, libShared)
+		expectSharedLinkDep(t, ctx, binNoFuzzer, libFuzzer)
+		expectSharedLinkDep(t, ctx, libShared, libTransitive)
+		expectSharedLinkDep(t, ctx, libFuzzer, libTransitiveFuzzer)
+
+		expectStaticLinkDep(t, ctx, binNoFuzzer, libStaticNoFuzzerVariant)
+		expectStaticLinkDep(t, ctx, binNoFuzzer, libNoFuzzer)
+		expectStaticLinkDep(t, ctx, binNoFuzzer, libStaticFuzzerNoFuzzerVariant)
+	}
+
+	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
+}
+
 func TestUbsan(t *testing.T) {
 	if runtime.GOOS != "linux" {
 		t.Skip("requires linux")
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 3bc311b..029bbb4 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -629,40 +629,41 @@
 // symlink tree creation binary. Then the latter would not need to depend on
 // the very heavy-weight machinery of soong_build .
 func runSymlinkForestCreation(configuration android.Config, ctx *android.Context, extraNinjaDeps []string, metricsDir string) string {
-	var ninjaDeps []string
-	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
-
-	generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
-	workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
-
-	excludes := bazelArtifacts()
-
-	if outDir[0] != '/' {
-		excludes = append(excludes, outDir)
-	}
-
-	existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
-		os.Exit(1)
-	}
-
-	pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(configuration.Bp2buildPackageConfig, topDir, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
-	excludes = append(excludes, pathsToIgnoredBuildFiles...)
-	excludes = append(excludes, getTemporaryExcludes()...)
-
-	// PlantSymlinkForest() returns all the directories that were readdir()'ed.
-	// Such a directory SHOULD be added to `ninjaDeps` so that a child directory
-	// or file created/deleted under it would trigger an update of the symlink
-	// forest.
 	ctx.EventHandler.Do("symlink_forest", func() {
-		symlinkForestDeps := bp2build.PlantSymlinkForest(
-			configuration.IsEnvTrue("BP2BUILD_VERBOSE"), topDir, workspaceRoot, generatedRoot, excludes)
-		ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
-	})
+		var ninjaDeps []string
+		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
-	writeDepFile(symlinkForestMarker, ctx.EventHandler, ninjaDeps)
-	touch(shared.JoinPath(topDir, symlinkForestMarker))
+		generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
+		workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
+
+		excludes := bazelArtifacts()
+
+		if outDir[0] != '/' {
+			excludes = append(excludes, outDir)
+		}
+
+		existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
+			os.Exit(1)
+		}
+
+		pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(configuration.Bp2buildPackageConfig, topDir, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
+		excludes = append(excludes, pathsToIgnoredBuildFiles...)
+		excludes = append(excludes, getTemporaryExcludes()...)
+
+		// PlantSymlinkForest() returns all the directories that were readdir()'ed.
+		// Such a directory SHOULD be added to `ninjaDeps` so that a child directory
+		// or file created/deleted under it would trigger an update of the symlink forest.
+		ctx.EventHandler.Do("plant", func() {
+			symlinkForestDeps := bp2build.PlantSymlinkForest(
+				configuration.IsEnvTrue("BP2BUILD_VERBOSE"), topDir, workspaceRoot, generatedRoot, excludes)
+			ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
+		})
+
+		writeDepFile(symlinkForestMarker, ctx.EventHandler, ninjaDeps)
+		touch(shared.JoinPath(topDir, symlinkForestMarker))
+	})
 	codegenMetrics := bp2build.ReadCodegenMetrics(metricsDir)
 	if codegenMetrics == nil {
 		m := bp2build.CreateCodegenMetrics()