Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/soong/cc/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 68 kB image not shown  

Quelle  sanitize.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cc

import (
 "fmt"
 "sort"
 "strings"
 "sync"

 "github.com/google/blueprint"
 "github.com/google/blueprint/proptools"

 "android/soong/android"
 "android/soong/cc/config"
 "android/soong/etc"
)

//go:generate go run ../../blueprint/gobtools/codegen

var (
 // Any C flags added by sanitizer which libTooling tools may not
 // understand also need to be added to ClangLibToolingUnknownCflags in
 // cc/config/clang.go

 asanCflags = []string{
  "-fno-omit-frame-pointer",
 }

 // DO NOT ADD MLLVM FLAGS HERE! ADD THEM BELOW TO hwasanCommonFlags.
 hwasanCflags = []string{
  "-fno-omit-frame-pointer",
  "-Wno-frame-larger-than=",
  "-fsanitize-hwaddress-abi=platform",
 }

 // ThinLTO performs codegen during link time, thus these flags need to
 // passed to both CFLAGS and LDFLAGS.
 hwasanCommonflags = []string{
  // The following improves debug location information
  // availability at the cost of its accuracy. It increases
  // the likelihood of a stack variable's frame offset
  // to be recorded in the debug info, which is important
  // for the quality of hwasan reports. The downside is a
  // higher number of "optimized out" stack variables.
  // b/112437883.
  "-instcombine-lower-dbg-declare=0",
  "-dom-tree-reachability-max-bbs-to-explore=128",
 }

 sanitizeIgnorelistPrefix = "-fsanitize-ignorelist="

 cfiBlocklistPath     = "external/compiler-rt/lib/cfi"
 cfiBlocklistFilename = "cfi_blocklist.txt"
 cfiEnableFlag        = "-fsanitize=cfi"
 cfiCrossDsoFlag      = "-fsanitize-cfi-cross-dso"
 cfiCflags            = []string{"-flto", cfiCrossDsoFlag,
  sanitizeIgnorelistPrefix + cfiBlocklistPath + "/" + cfiBlocklistFilename}
 // -flto and -fvisibility are required by clang when -fsanitize=cfi is
 // used, but have no effect on assembly files
 cfiAsflags = []string{"-flto", "-fvisibility=default"}
 cfiLdflags = []string{"-flto", cfiCrossDsoFlag, cfiEnableFlag,
  "-Wl,-plugin-opt,O1"}
 cfiExportsMapPath      = "build/soong/cc/config"
 cfiExportsMapFilename  = "cfi_exports.map"
 cfiAssemblySupportFlag = "-fno-sanitize-cfi-canonical-jump-tables"

 intOverflowCflags = []string{"-fsanitize-ignorelist=build/soong/cc/config/integer_overflow_blocklist.txt"}

 minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
  "-fno-sanitize-recover=integer,undefined"}
 memtagStackCommonFlags = []string{"-Xclang -target-feature -Xclang +mte"}
 memtagStackLlvmFlags   = []string{"-dom-tree-reachability-max-bbs-to-explore=128"}

 hostOnlySanitizeFlags   = []string{"-fno-sanitize-recover=all"}
 deviceOnlySanitizeFlags = []string{"-fsanitize-trap=all"}

 noSanitizeLinkRuntimeFlag = "-fno-sanitize-link-runtime"
)

type SanitizerType int

const (
 Asan SanitizerType = iota + 1
 Hwasan
 tsan
 intOverflow
 scs
 Fuzzer
 Memtag_heap
 Memtag_stack
 Memtag_globals
 cfi // cfi is last to prevent it running before incompatible mutators
)

var Sanitizers = []SanitizerType{
 Asan,
 Hwasan,
 tsan,
 intOverflow,
 scs,
 Fuzzer,
 Memtag_heap,
 Memtag_stack,
 Memtag_globals,
 cfi, // cfi is last to prevent it running before incompatible mutators
}

// Name of the sanitizer variation for this sanitizer type
func (t SanitizerType) variationName() string {
 switch t {
 case Asan:
  return "asan"
 case Hwasan:
  return "hwasan"
 case tsan:
  return "tsan"
 case intOverflow:
  return "intOverflow"
 case cfi:
  return "cfi"
 case scs:
  return "scs"
 case Memtag_heap:
  return "memtag_heap"
 case Memtag_stack:
  return "memtag_stack"
 case Memtag_globals:
  return "memtag_globals"
 case Fuzzer:
  return "fuzzer"
 default:
  panic(fmt.Errorf("unknown SanitizerType %d", t))
 }
}

// This is the sanitizer names in SANITIZE_[TARGET|HOST]
func (t SanitizerType) name() string {
 switch t {
 case Asan:
  return "address"
 case Hwasan:
  return "hwaddress"
 case Memtag_heap:
  return "memtag_heap"
 case Memtag_stack:
  return "memtag_stack"
 case Memtag_globals:
  return "memtag_globals"
 case tsan:
  return "thread"
 case intOverflow:
  return "integer_overflow"
 case cfi:
  return "cfi"
 case scs:
  return "shadow-call-stack"
 case Fuzzer:
  return "fuzzer"
 default:
  panic(fmt.Errorf("unknown SanitizerType %d", t))
 }
}

func (t SanitizerType) registerMutators(ctx android.RegisterMutatorsContext) {
 switch t {
 case cfi, Hwasan, Asan, tsan, Fuzzer, scs, Memtag_stack:
  sanitizer := &sanitizerSplitMutator{t}
  ctx.Transition(t.variationName(), sanitizer)
 case Memtag_heap, Memtag_globals, intOverflow:
  // do nothing
 default:
  panic(fmt.Errorf("unknown SanitizerType %d", t))
 }
}

// 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:
  return true
 case Hwasan:
  return true
 case tsan:
  return true
 case intOverflow:
  return true
 case cfi:
  return true
 case scs:
  return true
 case Fuzzer:
  return true
 case Memtag_heap:
  return true
 case Memtag_stack:
  return true
 case Memtag_globals:
  return true
 default:
  return false
 }
}

// incompatibleWithCfi returns true if a sanitizer is incompatible with CFI.
func (t SanitizerType) incompatibleWithCfi() bool {
 return t == Asan || t == Fuzzer || t == Hwasan
}

// @auto-generate: gob
type SanitizeUserProps struct {
 // Prevent use of any sanitizers on this module
 Never *bool `android:"arch_variant"`

 // ASan (Address sanitizer), incompatible with static binaries.
 // Always runs in a diagnostic mode.
 // Use of address sanitizer disables cfi sanitizer.
 // Hwaddress sanitizer takes precedence over this sanitizer.
 Address *bool `android:"arch_variant"`
 // TSan (Thread sanitizer), incompatible with static binaries and 32 bit architectures.
 // Always runs in a diagnostic mode.
 // Use of thread sanitizer disables cfi and scudo sanitizers.
 // Hwaddress sanitizer takes precedence over this sanitizer.
 Thread *bool `android:"arch_variant"`
 // HWASan (Hardware Address sanitizer).
 // Use of hwasan sanitizer disables cfi, address, thread, and scudo sanitizers.
 Hwaddress *bool `android:"arch_variant"`

 // Undefined behavior sanitizer
 All_undefined *bool `android:"arch_variant"`
 // Subset of undefined behavior sanitizer
 Undefined *bool `android:"arch_variant"`
 // List of specific undefined behavior sanitizers to enable
 Misc_undefined []string `android:"arch_variant"`
 // Fuzzer, incompatible with static binaries.
 Fuzzer *bool `android:"arch_variant"`
 // safe-stack sanitizer, incompatible with 32-bit architectures.
 Safestack *bool `android:"arch_variant"`
 // cfi sanitizer, incompatible with asan, hwasan, fuzzer, or Darwin
 Cfi *bool `android:"arch_variant"`
 // signed/unsigned integer overflow sanitizer, incompatible with Darwin.
 Integer_overflow *bool `android:"arch_variant"`
 // scudo sanitizer, incompatible with asan, hwasan, tsan
 // This should not be used in Android 11+ : https://source.android.com/devices/tech/debug/scudo
 // deprecated
 Scudo *bool `android:"arch_variant"`
 // shadow-call-stack sanitizer, only available on arm64/riscv64.
 Scs *bool `android:"arch_variant"`
 // Memory-tagging, only available on arm64
 // if diag.memtag unset or false, enables async memory tagging
 Memtag_heap *bool `android:"arch_variant"`
 // Memory-tagging stack instrumentation, only available on arm64
 // Adds instrumentation to detect stack buffer overflows and use-after-scope using MTE.
 Memtag_stack *bool `android:"arch_variant"`
 // Memory-tagging globals instrumentation, only available on arm64
 // Adds instrumentation to detect global buffer overflows using MTE.
 Memtag_globals *bool `android:"arch_variant"`

 // A modifier for ASAN and HWASAN for write only instrumentation
 Writeonly *bool `android:"arch_variant"`

 // Sanitizers to run in the diagnostic mode (as opposed to the release mode).
 // Replaces abort() on error with a human-readable error message.
 // Address and Thread sanitizers always run in diagnostic mode.
 Diag struct {
  // Undefined behavior sanitizer, diagnostic mode
  Undefined *bool `android:"arch_variant"`
  // cfi sanitizer, diagnostic mode, incompatible with asan, hwasan, fuzzer, or Darwin
  Cfi *bool `android:"arch_variant"`
  // signed/unsigned integer overflow sanitizer, diagnostic mode, incompatible with Darwin.
  Integer_overflow *bool `android:"arch_variant"`
  // Memory-tagging, only available on arm64
  // requires sanitizer.memtag: true
  // if set, enables sync memory tagging
  Memtag_heap *bool `android:"arch_variant"`
  // List of specific undefined behavior sanitizers to enable in diagnostic mode
  Misc_undefined []string `android:"arch_variant"`
  // List of sanitizers to pass to -fno-sanitize-recover
  // results in only the first detected error for these sanitizers being reported and program then
  // exits with a non-zero exit code.
  No_recover []string `android:"arch_variant"`
 } `android:"arch_variant"`

 // Sanitizers to run with flag configuration specified
 Config struct {
  // Enables CFI support flags for assembly-heavy libraries
  Cfi_assembly_support *bool `android:"arch_variant"`
 } `android:"arch_variant"`

 // List of sanitizers to pass to -fsanitize-recover
 // allows execution to continue for these sanitizers to detect multiple errors rather than only
 // the first one
 Recover []string

 // value to pass to -fsanitize-ignorelist
 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 Memory-tagging globals instrumentation is enabled for this module
 Memtag_globals *bool `android:"arch_variant"`

 // 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"`
 SanitizeMutated sanitizeMutatedProperties `blueprint:"mutated"`

 // ForceDisable is set by the version mutator to disable sanitization of stubs variants
 ForceDisable bool `blueprint:"mutated"`

 // SanitizerEnabled is set by begin() if any of the sanitize boolean properties are set after
 // applying the logic that enables globally enabled sanitizers and disables any unsupported
 // sanitizers.
 // TODO(b/349906293): this has some unintuitive behavior.  It is set in begin() before the sanitize
 //  mutator is run if any of the individual sanitizes  properties are set, and then the individual
 //  sanitize properties are cleared in the non-sanitized variants, but this value is never cleared.
 //  That results in SanitizerEnabled being set in variants that have no sanitizers enabled, causing
 //  some of the sanitizer logic in flags() to be applied to the non-sanitized variant.
 SanitizerEnabled bool `blueprint:"mutated"`

 MinimalRuntimeDep bool     `blueprint:"mutated"`
 UbsanRuntimeDep   bool     `blueprint:"mutated"`
 InSanitizerDir    bool     `blueprint:"mutated"`
 Sanitizers        []string `blueprint:"mutated"`
 DiagSanitizers    []string `blueprint:"mutated"`
}

type sanitize struct {
 Properties SanitizeProperties
}

// Mark this tag with a check to see if apex dependency check should be skipped
func (t libraryDependencyTag) SkipApexAllowedDependenciesCheck() bool {
 return t.skipApexAllowedDependenciesCheck
}

var _ android.SkipApexAllowedDependenciesCheck = (*libraryDependencyTag)(nil)

func init() {
 pctx.StaticVariable("HostOnlySanitizeFlags", strings.Join(hostOnlySanitizeFlags, " "))

 android.RegisterMakeVarsProvider(pctx, cfiMakeVarsProvider)
 android.RegisterMakeVarsProvider(pctx, hwasanMakeVarsProvider)
 android.RegisterMakeVarsProvider(pctx, memtagStackMakeVarsProvider)

 RegisterSanitizerLibrariesTxtType(android.InitRegistrationContext)
}

func (sanitize *sanitize) props() []interface{} {
 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.Memtag_globals = userProps.Memtag_globals
 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.SanitizeMutated
 s.copyUserPropertiesToMutated(&sanitize.Properties.Sanitize)

 if sanitize.Properties.ForceDisable {
  return
 }

 // Don't apply sanitizers to NDK code.
 if ctx.useSdk() {
  s.Never = BoolPtr(true)
 }

 // Never always wins.
 if Bool(s.Never) {
  return
 }

 // cc_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {memtag_heap: false}).
 if ctx.testBinary() {
  if s.Memtag_heap == nil {
   s.Memtag_heap = proptools.BoolPtr(true)
  }
  if s.Diag.Memtag_heap == nil {
   s.Diag.Memtag_heap = proptools.BoolPtr(true)
  }
 }

 var globalSanitizers []string
 var globalSanitizersDiag []string

 if ctx.Host() {
  if !ctx.Windows() {
   globalSanitizers = ctx.Config().SanitizeHost()
  }
 } else {
  arches := ctx.Config().SanitizeDeviceArch()
  if len(arches) == 0 || inList(ctx.Arch().ArchType.Name, arches) {
   globalSanitizers = ctx.Config().SanitizeDevice()
   globalSanitizersDiag = ctx.Config().SanitizeDeviceDiag()
  }
 }

 if len(globalSanitizers) > 0 {
  var found bool
  if found, globalSanitizers = removeFromList("undefined", globalSanitizers); found && s.All_undefined == nil {
   s.All_undefined = proptools.BoolPtr(true)
  }

  if found, globalSanitizers = removeFromList("default-ub", globalSanitizers); found && s.Undefined == nil {
   s.Undefined = proptools.BoolPtr(true)
  }

  if found, globalSanitizers = removeFromList("address", globalSanitizers); found && s.Address == nil {
   s.Address = proptools.BoolPtr(true)
  }

  if found, globalSanitizers = removeFromList("thread", globalSanitizers); found && s.Thread == nil {
   s.Thread = proptools.BoolPtr(true)
  }

  if found, globalSanitizers = removeFromList("fuzzer", globalSanitizers); found && s.Fuzzer == nil {
   s.Fuzzer = proptools.BoolPtr(true)
  }

  if found, globalSanitizers = removeFromList("safe-stack", globalSanitizers); found && s.Safestack == nil {
   s.Safestack = proptools.BoolPtr(true)
  }

  if found, globalSanitizers = removeFromList("cfi", globalSanitizers); found && s.Cfi == nil {
   if !ctx.Config().CFIDisabledForPath(ctx.ModuleDir()) {
    s.Cfi = proptools.BoolPtr(true)
   }
  }

  // Global integer_overflow builds do not support static libraries.
  if found, globalSanitizers = removeFromList("integer_overflow", globalSanitizers); found && s.Integer_overflow == nil {
   if !ctx.Config().IntegerOverflowDisabledForPath(ctx.ModuleDir()) && !ctx.static() {
    s.Integer_overflow = proptools.BoolPtr(true)
   }
  }

  if found, globalSanitizers = removeFromList("scudo", globalSanitizers); found && s.Scudo == nil {
   s.Scudo = proptools.BoolPtr(true)
  }

  if found, globalSanitizers = removeFromList("hwaddress", globalSanitizers); found && s.Hwaddress == nil {
   if !ctx.Config().HWASanDisabledForPath(ctx.ModuleDir()) {
    s.Hwaddress = proptools.BoolPtr(true)
   }
  }

  if found, globalSanitizers = removeFromList("writeonly", globalSanitizers); found && s.Writeonly == nil {
   // Hwaddress and Address are set before, so we can check them here
   // If they aren't explicitly set in the blueprint/SANITIZE_(HOST|TARGET), they would be nil instead of false
   if s.Address == nil && s.Hwaddress == nil {
    ctx.ModuleErrorf("writeonly modifier cannot be used without 'address' or 'hwaddress'")
   }
   s.Writeonly = proptools.BoolPtr(true)
  }
  if found, globalSanitizers = removeFromList("memtag_heap", globalSanitizers); found && s.Memtag_heap == nil {
   if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) {
    s.Memtag_heap = proptools.BoolPtr(true)
   }
  }

  if found, globalSanitizers = removeFromList("memtag_stack", globalSanitizers); found && s.Memtag_stack == nil {
   s.Memtag_stack = proptools.BoolPtr(true)
  }

  if found, globalSanitizers = removeFromList("memtag_globals", globalSanitizers); found && s.Memtag_globals == nil {
   s.Memtag_globals = proptools.BoolPtr(true)
  }

  if len(globalSanitizers) > 0 {
   ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
  }

  // Global integer_overflow builds do not support static library diagnostics.
  if found, globalSanitizersDiag = removeFromList("integer_overflow", globalSanitizersDiag); found &&
   s.Diag.Integer_overflow == nil && Bool(s.Integer_overflow) && !ctx.static() {
   s.Diag.Integer_overflow = proptools.BoolPtr(true)
  }

  if found, globalSanitizersDiag = removeFromList("cfi", globalSanitizersDiag); found &&
   s.Diag.Cfi == nil && Bool(s.Cfi) {
   s.Diag.Cfi = proptools.BoolPtr(true)
  }

  if found, globalSanitizersDiag = removeFromList("memtag_heap", globalSanitizersDiag); found &&
   s.Diag.Memtag_heap == nil && Bool(s.Memtag_heap) {
   s.Diag.Memtag_heap = proptools.BoolPtr(true)
  }

  if len(globalSanitizersDiag) > 0 {
   ctx.ModuleErrorf("unknown global sanitizer diagnostics option %s", globalSanitizersDiag[0])
  }
 }

 // Enable Memtag for all components in the include paths (for Aarch64 only)
 if ctx.Arch().ArchType == android.Arm64 && ctx.toolchain().Bionic() {
  if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
   if s.Memtag_heap == nil {
    s.Memtag_heap = proptools.BoolPtr(true)
   }
   if s.Diag.Memtag_heap == nil {
    s.Diag.Memtag_heap = proptools.BoolPtr(true)
   }
  } else if ctx.Config().MemtagHeapAsyncEnabledForPath(ctx.ModuleDir()) {
   if s.Memtag_heap == nil {
    s.Memtag_heap = proptools.BoolPtr(true)
   }
  }
 }

 // Enable HWASan for all components in the include paths (for Aarch64 only)
 if s.Hwaddress == nil && ctx.Config().HWASanEnabledForPath(ctx.ModuleDir()) &&
  ctx.Arch().ArchType == android.Arm64 && ctx.toolchain().Bionic() {
  s.Hwaddress = proptools.BoolPtr(true)
 }

 // Enable CFI for non-host components in the include paths
 if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && !ctx.Host() {
  s.Cfi = proptools.BoolPtr(true)
  if inList("cfi", ctx.Config().SanitizeDeviceDiag()) {
   s.Diag.Cfi = proptools.BoolPtr(true)
  }
 }

 // Is CFI actually enabled?
 if !ctx.Config().EnableCFI() {
  s.Cfi = nil
  s.Diag.Cfi = nil
 }

 // HWASan requires AArch64 hardware feature (top-byte-ignore).
 if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() {
  s.Hwaddress = nil
 }

 // SCS is only implemented on AArch64/riscv64.
 if (ctx.Arch().ArchType != android.Arm64 && ctx.Arch().ArchType != android.Riscv64) || !ctx.toolchain().Bionic() {
  s.Scs = nil
 }

 // Memtag_heap is only implemented on AArch64.
 // Memtag ABI is Android specific for now, so disable for host.
 if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() || ctx.Host() {
  s.Memtag_heap = nil
  s.Memtag_stack = nil
  s.Memtag_globals = nil
 }

 // Also disable CFI if ASAN is enabled.
 if Bool(s.Address) || Bool(s.Hwaddress) {
  s.Cfi = nil
  s.Diag.Cfi = nil
  // HWASAN and ASAN win against MTE.
  s.Memtag_heap = nil
  s.Memtag_stack = nil
  s.Memtag_globals = nil
 }

 // Disable sanitizers that depend on the UBSan runtime for windows/darwin builds.
 if !ctx.Os().Linux() {
  s.Cfi = nil
  s.Diag.Cfi = nil
  s.Misc_undefined = nil
  s.Undefined = nil
  s.All_undefined = nil
  s.Integer_overflow = nil
 }

 // Disable CFI for musl
 if ctx.toolchain().Musl() {
  s.Cfi = nil
  s.Diag.Cfi = nil
 }

 // TODO(b/280478629): runtimes don't exist for musl arm64 or LFI yet.
 if (ctx.toolchain().Musl() && ctx.Arch().ArchType == android.Arm64) || ctx.Target().LFI {
  s.Address = nil
  s.Hwaddress = nil
  s.Thread = nil
  s.Scudo = nil
  s.Fuzzer = nil
  s.Cfi = nil
  s.Diag.Cfi = nil
  s.Misc_undefined = nil
  s.Undefined = nil
  s.All_undefined = nil
  s.Integer_overflow = nil
 }

 // LFI doesn't support MTE
 if ctx.Target().LFI {
  s.Memtag_globals = nil
  s.Memtag_heap = nil
  s.Memtag_stack = nil
 }

 if ctx.inRamdisk() || ctx.inVendorRamdisk() || ctx.inRecovery() {
  // HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
  // Keep libc instrumented so that ramdisk / vendor_ramdisk / recovery can run hwasan-instrumented code if necessary.
  if !strings.HasPrefix(ctx.ModuleDir(), "bionic/libc") {
   s.Hwaddress = nil
  }
  // Memtag stack in ramdisk makes pKVM unhappy.
  s.Memtag_stack = nil
 }

 if ctx.staticBinary() {
  s.Address = nil
  s.Fuzzer = nil
  s.Thread = nil
 }

 if Bool(s.All_undefined) {
  s.Undefined = nil
 }

 if !ctx.toolchain().Is64Bit() {
  // TSAN and SafeStack are not supported on 32-bit architectures
  s.Thread = nil
  s.Safestack = nil
  // TODO(ccross): error for compile_multilib = "32"?
 }

 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.Memtag_heap) || Bool(s.Memtag_stack) ||
  Bool(s.Memtag_globals)) {
  sanitize.Properties.SanitizerEnabled = true
 }

 // Disable Scudo if ASan or TSan is enabled, or if it's disabled globally.
 if Bool(s.Address) || Bool(s.Thread) || Bool(s.Hwaddress) || ctx.Config().DisableScudo() {
  s.Scudo = nil
 }

 if Bool(s.Hwaddress) {
  s.Address = nil
  s.Thread = nil
 }

 // TODO(b/131771163): CFI transiently depends on LTO, and thus Fuzzer is
 // mutually incompatible.
 if Bool(s.Fuzzer) {
  s.Cfi = nil
 }
}

func toDisableImplicitIntegerChange(flags []string) bool {
 // Returns true if any flag is fsanitize*integer, and there is
 // no explicit flag about sanitize=implicit-integer-sign-change.
 for _, f := range flags {
  if strings.Contains(f, "sanitize=implicit-integer-sign-change") {
   return false
  }
 }
 for _, f := range flags {
  if strings.HasPrefix(f, "-fsanitize") && strings.Contains(f, "integer") {
   return true
  }
 }
 return false
}

func toDisableUnsignedShiftBaseChange(flags []string) bool {
 // Returns true if any flag is fsanitize*integer, and there is
 // no explicit flag about sanitize=unsigned-shift-base.
 for _, f := range flags {
  if strings.Contains(f, "sanitize=unsigned-shift-base") {
   return false
  }
 }
 for _, f := range flags {
  if strings.HasPrefix(f, "-fsanitize") && strings.Contains(f, "integer") {
   return true
  }
 }
 return false
}

func toDisableFunctionAndKcfiSanitizer(ctx ModuleContext, flags []string) bool {
 // Function and kcfi sanitizers are not compatible with execute-only mode.
 // If either of them are enabled explicitly together with XOM, then throw an error.
 // If none of them is enabled explicitly they still need to be disabled if
 // execute-only is on, as they still can be enabled implicitly via undefined sanitizer.

 // XomEnabledForModule may return true even if a static dependency disables XOM in this module.
 // In those cases we should still error out since it makes more sense for the module definition
 // to be explicit with regard to XOM and this sanitizer mixing. Otherwise, changing static
 // dependencies may result in this error appearing and disappearing in a non-obvious manner.
 if XomEnabledForModule(ctx) {
  for _, f := range flags {
   if strings.HasPrefix(f, "-fsanitize") &&
    (strings.Contains(f, "function") || strings.Contains(f, "kcfi")) {
    ctx.ModuleErrorf("\nFunction and kcfi sanitizers are not supported with XOMenabled")
   }
  }
  return true
 }
 return false
}

func (s *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
 if s.Properties.ForceDisable {
  return flags
 }

 if !s.Properties.SanitizerEnabled && !s.Properties.UbsanRuntimeDep {
  return flags
 }
 sanProps := &s.Properties.SanitizeMutated

 if Bool(sanProps.Address) {
  if ctx.Arch().ArchType == android.Arm {
   // Frame pointer based unwinder in ASan requires ARM frame setup.
   // TODO: put in flags?
   flags.RequiredInstructionSet = "arm"
  }
  flags.Local.CFlags = append(flags.Local.CFlags, asanCflags...)

  if Bool(sanProps.Writeonly) {
   flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-asan-instrument-reads=0")
  }

  if ctx.Host() {
   if !ctx.Darwin() { // ld64.lld doesn't know about '--no-as-needed'
    // -nodefaultlibs (provided with libc++) prevents the driver from linking
    // libraries needed with -fsanitize=address. http://b/18650275 (WAI)
    flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-as-needed")
   }
  } else {
   flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-asan-globals=0")

   // Check if this module needs to use the bootstrap linker
   useBootstrap := ctx.bootstrap()
   if ctx.Config().GetBuildFlagBool("RELEASE_DEPRECATE_RUNTIME_APEX") {
    useBootstrap = false
   }

   if useBootstrap {
    flags.DynamicLinker = "/system/bin/bootstrap/linker_asan"
   } else {
    flags.DynamicLinker = "/system/bin/linker_asan"
   }
   if flags.Toolchain.Is64Bit() {
    flags.DynamicLinker += "64"
   }
  }
 }

 if Bool(sanProps.Hwaddress) {
  flags.Local.CFlags = append(flags.Local.CFlags, hwasanCflags...)

  for _, flag := range hwasanCommonflags {
   flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", flag)
  }
  for _, flag := range hwasanCommonflags {
   flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,"+flag)
  }

  if Bool(sanProps.Writeonly) {
   flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-hwasan-instrument-reads=0")
  }
  if !ctx.staticBinary() && !ctx.Host() {

   // Check if this module needs to use the bootstrap linker
   useBootstrap := ctx.bootstrap()
   if ctx.Config().GetBuildFlagBool("RELEASE_DEPRECATE_RUNTIME_APEX") {
    useBootstrap = false
   }

   if useBootstrap {
    flags.DynamicLinker = "/system/bin/bootstrap/linker_hwasan64"
   } else {
    flags.DynamicLinker = "/system/bin/linker_hwasan64"
   }
  }
 }

 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.
  _, flags.Local.LdFlags = removeFromList("-flto", flags.Local.LdFlags)
  _, flags.Local.CFlags = removeFromList("-flto", flags.Local.CFlags)
  flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-lto")
  flags.Local.CFlags = append(flags.Local.CFlags, "-fno-lto")

  // TODO(b/142430592): Upstream linker scripts for sanitizer runtime libraries
  // discard the sancov_lowest_stack symbol, because it's emulated TLS (and thus
  // doesn't match the linker script due to the "__emutls_v." prefix).
  flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-sanitize-coverage=stack-depth")
  flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-coverage=stack-depth")

  // Disable fortify for fuzzing builds. Generally, we'll be building with
  // UBSan or ASan here and the fortify checks pollute the stack traces.
  flags.Local.CFlags = append(flags.Local.CFlags, "-U_FORTIFY_SOURCE")

  // Build fuzzer-sanitized libraries with an $ORIGIN DT_RUNPATH. Android's
  // linker uses DT_RUNPATH, not DT_RPATH. When we deploy cc_fuzz targets and
  // their libraries to /data/fuzz/<arch>/lib, any transient shared library gets
  // the DT_RUNPATH from the shared library above it, and not the executable,
  // meaning that the lookup falls back to the system. Adding the $ORIGIN to the
  // DT_RUNPATH here means that transient shared libraries can be found
  // colocated with their parents.
  flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN`)
 }

 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.
   flags.RequiredInstructionSet = "thumb"
  }

  flags.Local.CFlags = append(flags.Local.CFlags, cfiCflags...)
  flags.Local.AsFlags = append(flags.Local.AsFlags, cfiAsflags...)
  flags.CFlagsDeps = append(flags.CFlagsDeps, android.PathForSource(ctx, cfiBlocklistPath+"/"+cfiBlocklistFilename))
  if Bool(s.Properties.Sanitize.Config.Cfi_assembly_support) {
   flags.Local.CFlags = append(flags.Local.CFlags, cfiAssemblySupportFlag)
  }
  // Only append the default visibility flag if -fvisibility has not already been set
  // to hidden.
  if !inList("-fvisibility=hidden", flags.Local.CFlags) {
   flags.Local.CFlags = append(flags.Local.CFlags, "-fvisibility=default")
  }
  flags.Local.LdFlags = append(flags.Local.LdFlags, cfiLdflags...)

  if ctx.staticBinary() {
   _, flags.Local.CFlags = removeFromList("-fsanitize-cfi-cross-dso", flags.Local.CFlags)
   _, flags.Local.LdFlags = removeFromList("-fsanitize-cfi-cross-dso", flags.Local.LdFlags)
  }
 }

 if Bool(sanProps.Memtag_stack) {
  flags.Local.CFlags = append(flags.Local.CFlags, memtagStackCommonFlags...)
  flags.Local.AsFlags = append(flags.Local.AsFlags, memtagStackCommonFlags...)
  flags.Local.LdFlags = append(flags.Local.LdFlags, memtagStackCommonFlags...)

  for _, flag := range memtagStackLlvmFlags {
   flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", flag)
  }
  for _, flag := range memtagStackLlvmFlags {
   flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,"+flag)
  }
 }

 if (Bool(sanProps.Memtag_heap) || Bool(sanProps.Memtag_stack) || Bool(sanProps.Memtag_globals)) && 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(sanProps.Integer_overflow) {
  flags.Local.CFlags = append(flags.Local.CFlags, intOverflowCflags...)
 }

 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)

  if ctx.toolchain().Bionic() || ctx.toolchain().Musl() {
   // Bionic and musl sanitizer runtimes have already been added as dependencies so that
   // the right variant of the runtime will be used (with the "-android" or "-musl"
   // suffixes), so don't let clang the runtime library.
   flags.Local.LdFlags = append(flags.Local.LdFlags, noSanitizeLinkRuntimeFlag)
  } else {
   // Host sanitizers only link symbols in the final executable, so
   // there will always be undefined symbols in intermediate libraries.
   _, flags.Global.LdFlags = removeFromList("-Wl,--no-undefined", flags.Global.LdFlags)
  }

  if !ctx.toolchain().Bionic() {
   // non-Bionic toolchain prebuilts are missing UBSan's vptr and function san.
   // Musl toolchain prebuilts have vptr and function sanitizers, but enabling them
   // implicitly enables RTTI which causes RTTI mismatch issues with dependencies.

   flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
  }

  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() {
   flags.Local.CFlags = append(flags.Local.CFlags, hostOnlySanitizeFlags...)
  } else {
   flags.Local.CFlags = append(flags.Local.CFlags, deviceOnlySanitizeFlags...)
  }

  if enableMinimalRuntime(s) {
   flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
  }

  // http://b/119329758, Android core does not boot up with this sanitizer yet.
  if toDisableImplicitIntegerChange(flags.Local.CFlags) {
   flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=implicit-integer-sign-change")
  }
  // http://b/171275751, Android doesn't build with this sanitizer yet.
  if toDisableUnsignedShiftBaseChange(flags.Local.CFlags) {
   flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=unsigned-shift-base")
  }

  if toDisableFunctionAndKcfiSanitizer(ctx, flags.Local.CFlags) {
   flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=function", "-fno-sanitize=kcfi")
  }
 }

 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 s.Properties.Sanitize.Recover != nil {
  flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-recover="+
   strings.Join(s.Properties.Sanitize.Recover, ","))
 }

 if s.Properties.Sanitize.Diag.No_recover != nil {
  flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-recover="+
   strings.Join(s.Properties.Sanitize.Diag.No_recover, ","))
 }

 blocklist := android.OptionalPathForModuleSrc(ctx, s.Properties.Sanitize.Blocklist)
 if blocklist.Valid() {
  flags.Local.CFlags = append(flags.Local.CFlags, sanitizeIgnorelistPrefix+blocklist.String())
  flags.CFlagsDeps = append(flags.CFlagsDeps, blocklist.Path())
 }

 return flags
}

func (s *sanitize) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
 // 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(s.Properties.SanitizeMutated.Cfi) {
   entries.SubName += ".cfi"
  }
  if Bool(s.Properties.SanitizeMutated.Hwaddress) {
   entries.SubName += ".hwasan"
  }
  if Bool(s.Properties.SanitizeMutated.Scs) {
   entries.SubName += ".scs"
  }
 }
}

func (s *sanitize) inSanitizerDir() bool {
 return s.Properties.InSanitizerDir
}

// getSanitizerBoolPtr returns the SanitizerTypes associated bool pointer from SanitizeProperties.
func (s *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool {
 switch t {
 case Asan:
  return s.Properties.SanitizeMutated.Address
 case Hwasan:
  return s.Properties.SanitizeMutated.Hwaddress
 case tsan:
  return s.Properties.SanitizeMutated.Thread
 case intOverflow:
  return s.Properties.SanitizeMutated.Integer_overflow
 case cfi:
  return s.Properties.SanitizeMutated.Cfi
 case scs:
  return s.Properties.SanitizeMutated.Scs
 case Memtag_heap:
  return s.Properties.SanitizeMutated.Memtag_heap
 case Memtag_stack:
  return s.Properties.SanitizeMutated.Memtag_stack
 case Memtag_globals:
  return s.Properties.SanitizeMutated.Memtag_globals
 case Fuzzer:
  return s.Properties.SanitizeMutated.Fuzzer
 default:
  panic(fmt.Errorf("unknown SanitizerType %d", t))
 }
}

// isUnsanitizedVariant returns true if no sanitizers are enabled.
func (sanitize *sanitize) isUnsanitizedVariant() bool {
 return !sanitize.isSanitizerEnabled(Asan) &&
  !sanitize.isSanitizerEnabled(Hwasan) &&
  !sanitize.isSanitizerEnabled(tsan) &&
  !sanitize.isSanitizerEnabled(cfi) &&
  !sanitize.isSanitizerEnabled(scs) &&
  !sanitize.isSanitizerEnabled(Memtag_heap) &&
  !sanitize.isSanitizerEnabled(Memtag_stack) &&
  !sanitize.isSanitizerEnabled(Memtag_globals) &&
  !sanitize.isSanitizerEnabled(Fuzzer)
}

// isVariantOnProductionDevice returns true if variant is for production devices (no non-production sanitizers enabled).
func (sanitize *sanitize) isVariantOnProductionDevice() bool {
 return !sanitize.isSanitizerEnabled(Asan) &&
  !sanitize.isSanitizerEnabled(Hwasan) &&
  !sanitize.isSanitizerEnabled(tsan) &&
  !sanitize.isSanitizerEnabled(Fuzzer)
}

func (sanitize *sanitize) SetSanitizer(t SanitizerType, b bool) {
 bPtr := proptools.BoolPtr(b)
 if !b {
  bPtr = nil
 }
 switch t {
 case Asan:
  sanitize.Properties.SanitizeMutated.Address = bPtr
  // For ASAN variant, we need to disable Memtag_stack
  sanitize.Properties.SanitizeMutated.Memtag_stack = nil
  sanitize.Properties.SanitizeMutated.Memtag_globals = nil
 case Hwasan:
  sanitize.Properties.SanitizeMutated.Hwaddress = bPtr
  // For HWAsan variant, we need to disable Memtag_stack
  sanitize.Properties.SanitizeMutated.Memtag_stack = nil
  sanitize.Properties.SanitizeMutated.Memtag_globals = nil
 case tsan:
  sanitize.Properties.SanitizeMutated.Thread = bPtr
 case intOverflow:
  sanitize.Properties.SanitizeMutated.Integer_overflow = bPtr
 case cfi:
  sanitize.Properties.SanitizeMutated.Cfi = bPtr
 case scs:
  sanitize.Properties.SanitizeMutated.Scs = bPtr
 case Memtag_heap:
  sanitize.Properties.SanitizeMutated.Memtag_heap = bPtr
 case Memtag_stack:
  sanitize.Properties.SanitizeMutated.Memtag_stack = bPtr
  // We do not need to disable ASAN or HWASan here, as there is no Memtag_stack variant.
 case Memtag_globals:
  sanitize.Properties.Sanitize.Memtag_globals = bPtr
 case Fuzzer:
  sanitize.Properties.SanitizeMutated.Fuzzer = bPtr
 default:
  panic(fmt.Errorf("unknown SanitizerType %d", t))
 }
 if b {
  sanitize.Properties.SanitizerEnabled = true
 }
}

// Check if the sanitizer is explicitly disabled (as opposed to nil by
// virtue of not being set).
func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t SanitizerType) bool {
 if sanitize == nil {
  return false
 }

 sanitizerVal := sanitize.getSanitizerBoolPtr(t)
 return sanitizerVal != nil && *sanitizerVal == false
}

// There isn't an analog of the method above (ie:isSanitizerExplicitlyEnabled)
// because enabling a sanitizer either directly (via the blueprint) or
// indirectly (via a mutator) sets the bool ptr to true, and you can't
// distinguish between the cases. It isn't needed though - both cases can be
// treated identically.
func (s *sanitize) isSanitizerEnabled(t SanitizerType) bool {
 if s == nil {
  return false
 }
 if s.Properties.ForceDisable || proptools.Bool(s.Properties.SanitizeMutated.Never) {
  return false
 }

 sanitizerVal := s.getSanitizerBoolPtr(t)
 return sanitizerVal != nil && *sanitizerVal == true
}

// IsSanitizableDependencyTag returns true if the dependency tag is sanitizable.
func IsSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
 switch t := tag.(type) {
 case dependencyTag:
  return t == reuseObjTag || t == objDepTag
 case libraryDependencyTag:
  return true
 default:
  return false
 }
}

func (m *Module) SanitizableDepTagChecker() SantizableDependencyTagChecker {
 return IsSanitizableDependencyTag
}

// @auto-generate: gob
type PlatformSanitizeableInfo struct {
 // IsSanitizerEnabled is a list of enabled flags for each sanitizer in the same order as Sanitizers.
 // If it is non-nil it will be the same length as Sanitizers.
 IsSanitizerEnabled []bool
}

var PlatformSanitizeableInfoProvider = blueprint.NewMutatorProvider[PlatformSanitizeableInfo]("sanitize_markapexes")

// If an APEX is sanitized or not depends on whether it contains at least one
// sanitized module. Transition mutators cannot propagate information up the
// dependency graph this way, so we need an auxiliary mutator to do so.
func markSanitizableApexesMutator(ctx android.BottomUpMutatorContext) {
 if sanitizeable, ok := ctx.Module().(Sanitizeable); ok {
  enabled := make([]bool, len(Sanitizers))
  for i, sanitizer := range Sanitizers {
   enabled[i] = sanitizeable.IsSanitizerEnabled(ctx.Config(), sanitizer.name())
  }

  ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
   if info, ok := android.OtherModuleProvider(ctx, dep, PlatformSanitizeableInfoProvider); ok &&
    len(info.IsSanitizerEnabled) > 0 {
    for i := range Sanitizers {
     enabled[i] = enabled[i] || info.IsSanitizerEnabled[i]
    }
   }
  })

  for i, sanitizer := range Sanitizers {
   if enabled[i] {
    sanitizeable.EnableSanitizer(sanitizer.name())
   }
  }
 } else if sanitizeable, ok := ctx.Module().(PlatformSanitizeable); ok {
  var isSanitizerEnabled []bool
  for i, sanitizer := range Sanitizers {
   if sanitizeable.IsSanitizerEnabled(sanitizer) {
    if isSanitizerEnabled == nil {
     isSanitizerEnabled = make([]bool, len(Sanitizers))
    }
    isSanitizerEnabled[i] = true
   }
  }
  android.SetProvider(ctx, PlatformSanitizeableInfoProvider, PlatformSanitizeableInfo{
   IsSanitizerEnabled: isSanitizerEnabled,
  })
 }
}

type sanitizerSplitMutator struct {
 sanitizer SanitizerType
}

func (s *sanitizerSplitMutator) split(ctx android.BaseModuleContext) []string {
 if c, ok := ctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
  // If the given sanitizer is not requested in the .bp file for a module, it
  // won't automatically build the sanitized variation.
  if !c.IsSanitizerEnabled(s.sanitizer) {
   return []string{""}
  }

  if c.Binary() {
   // If a sanitizer is enabled for a binary, we do not build the version
   // without the sanitizer
   return []string{s.sanitizer.variationName()}
  } else if c.StaticallyLinked() || c.Header() {
   // For static libraries, we build both versions. Some Make modules
   // apparently depend on this behavior.
   return []string{"", s.sanitizer.variationName()}
  } else {
   // We only build the requested variation of dynamic libraries
   return []string{s.sanitizer.variationName()}
  }
 }

 if _, ok := ctx.Module().(JniSanitizeable); ok {
  // TODO: this should call into JniSanitizable.IsSanitizerEnabledForJni but
  // that is short-circuited for now
  return []string{""}
 }

 // If an APEX has a sanitized dependency, we build the APEX in the sanitized
 // variation. This is useful because such APEXes require extra dependencies.
 if sanitizeable, ok := ctx.Module().(Sanitizeable); ok {
  enabled := sanitizeable.IsSanitizerEnabled(ctx.Config(), s.sanitizer.name())
  if enabled {
   return []string{s.sanitizer.variationName()}
  } else {
   return []string{""}
  }
 }

 return []string{""}
}

func (s *sanitizerSplitMutator) Split(ctx android.BaseModuleContext) []string {
 allSplits := s.split(ctx)
 if ctx.Config().GetBuildFlagBool("RELEASE_SOONG_SANITIZER_VARIANT_ON_DEMAND") {
  return allSplits[0:1]
 } else {
  return allSplits
 }
}

func (s *sanitizerSplitMutator) SplitOnDemand(ctx android.BaseModuleContext) []string {
 allSplits := s.split(ctx)
 if len(allSplits) <= 1 || !ctx.Config().GetBuildFlagBool("RELEASE_SOONG_SANITIZER_VARIANT_ON_DEMAND") {
  return nil
 } else {
  return allSplits[1:]
 }
}

func (s *sanitizerSplitMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
 if c, ok := ctx.Module().(PlatformSanitizeable); ok {
  if !c.SanitizableDepTagChecker()(ctx.DepTag()) {
   // If the dependency is through a non-sanitizable tag, use the
   // non-sanitized variation
   return ""
  }

  return sourceVariation
 } else if _, ok := ctx.Module().(JniSanitizeable); ok {
  // TODO: this should call into JniSanitizable.IsSanitizerEnabledForJni but
  // that is short-circuited for now
  return ""
 } else {
  // Otherwise, do not rock the boat.
  return sourceVariation
 }
}

func (s *sanitizerSplitMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
 if d, ok := ctx.Module().(PlatformSanitizeable); ok {
  if !d.SanitizePropDefined() ||
   d.SanitizeNever() ||
   d.IsSanitizerExplicitlyDisabled(s.sanitizer) ||
   !d.SanitizerSupported(s.sanitizer) {
   // If a module opts out of a sanitizer, use its non-sanitized variation
   return ""
  }

  // Binaries are always built in the variation they requested.
  if d.Binary() {
   if d.IsSanitizerEnabled(s.sanitizer) {
    return s.sanitizer.variationName()
   } else {
    return ""
   }
  }

  // If a shared library requests to be sanitized, it will be built for that
  // sanitizer. Otherwise, some sanitizers propagate through shared library
  // dependency edges, some do not.
  if !d.StaticallyLinked() && !d.Header() {
   if d.IsSanitizerEnabled(s.sanitizer) {
    return s.sanitizer.variationName()
   }

   // Some sanitizers do not propagate to shared dependencies
   if !s.sanitizer.shouldPropagateToSharedLibraryDeps() {
    return ""
   }
  }

  // Static and header libraries inherit whether they are sanitized from the
  // module they are linked into
  return incomingVariation
 } else if d, ok := ctx.Module().(Sanitizeable); ok {
  // If an APEX contains a sanitized module, it will be built in the variation
  // corresponding to that sanitizer.
  enabled := d.IsSanitizerEnabled(ctx.Config(), s.sanitizer.name())
  if enabled {
   return s.sanitizer.variationName()
  }

  return incomingVariation
 }

 return ""
}

func (s *sanitizerSplitMutator) Mutate(mctx android.BottomUpMutatorContext, variationName string) {
 sanitizerVariation := variationName == s.sanitizer.variationName()

 if c, ok := mctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
  sanitizerEnabled := c.IsSanitizerEnabled(s.sanitizer)

  oneMakeVariation := false
  if c.StaticallyLinked() || c.Header() {
   if s.sanitizer != cfi && s.sanitizer != scs && s.sanitizer != Hwasan {
    // These sanitizers export only one variation to Make. For the rest,
    // Make targets can depend on both the sanitized and non-sanitized
    // versions.
    oneMakeVariation = true
   }
  } else if !c.Binary() {
   // Shared library. These are the sanitizers that do propagate through shared
   // library dependencies and therefore can cause multiple variations of a
   // shared library to be built.
   if s.sanitizer != cfi && s.sanitizer != Hwasan && s.sanitizer != scs && s.sanitizer != Asan {
    oneMakeVariation = true
   }
  }

  if oneMakeVariation {
   if sanitizerEnabled != sanitizerVariation {
    c.SetPreventInstall()
    c.SetHideFromMake()
   }
  }

  if sanitizerVariation {
   c.SetSanitizer(s.sanitizer, true)

   // CFI is incompatible with ASAN so disable it in ASAN variations
   if s.sanitizer.incompatibleWithCfi() {
    cfiSupported := mctx.Module().(PlatformSanitizeable).SanitizerSupported(cfi)
    if mctx.Device() && cfiSupported {
     c.SetSanitizer(cfi, false)
    }
   }

   // locate the asan libraries under /data/asan
   if !c.Binary() && !c.StaticallyLinked() && !c.Header() && mctx.Device() && s.sanitizer == Asan && sanitizerEnabled {
    c.SetInSanitizerDir()
   }

   if c.StaticallyLinked() && c.ExportedToMake() {
    if s.sanitizer == Hwasan {
     hwasanStaticLibs(mctx.Config()).add(c, c.Module().Name())
    } else if s.sanitizer == cfi {
     cfiStaticLibs(mctx.Config()).add(c, c.Module().Name())
    } else if s.sanitizer == Memtag_stack {
     memtagStackStaticLibs(mctx.Config()).add(c, c.Module().Name())
    }
   }
  } else if c.IsSanitizerEnabled(s.sanitizer) {
   // Disable the sanitizer for the non-sanitized variation
   c.SetSanitizer(s.sanitizer, false)
  }
 } else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok {
  // If an APEX has sanitized dependencies, it gets a few more dependencies
  if sanitizerVariation {
   sanitizeable.AddSanitizerDependencies(mctx, s.sanitizer.name())
  }
 }
}

func (c *Module) SanitizeNever() bool {
 return c.sanitize.Properties.ForceDisable || Bool(c.sanitize.Properties.SanitizeMutated.Never)
}

func (c *Module) IsSanitizerExplicitlyDisabled(t SanitizerType) bool {
 return c.sanitize.isSanitizerExplicitlyDisabled(t)
}

// SanitizerRuntimeDepInfo is exported from static libraries that have sanitizers enabled that require a runtime.
// @auto-generate: gob
type SanitizerRuntimeDepInfo struct {
 MinimalRuntimeNeeded bool
 UbsanRuntimeNeeded   bool
}

var SanitizerRuntimeDepInfoProvider = blueprint.NewMutatorProvider[SanitizerRuntimeDepInfo]("sanitize_runtime_deps")

// Propagate the ubsan minimal runtime dependency when there are integer overflow sanitized static dependencies.
func sanitizerRuntimeDepsMutator(mctx android.BottomUpMutatorContext) {
 // Change this to PlatformSanitizable when/if non-cc modules support ubsan sanitizers.
 if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
  if c.sanitize.Properties.ForceDisable {
   return
  }

  var info SanitizerRuntimeDepInfo
  info.MinimalRuntimeNeeded = enableMinimalRuntime(c.sanitize)
  info.UbsanRuntimeNeeded = enableUbsanRuntime(c.sanitize)

  // Visit dependencies to determine if any require the runtime to be linked in.
  isSanitizableDependencyTag := c.SanitizableDepTagChecker()
  mctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
   if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(dep)) {
    return
   }

   if depInfo, ok := android.OtherModuleProvider(mctx, dep, SanitizerRuntimeDepInfoProvider); ok {
    info.MinimalRuntimeNeeded = info.MinimalRuntimeNeeded || depInfo.MinimalRuntimeNeeded
    info.UbsanRuntimeNeeded = info.UbsanRuntimeNeeded || depInfo.UbsanRuntimeNeeded
   }
  })

  if info.MinimalRuntimeNeeded {
   // If a static dependency is built with the minimal runtime,
   // make sure we include the ubsan minimal runtime.
   c.sanitize.Properties.MinimalRuntimeDep = true
  }
  if info.UbsanRuntimeNeeded {
   // If a static dependency runs with full ubsan diagnostics,
   // make sure we include the ubsan runtime.
   c.sanitize.Properties.UbsanRuntimeDep = true
  }

  // If this is a static library with a runtime requirement then re-export the requirement.
  if c.static() && (info != SanitizerRuntimeDepInfo{}) {
   android.SetProvider(mctx, SanitizerRuntimeDepInfoProvider, info)
  }
 }
}

// Add the dependency to the runtime library for each of the sanitizer variants
func sanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) {
 if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
  if !c.Enabled(mctx) {
   return
  }
  if c.sanitize.Properties.ForceDisable {
   return
  }

  var sanitizers []string
  var diagSanitizers []string

  sanProps := &c.sanitize.Properties.SanitizeMutated

  if Bool(sanProps.All_undefined) {
   sanitizers = append(sanitizers, "undefined")
  } else {
   if Bool(sanProps.Undefined) {
    sanitizers = append(sanitizers,
     "bool",
     "integer-divide-by-zero",
     "object-size",
     "return",
     "returns-nonnull-attribute",
     "shift-exponent",
     "unreachable",
     "vla-bound",
     // TODO(danalbert): The following checks currently have compiler performance issues.
     //"alignment",
     //"bounds",
     //"enum",
     //"float-cast-overflow",
     //"float-divide-by-zero",
     //"nonnull-attribute",
     //"null",
     //"shift-base",
     //"signed-integer-overflow",
    )
   }
   sanitizers = append(sanitizers, sanProps.Misc_undefined...)
  }

  if Bool(sanProps.Diag.Undefined) {
   diagSanitizers = append(diagSanitizers, "undefined")
  }

  diagSanitizers = append(diagSanitizers, sanProps.Diag.Misc_undefined...)

  if Bool(sanProps.Address) {
   sanitizers = append(sanitizers, "address")
   diagSanitizers = append(diagSanitizers, "address")
  }

  if Bool(sanProps.Hwaddress) {
   sanitizers = append(sanitizers, "hwaddress")
  }

  if Bool(sanProps.Thread) {
   sanitizers = append(sanitizers, "thread")
  }

  if Bool(sanProps.Safestack) {
   sanitizers = append(sanitizers, "safe-stack")
  }

  if Bool(sanProps.Cfi) {
   sanitizers = append(sanitizers, "cfi")

   if Bool(sanProps.Diag.Cfi) {
    diagSanitizers = append(diagSanitizers, "cfi")
   }
  }

  if Bool(sanProps.Integer_overflow) {
   sanitizers = append(sanitizers, "unsigned-integer-overflow")
   sanitizers = append(sanitizers, "signed-integer-overflow")
   if Bool(sanProps.Diag.Integer_overflow) {
    diagSanitizers = append(diagSanitizers, "unsigned-integer-overflow")
    diagSanitizers = append(diagSanitizers, "signed-integer-overflow")
   }
  }

  if Bool(sanProps.Scudo) {
   sanitizers = append(sanitizers, "scudo")
  }

  if Bool(sanProps.Scs) {
   sanitizers = append(sanitizers, "shadow-call-stack")
  }

  if Bool(sanProps.Memtag_heap) && c.Binary() {
   sanitizers = append(sanitizers, "memtag-heap")
  }

  if Bool(sanProps.Memtag_stack) {
   sanitizers = append(sanitizers, "memtag-stack")
  }

  if Bool(sanProps.Memtag_globals) {
   sanitizers = append(sanitizers, "memtag-globals")
  }

  if Bool(sanProps.Fuzzer) {
   sanitizers = append(sanitizers, "fuzzer-no-link")
  }

  // Save the list of sanitizers. These will be used again when generating
  // the build rules (for Cflags, etc.)
  c.sanitize.Properties.Sanitizers = sanitizers
  c.sanitize.Properties.DiagSanitizers = diagSanitizers

  // TODO(b/150822854) Hosts have a different default behavior and assume the runtime library is used.
  if c.Host() {
   diagSanitizers = sanitizers
  }

  addStaticDeps := func(dep string, hideSymbols bool) {
   // static executable gets static runtime libs
   depTag := libraryDependencyTag{Kind: staticLibraryDependency, unexportedSymbols: hideSymbols}
   variations := append(mctx.Target().Variations(),
    blueprint.Variation{Mutator: "link", Variation: "static"})
   if c.Device() {
    variations = append(variations, c.ImageVariation())
   }
   if c.UseSdk() {
    variations = append(variations,
     blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
   }
   mctx.AddFarVariationDependencies(variations, depTag, dep)
  }

  // Determine the runtime library required
  runtimeSharedLibrary := ""
  toolchain := c.toolchain(mctx)
  if Bool(sanProps.Address) {
   if toolchain.Musl() || (c.staticBinary() && toolchain.Bionic()) {
    // Use a static runtime for musl to match what clang does for glibc.
    addStaticDeps(config.AddressSanitizerStaticRuntimeLibrary(), false)
    addStaticDeps(config.AddressSanitizerCXXStaticRuntimeLibrary(), false)
   } else {
    runtimeSharedLibrary = config.AddressSanitizerRuntimeLibrary()
   }
  } else if Bool(sanProps.Hwaddress) {
   if c.staticBinary() {
    addStaticDeps(config.HWAddressSanitizerStaticLibrary(), true)
    addStaticDeps("libdl", false)
   } else {
    runtimeSharedLibrary = config.HWAddressSanitizerRuntimeLibrary()
   }
  } else if Bool(sanProps.Thread) {
   runtimeSharedLibrary = config.ThreadSanitizerRuntimeLibrary()
  } else if Bool(sanProps.Scudo) {
   if len(diagSanitizers) == 0 && !c.sanitize.Properties.UbsanRuntimeDep {
    runtimeSharedLibrary = config.ScudoMinimalRuntimeLibrary()
   } else {
    runtimeSharedLibrary = config.ScudoRuntimeLibrary()
   }
  } else if len(diagSanitizers) > 0 || c.sanitize.Properties.UbsanRuntimeDep ||
   Bool(sanProps.Fuzzer) ||
   Bool(sanProps.Undefined) ||
   Bool(sanProps.All_undefined) {
   if toolchain.Musl() || c.staticBinary() {
    // Use a static runtime for static binaries.  For sanitized glibc binaries the runtime is
    // added automatically by clang, but for static glibc binaries that are not sanitized but
    // have a sanitized dependency the runtime needs to be added manually.
    // Also manually add a static runtime for musl to match what clang does for glibc.
    // Otherwise dlopening libraries that depend on libclang_rt.ubsan_standalone.so fails with:
    // Error relocating ...: initial-exec TLS resolves to dynamic definition
    addStaticDeps(config.UndefinedBehaviorSanitizerRuntimeLibrary()+".static", true)
   } else {
    runtimeSharedLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary()
   }
  }

  if enableMinimalRuntime(c.sanitize) || c.sanitize.Properties.MinimalRuntimeDep {
   addStaticDeps(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(), true)
  }

  if runtimeSharedLibrary != "" && (toolchain.Bionic() || toolchain.Musl()) {
   // UBSan is supported on non-bionic linux host builds as well

   // Adding dependency to the runtime library. We are using *FarVariation*
   // because the runtime libraries themselves are not mutated by sanitizer
   // mutators and thus don't have sanitizer variants whereas this module
   // has been already mutated.
   //
   // Note that by adding dependency with {static|shared}DepTag, the lib is
   // added to libFlags and LOCAL_SHARED_LIBRARIES by cc.Module
   if c.staticBinary() {
    // Most sanitizers are either disabled for static binaries or have already
    // handled the static binary case above through a direct call to addStaticDeps.
    // If not, treat the runtime shared library as a static library and hope for
    // the best.
    addStaticDeps(runtimeSharedLibrary, true)
   } else if !c.static() && !c.Header() {
    // Skip apex dependency check for sharedLibraryDependency
    // when sanitizer diags are enabled. Skipping the check will allow
    // building with diag libraries without having to list the
    // dependency in Apex's allowed_deps file.
    diagEnabled := len(diagSanitizers) > 0
    // dynamic executable and shared libs get shared runtime libs
    depTag := libraryDependencyTag{
     Kind:  sharedLibraryDependency,
     Order: earlyLibraryDependency,

     skipApexAllowedDependenciesCheck: diagEnabled,
    }
    variations := append(mctx.Target().Variations(),
     blueprint.Variation{Mutator: "link", Variation: "shared"})
    if c.Device() {
     variations = append(variations, c.ImageVariation())
    }
    if c.UseSdk() {
     variations = append(variations,
      blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
    }
    AddSharedLibDependenciesWithVersions(mctx, c, variations, depTag, runtimeSharedLibrary, "", true)
   }
   // static lib does not have dependency to the runtime library. The
   // dependency will be added to the executables or shared libs using
   // the static lib.
  }
 }
}

type Sanitizeable interface {
 android.Module
 IsSanitizerEnabled(config android.Config, sanitizerName string) bool
 EnableSanitizer(sanitizerName string)
 AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string)
}

type JniSanitizeable interface {
 android.Module
 IsSanitizerEnabledForJni(ctx android.BaseModuleContext, sanitizerName string) bool
}

func (c *Module) MinimalRuntimeDep() bool {
 return c.sanitize.Properties.MinimalRuntimeDep
}

func (c *Module) UbsanRuntimeDep() bool {
 return c.sanitize.Properties.UbsanRuntimeDep
}

func (c *Module) SanitizePropDefined() bool {
 return c.sanitize != nil
}

func (c *Module) IsSanitizerEnabled(t SanitizerType) bool {
 return c.sanitize.isSanitizerEnabled(t)
}

func (c *Module) StaticallyLinked() bool {
 return c.static()
}

func (c *Module) SetInSanitizerDir() {
 if c.sanitize != nil {
  c.sanitize.Properties.InSanitizerDir = true
 }
}

func (c *Module) SetSanitizer(t SanitizerType, b bool) {
 if c.sanitize != nil {
  c.sanitize.SetSanitizer(t, b)
 }
}

var _ PlatformSanitizeable = (*Module)(nil)

type sanitizerStaticLibsMap struct {
 // libsMap contains one list of modules per each image and each arch.
 // e.g. libs[vendor]["arm"] contains arm modules installed to vendor
 libsMap       map[ImageVariantType]map[string][]string
 libsMapLock   sync.Mutex
 sanitizerType SanitizerType
}

func newSanitizerStaticLibsMap(t SanitizerType) *sanitizerStaticLibsMap {
 return &sanitizerStaticLibsMap{
  sanitizerType: t,
  libsMap:       make(map[ImageVariantType]map[string][]string),
 }
}

// Add the current module to sanitizer static libs maps
// Each module should pass its exported name as names of Make and Soong can differ.
func (s *sanitizerStaticLibsMap) add(c LinkableInterface, name string) {
 image := GetImageVariantType(c)
 arch := c.Module().Target().Arch.ArchType.String()

 s.libsMapLock.Lock()
 defer s.libsMapLock.Unlock()

 if _, ok := s.libsMap[image]; !ok {
  s.libsMap[image] = make(map[string][]string)
 }

 s.libsMap[image][arch] = append(s.libsMap[image][arch], name)
}

// Exports makefile variables in the following format:
// SOONG_{sanitizer}_{image}_{arch}_STATIC_LIBRARIES
// e.g. SOONG_cfi_core_x86_STATIC_LIBRARIES
// These are to be used by use_soong_sanitized_static_libraries.
// See build/make/core/binary.mk for more details.
func (s *sanitizerStaticLibsMap) exportToMake(ctx android.MakeVarsContext) {
 for _, image := range android.SortedKeys(s.libsMap) {
  archMap := s.libsMap[ImageVariantType(image)]
  for _, arch := range android.SortedKeys(archMap) {
   libs := archMap[arch]
   sort.Strings(libs)

   key := fmt.Sprintf(
    "SOONG_%s_%s_%s_STATIC_LIBRARIES",
    s.sanitizerType.variationName(),
    image, // already upper
    arch)

   ctx.Strict(key, strings.Join(libs, " "))
  }
 }
}

var cfiStaticLibsKey = android.NewOnceKey("cfiStaticLibs")

func cfiStaticLibs(config android.Config) *sanitizerStaticLibsMap {
 return config.Once(cfiStaticLibsKey, func() interface{} {
  return newSanitizerStaticLibsMap(cfi)
 }).(*sanitizerStaticLibsMap)
}

var hwasanStaticLibsKey = android.NewOnceKey("hwasanStaticLibs")

func hwasanStaticLibs(config android.Config) *sanitizerStaticLibsMap {
 return config.Once(hwasanStaticLibsKey, func() interface{} {
  return newSanitizerStaticLibsMap(Hwasan)
 }).(*sanitizerStaticLibsMap)
}

var memtagStackStaticLibsKey = android.NewOnceKey("memtagStackStaticLibs")

func memtagStackStaticLibs(config android.Config) *sanitizerStaticLibsMap {
 return config.Once(memtagStackStaticLibsKey, func() interface{} {
  return newSanitizerStaticLibsMap(Memtag_stack)
 }).(*sanitizerStaticLibsMap)
}

func enableMinimalRuntime(sanitize *sanitize) bool {
 if sanitize.isSanitizerEnabled(Asan) {
  return false
 } else if sanitize.isSanitizerEnabled(Hwasan) {
  return false
 } else if sanitize.isSanitizerEnabled(Fuzzer) {
  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 {
 return enableUbsanRuntime(m.sanitize)
}

func (m *Module) MinimalRuntimeNeeded() bool {
 return enableMinimalRuntime(m.sanitize)
}

func enableUbsanRuntime(sanitize *sanitize) bool {
 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) {
 cfiStaticLibs(ctx.Config()).exportToMake(ctx)
}

func hwasanMakeVarsProvider(ctx android.MakeVarsContext) {
 hwasanStaticLibs(ctx.Config()).exportToMake(ctx)
}

func memtagStackMakeVarsProvider(ctx android.MakeVarsContext) {
 memtagStackStaticLibs(ctx.Config()).exportToMake(ctx)
}

type sanitizerLibrariesTxtModule struct {
 android.ModuleBase

 outputFile android.Path
}

var _ etc.PrebuiltEtcModule = (*sanitizerLibrariesTxtModule)(nil)

func RegisterSanitizerLibrariesTxtType(ctx android.RegistrationContext) {
 ctx.RegisterModuleType("sanitizer_libraries_txt", sanitizerLibrariesTxtFactory)
}

func sanitizerLibrariesTxtFactory() android.Module {
 m := &sanitizerLibrariesTxtModule{}
 android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
 return m
}

type sanitizerLibraryDependencyTag struct {
 blueprint.BaseDependencyTag
 android.AlwaysAllowDisabledModuleDependencyTag
}

var _ android.AllowDisabledModuleDependency = (*sanitizerLibraryDependencyTag)(nil)

func (txt *sanitizerLibrariesTxtModule) DepsMutator(actx android.BottomUpMutatorContext) {
 targets := actx.Config().Targets[android.Android]
 depTag := sanitizerLibraryDependencyTag{}

 for _, target := range targets {
  variation := append(target.Variations(),
   blueprint.Variation{Mutator: "image", Variation: ""},
   blueprint.Variation{Mutator: "sdk", Variation: ""},
   blueprint.Variation{Mutator: "link", Variation: "shared"},
  )
  for _, lib := range android.SortedStringValues(sanitizerVariables) {
   if actx.OtherModuleFarDependencyVariantExists(variation, lib) {
    actx.AddFarVariationDependencies(variation, depTag, lib)
   }

   prebuiltLibName := "prebuilt_" + lib
   if actx.OtherModuleFarDependencyVariantExists(variation, prebuiltLibName) {
    actx.AddFarVariationDependencies(variation, depTag, prebuiltLibName)
   }
  }
 }

}

func (txt *sanitizerLibrariesTxtModule) getSanitizerLibs(ctx android.ModuleContext) string {
 var sanitizerLibStems []string

 ctx.VisitDirectDepsProxy(func(m android.ModuleProxy) {
  info := android.OtherModuleProviderOrDefault(ctx, m, android.CommonModuleInfoProvider)
  if !info.Enabled {
   return
  }

  if _, ok := android.OtherModuleProvider(ctx, m, SharedLibraryInfoProvider); !ok {
   return
  }

  targets := ctx.Config().Targets[android.Android]

  var targetMatches bool
  for _, target := range targets {
   if info.Target.Os == target.Os && info.Target.Arch.ArchType == target.Arch.ArchType {
    targetMatches = true
   }
  }

  if !targetMatches {
   return
  }

  outputFiles := android.OutputFilesForModule(ctx, m, "")
  if len(outputFiles) == 1 {
   sanitizerLibStems = append(sanitizerLibStems, outputFiles[0].Base())
  } else if len(outputFiles) > 1 {
   panic(fmt.Errorf("multiple output files for %s: %s", m, outputFiles.Strings()))
  }
 })

 sanitizerLibStems = android.SortedUniqueStrings(sanitizerLibStems)
 return strings.Join(sanitizerLibStems, "\n")
}

func (txt *sanitizerLibrariesTxtModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 filename := txt.Name()

 outputFile := android.PathForModuleOut(ctx, filename)
 android.WriteFileRule(ctx, outputFile, txt.getSanitizerLibs(ctx))

 installPath := android.PathForModuleInstall(ctx, "etc")
 ctx.InstallFile(installPath, filename, outputFile)

 ctx.SetOutputFiles(android.Paths{outputFile}, "")
 txt.outputFile = outputFile

 etc.SetCommonPrebuiltEtcInfo(ctx, txt)
}

func (txt *sanitizerLibrariesTxtModule) PrepareAndroidMKProviderInfo(config android.Config) *android.AndroidMkProviderInfo {
 return &android.AndroidMkProviderInfo{
  PrimaryInfo: android.AndroidMkInfo{
   Class:      "ETC",
   OutputFile: android.OptionalPathForPath(txt.outputFile),
  },
 }
}

// PrebuiltEtcModule interface
func (txt *sanitizerLibrariesTxtModule) BaseDir() string {
 return "etc"
}

// PrebuiltEtcModule interface
func (txt *sanitizerLibrariesTxtModule) SubDir() string {
 return ""
}

[Dauer der Verarbeitung: 0.41 Sekunden, vorverarbeitet 2026-06-28]