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 50 kB image not shown  

Quelle  sanitize_test.go   Sprache: unbekannt

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

// Copyright 2021 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"
 "reflect"
 "runtime"
 "strings"
 "testing"

 "android/soong/android"
)

var prepareForAsanTest = android.FixtureAddFile("asan/Android.bp", []byte(`
 cc_library_shared {
  name: "libclang_rt.asan",
  host_supported: true,
 }
 cc_library_static {
  name: "libclang_rt.asan.static",
  host_supported: true,
 }
 cc_library_static {
  name: "libclang_rt.asan_cxx.static",
  host_supported: true,
 }
`))

var prepareForTsanTest = android.FixtureAddFile("tsan/Android.bp", []byte(`
 cc_library_shared {
  name: "libclang_rt.tsan",
  host_supported: true,
 }
`))

type providerInterface interface {
 android.OtherModuleProviderContext
}

// 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, _ := android.OtherModuleProvider(ctx, to.Module(), SharedLibraryInfoProvider)

 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)
 }
}

// expectNoSharedLinkDep verifies that the from module links against the to module as a
// shared library.
func expectNoSharedLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 t.Helper()
 fromLink := from.Description("link")
 toInfo, _ := android.OtherModuleProvider(ctx, to.Module(), SharedLibraryInfoProvider)

 if g, w := fromLink.OrderOnly.Strings(), toInfo.SharedLibrary.RelativeToTop().String(); android.InList(w, g) {
  t.Errorf("%s should not 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, _ := android.OtherModuleProvider(ctx, to.Module(), StaticLibraryInfoProvider)

 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)
 }

}

// expectNoStaticLinkDep verifies that the from module links against the to module as a
// static library.
func expectNoStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 t.Helper()
 fromLink := from.Description("link")
 toInfo, _ := android.OtherModuleProvider(ctx, to.Module(), StaticLibraryInfoProvider)

 if g, w := fromLink.Implicits.Strings(), toInfo.StaticLibrary.RelativeToTop().String(); android.InList(w, g) {
  t.Errorf("%s should not 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)
 }
}

type expectedRuntimeLinkage int

const (
 RUNTIME_LINKAGE_NONE   = expectedRuntimeLinkage(0)
 RUNTIME_LINKAGE_SHARED = iota
 RUNTIME_LINKAGE_STATIC
)

func TestAsan(t *testing.T) {
 t.Parallel()
 bp := `
  cc_binary {
   name: "bin_with_asan",
   host_supported: true,
   shared_libs: [
    "libshared",
    "libasan",
   ],
   static_libs: [
    "libstatic",
    "libnoasan",
    "libstatic_asan",
   ],
   sanitize: {
    address: true,
   }
  }

  cc_binary {
   name: "bin_no_asan",
   host_supported: true,
   shared_libs: [
    "libshared",
    "libasan",
   ],
   static_libs: [
    "libstatic",
    "libnoasan",
    "libstatic_asan",
   ],
  }

  cc_library_shared {
   name: "libshared",
   host_supported: true,
   shared_libs: ["libtransitive"],
  }

  cc_library_shared {
   name: "libasan",
   host_supported: true,
   shared_libs: ["libtransitive"],
   sanitize: {
    address: true,
   }
  }

  cc_library_shared {
   name: "libtransitive",
   host_supported: true,
  }

  cc_library_static {
   name: "libstatic",
   host_supported: true,
  }

  cc_library_static {
   name: "libnoasan",
   host_supported: true,
   sanitize: {
    address: false,
   }
  }

  cc_library_static {
   name: "libstatic_asan",
   host_supported: true,
   sanitize: {
    address: true,
   }
  }

 `

 preparer := android.GroupFixturePreparers(
  prepareForCcTest,
  prepareForAsanTest,
 )
 buildOS := preparer.RunTestWithBp(t, bp).Config.BuildOSTarget.String()

 check := func(t *testing.T, variant string, runtimeLinkage expectedRuntimeLinkage, preparer android.FixturePreparer) {
  result := preparer.RunTestWithBp(t, bp)
  ctx := result.TestContext
  asanVariant := variant + "_asan"
  sharedVariant := variant + "_shared"
  sharedAsanVariant := sharedVariant + "_asan"
  staticVariant := variant + "_static"
  staticAsanVariant := staticVariant + "_asan"

  // The binaries, one with asan and one without
  binWithAsan := result.ModuleForTests(t, "bin_with_asan", asanVariant)
  binNoAsan := result.ModuleForTests(t, "bin_no_asan", variant)

  // Shared libraries that don't request asan
  libShared := result.ModuleForTests(t, "libshared", sharedVariant)
  libTransitive := result.ModuleForTests(t, "libtransitive", sharedVariant)

  // Shared library that requests asan
  libAsan := result.ModuleForTests(t, "libasan", sharedAsanVariant)

  // Static library that uses an asan variant for bin_with_asan and a non-asan variant
  // for bin_no_asan.
  libStaticAsanVariant := result.ModuleForTests(t, "libstatic", staticAsanVariant)
  libStaticNoAsanVariant := result.ModuleForTests(t, "libstatic", staticVariant)

  // Static library that never uses asan.
  libNoAsan := result.ModuleForTests(t, "libnoasan", staticVariant)

  // Static library that specifies asan
  libStaticAsan := result.ModuleForTests(t, "libstatic_asan", staticAsanVariant)
  libStaticAsanNoAsanVariant := result.ModuleForTests(t, "libstatic_asan", staticVariant)

  libAsanSharedRuntime := result.ModuleForTests(t, "libclang_rt.asan", sharedVariant)
  libAsanStaticRuntime := result.ModuleForTests(t, "libclang_rt.asan.static", staticVariant)
  libAsanStaticCxxRuntime := result.ModuleForTests(t, "libclang_rt.asan_cxx.static", staticVariant)

  expectSharedLinkDep(t, ctx, binWithAsan, libShared)
  expectSharedLinkDep(t, ctx, binWithAsan, libAsan)
  expectSharedLinkDep(t, ctx, libShared, libTransitive)
  expectSharedLinkDep(t, ctx, libAsan, libTransitive)

  expectStaticLinkDep(t, ctx, binWithAsan, libStaticAsanVariant)
  expectStaticLinkDep(t, ctx, binWithAsan, libNoAsan)
  expectStaticLinkDep(t, ctx, binWithAsan, libStaticAsan)

  expectInstallDep(t, binWithAsan, libShared)
  expectInstallDep(t, binWithAsan, libAsan)
  expectInstallDep(t, binWithAsan, libTransitive)
  expectInstallDep(t, libShared, libTransitive)
  expectInstallDep(t, libAsan, libTransitive)

  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(t, binNoAsan, libShared)
  expectInstallDep(t, binNoAsan, libAsan)
  expectInstallDep(t, binNoAsan, libTransitive)
  expectInstallDep(t, libShared, libTransitive)
  expectInstallDep(t, libAsan, libTransitive)

  if runtimeLinkage == RUNTIME_LINKAGE_SHARED {
   expectSharedLinkDep(t, ctx, binWithAsan, libAsanSharedRuntime)
   expectNoSharedLinkDep(t, ctx, binNoAsan, libAsanSharedRuntime)
   expectSharedLinkDep(t, ctx, libAsan, libAsanSharedRuntime)
   expectNoSharedLinkDep(t, ctx, libShared, libAsanSharedRuntime)
   expectNoSharedLinkDep(t, ctx, libTransitive, libAsanSharedRuntime)
  } else {
   expectNoSharedLinkDep(t, ctx, binWithAsan, libAsanSharedRuntime)
   expectNoSharedLinkDep(t, ctx, binNoAsan, libAsanSharedRuntime)
   expectNoSharedLinkDep(t, ctx, libAsan, libAsanSharedRuntime)
   expectNoSharedLinkDep(t, ctx, libShared, libAsanSharedRuntime)
   expectNoSharedLinkDep(t, ctx, libTransitive, libAsanSharedRuntime)
  }

  if runtimeLinkage == RUNTIME_LINKAGE_STATIC {
   expectStaticLinkDep(t, ctx, binWithAsan, libAsanStaticRuntime)
   expectNoStaticLinkDep(t, ctx, binNoAsan, libAsanStaticRuntime)
   expectStaticLinkDep(t, ctx, libAsan, libAsanStaticRuntime)
   expectNoStaticLinkDep(t, ctx, libShared, libAsanStaticRuntime)
   expectNoStaticLinkDep(t, ctx, libTransitive, libAsanStaticRuntime)

   expectStaticLinkDep(t, ctx, binWithAsan, libAsanStaticCxxRuntime)
   expectNoStaticLinkDep(t, ctx, binNoAsan, libAsanStaticCxxRuntime)
   expectStaticLinkDep(t, ctx, libAsan, libAsanStaticCxxRuntime)
   expectNoStaticLinkDep(t, ctx, libShared, libAsanStaticCxxRuntime)
   expectNoStaticLinkDep(t, ctx, libTransitive, libAsanStaticCxxRuntime)
  } else {
   expectNoStaticLinkDep(t, ctx, binWithAsan, libAsanStaticRuntime)
   expectNoStaticLinkDep(t, ctx, binNoAsan, libAsanStaticRuntime)
   expectNoStaticLinkDep(t, ctx, libAsan, libAsanStaticRuntime)
   expectNoStaticLinkDep(t, ctx, libShared, libAsanStaticRuntime)
   expectNoStaticLinkDep(t, ctx, libTransitive, libAsanStaticRuntime)

   expectNoStaticLinkDep(t, ctx, binWithAsan, libAsanStaticCxxRuntime)
   expectNoStaticLinkDep(t, ctx, binNoAsan, libAsanStaticCxxRuntime)
   expectNoStaticLinkDep(t, ctx, libAsan, libAsanStaticCxxRuntime)
   expectNoStaticLinkDep(t, ctx, libShared, libAsanStaticCxxRuntime)
   expectNoStaticLinkDep(t, ctx, libTransitive, libAsanStaticCxxRuntime)
  }
 }

 t.Run("host", func(t *testing.T) { check(t, buildOS, RUNTIME_LINKAGE_NONE, preparer) })
 t.Run("device", func(t *testing.T) { check(t, "android_arm64_armv8-a", RUNTIME_LINKAGE_SHARED, preparer) })
 t.Run("host musl", func(t *testing.T) {
  check(t, "linux_musl_x86_64", RUNTIME_LINKAGE_STATIC,
   android.GroupFixturePreparers(preparer, PrepareForTestWithHostMusl))
 })
}

func TestTsan(t *testing.T) {
 t.Parallel()
 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,
 }
`

 preparer := android.GroupFixturePreparers(
  prepareForCcTest,
  prepareForTsanTest,
 )
 buildOS := preparer.RunTestWithBp(t, bp).Config.BuildOSTarget.String()

 check := func(t *testing.T, variant string, preparer android.FixturePreparer) {
  result := preparer.RunTestWithBp(t, bp)
  ctx := result.TestContext
  tsanVariant := variant + "_tsan"
  sharedVariant := variant + "_shared"
  sharedTsanVariant := sharedVariant + "_tsan"

  // The binaries, one with tsan and one without
  binWithTsan := result.ModuleForTests(t, "bin_with_tsan", tsanVariant)
  binNoTsan := result.ModuleForTests(t, "bin_no_tsan", variant)

  // Shared libraries that don't request tsan
  libShared := result.ModuleForTests(t, "libshared", sharedVariant)
  libTransitive := result.ModuleForTests(t, "libtransitive", sharedVariant)

  // Shared library that requests tsan
  libTsan := result.ModuleForTests(t, "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, buildOS, preparer) })
 t.Run("device", func(t *testing.T) { check(t, "android_arm64_armv8-a", preparer) })
 t.Run("host musl", func(t *testing.T) {
  check(t, "linux_musl_x86_64", android.GroupFixturePreparers(preparer, PrepareForTestWithHostMusl))
 })
}

func TestMiscUndefined(t *testing.T) {
 if runtime.GOOS != "linux" {
  t.Skip("requires linux")
 }

 t.Parallel()
 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"],
 }
`

 preparer := android.GroupFixturePreparers(
  prepareForCcTest,
 )
 buildOS := preparer.RunTestWithBp(t, bp).Config.BuildOSTarget.String()

 check := func(t *testing.T, variant string, preparer android.FixturePreparer) {
  result := preparer.RunTestWithBp(t, bp)
  ctx := result.TestContext
  staticVariant := variant + "_static"

  // The binaries, one with ubsan and one without
  binWithUbsan := result.ModuleForTests(t, "bin_with_ubsan", variant)
  binNoUbsan := result.ModuleForTests(t, "bin_no_ubsan", variant)

  // Static libraries that don't request ubsan
  libStatic := result.ModuleForTests(t, "libstatic", staticVariant)
  libTransitive := result.ModuleForTests(t, "libtransitive", staticVariant)

  libUbsan := result.ModuleForTests(t, "libubsan", staticVariant)

  libUbsanMinimal := result.ModuleForTests(t, "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, buildOS, preparer) })
 t.Run("device", func(t *testing.T) { check(t, "android_arm64_armv8-a", preparer) })
 t.Run("host musl", func(t *testing.T) {
  check(t, "linux_musl_x86_64", android.GroupFixturePreparers(preparer, PrepareForTestWithHostMusl))
 })
}

func TestFuzz(t *testing.T) {
 t.Parallel()
 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(t, "bin_with_fuzzer", fuzzerVariant)
  binNoFuzzer := result.ModuleForTests(t, "bin_no_fuzzer", variant)

  // Shared libraries that don't request fuzzer
  libShared := result.ModuleForTests(t, "libshared", sharedVariant)
  libTransitive := result.ModuleForTests(t, "libtransitive", sharedVariant)

  // Shared libraries that don't request fuzzer
  libSharedFuzzer := result.ModuleForTests(t, "libshared", sharedFuzzerVariant)
  libTransitiveFuzzer := result.ModuleForTests(t, "libtransitive", sharedFuzzerVariant)

  // Shared library that requests fuzzer
  libFuzzer := result.ModuleForTests(t, "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(t, "libstatic", staticFuzzerVariant)
  libStaticNoFuzzerVariant := result.ModuleForTests(t, "libstatic", staticVariant)

  // Static library that never uses fuzzer.
  libNoFuzzer := result.ModuleForTests(t, "libnofuzzer", staticVariant)

  // Static library that specifies fuzzer
  libStaticFuzzer := result.ModuleForTests(t, "libstatic_fuzzer", staticFuzzerVariant)
  libStaticFuzzerNoFuzzerVariant := result.ModuleForTests(t, "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) {
 t.Parallel()
 if runtime.GOOS != "linux" {
  t.Skip("requires linux")
 }

 bp := `
  cc_binary {
   name: "bin_with_ubsan",
   host_supported: true,
   shared_libs: [
    "libshared",
   ],
   static_libs: [
    "libstatic",
    "libnoubsan",
   ],
   sanitize: {
    undefined: true,
   }
  }

  cc_binary {
   name: "bin_depends_ubsan_static",
   host_supported: true,
   shared_libs: [
    "libshared",
   ],
   static_libs: [
    "libstatic",
    "libubsan",
    "libnoubsan",
   ],
  }

  cc_binary {
   name: "bin_depends_ubsan_shared",
   host_supported: true,
   shared_libs: [
    "libsharedubsan",
   ],
  }

  cc_binary {
   name: "bin_no_ubsan",
   host_supported: true,
   shared_libs: [
    "libshared",
   ],
   static_libs: [
    "libstatic",
    "libnoubsan",
   ],
  }

  cc_binary {
   name: "static_bin_with_ubsan_dep",
   static_executable: true,
   host_supported: true,
   static_libs: [
    "libubsan_diag",
   ],
  }

  cc_library_shared {
   name: "libshared",
   host_supported: true,
   shared_libs: ["libtransitive"],
  }

  cc_library_shared {
   name: "libtransitive",
   host_supported: true,
  }

  cc_library_shared {
   name: "libsharedubsan",
   host_supported: true,
   sanitize: {
    undefined: true,
   }
  }

  cc_library_static {
   name: "libubsan",
   host_supported: true,
   sanitize: {
    undefined: true,
   }
  }

  cc_library_static {
   name: "libubsan_diag",
   host_supported: true,
   sanitize: {
    undefined: true,
    diag: {
     undefined: true,
    },
   },
  }

  cc_library_static {
   name: "libstatic",
   host_supported: true,
  }

  cc_library_static {
   name: "libnoubsan",
   host_supported: true,
  }
 `

 preparer := android.GroupFixturePreparers(
  prepareForCcTest,
 )
 buildOS := preparer.RunTestWithBp(t, bp).Config.BuildOSTarget.String()

 check := func(t *testing.T, variant string, preparer android.FixturePreparer) {
  result := preparer.RunTestWithBp(t, bp)
  staticVariant := variant + "_static"
  sharedVariant := variant + "_shared"

  minimalRuntime := result.ModuleForTests(t, "libclang_rt.ubsan_minimal", staticVariant)
  standaloneRuntime := result.ModuleForTests(t, "libclang_rt.ubsan_standalone.static", staticVariant)

  // The binaries, one with ubsan and one without
  binWithUbsan := result.ModuleForTests(t, "bin_with_ubsan", variant)
  binDependsUbsan := result.ModuleForTests(t, "bin_depends_ubsan_static", variant)
  libSharedUbsan := result.ModuleForTests(t, "libsharedubsan", sharedVariant)
  binDependsUbsanShared := result.ModuleForTests(t, "bin_depends_ubsan_shared", variant)
  binNoUbsan := result.ModuleForTests(t, "bin_no_ubsan", variant)
  staticBin := result.ModuleForTests(t, "static_bin_with_ubsan_dep", variant)

  android.AssertStringListContains(t, "missing libclang_rt.ubsan_minimal in bin_with_ubsan static libs",
   strings.Split(binWithUbsan.Rule("ld").Args["libFlags"], " "),
   minimalRuntime.OutputFiles(result.TestContext, t, "")[0].String())

  android.AssertStringListContains(t, "missing libclang_rt.ubsan_minimal in bin_depends_ubsan_static static libs",
   strings.Split(binDependsUbsan.Rule("ld").Args["libFlags"], " "),
   minimalRuntime.OutputFiles(result.TestContext, t, "")[0].String())

  android.AssertStringListContains(t, "missing libclang_rt.ubsan_minimal in libsharedubsan static libs",
   strings.Split(libSharedUbsan.Rule("ld").Args["libFlags"], " "),
   minimalRuntime.OutputFiles(result.TestContext, t, "")[0].String())

  android.AssertStringListDoesNotContain(t, "unexpected libclang_rt.ubsan_minimal in bin_depends_ubsan_shared static libs",
   strings.Split(binDependsUbsanShared.Rule("ld").Args["libFlags"], " "),
   minimalRuntime.OutputFiles(result.TestContext, t, "")[0].String())

  android.AssertStringListDoesNotContain(t, "unexpected libclang_rt.ubsan_minimal in bin_no_ubsan static libs",
   strings.Split(binNoUbsan.Rule("ld").Args["libFlags"], " "),
   minimalRuntime.OutputFiles(result.TestContext, t, "")[0].String())

  android.AssertStringListContains(t, "missing -Wl,--exclude-libs for minimal runtime in bin_with_ubsan",
   strings.Split(binWithUbsan.Rule("ld").Args["ldFlags"], " "),
   "-Wl,--exclude-libs="+minimalRuntime.OutputFiles(result.TestContext, t, "")[0].Base())

  android.AssertStringListContains(t, "missing -Wl,--exclude-libs for minimal runtime in bin_depends_ubsan_static static libs",
   strings.Split(binDependsUbsan.Rule("ld").Args["ldFlags"], " "),
   "-Wl,--exclude-libs="+minimalRuntime.OutputFiles(result.TestContext, t, "")[0].Base())

  android.AssertStringListContains(t, "missing -Wl,--exclude-libs for minimal runtime in libsharedubsan static libs",
   strings.Split(libSharedUbsan.Rule("ld").Args["ldFlags"], " "),
   "-Wl,--exclude-libs="+minimalRuntime.OutputFiles(result.TestContext, t, "")[0].Base())

  android.AssertStringListDoesNotContain(t, "unexpected -Wl,--exclude-libs for minimal runtime in bin_depends_ubsan_shared static libs",
   strings.Split(binDependsUbsanShared.Rule("ld").Args["ldFlags"], " "),
   "-Wl,--exclude-libs="+minimalRuntime.OutputFiles(result.TestContext, t, "")[0].Base())

  android.AssertStringListDoesNotContain(t, "unexpected -Wl,--exclude-libs for minimal runtime in bin_no_ubsan static libs",
   strings.Split(binNoUbsan.Rule("ld").Args["ldFlags"], " "),
   "-Wl,--exclude-libs="+minimalRuntime.OutputFiles(result.TestContext, t, "")[0].Base())

  android.AssertStringListContains(t, "missing libclang_rt.ubsan_standalone.static in static_bin_with_ubsan_dep static libs",
   strings.Split(staticBin.Rule("ld").Args["libFlags"], " "),
   standaloneRuntime.OutputFiles(result.TestContext, t, "")[0].String())

 }

 t.Run("host", func(t *testing.T) { check(t, buildOS, preparer) })
 t.Run("device", func(t *testing.T) { check(t, "android_arm64_armv8-a", preparer) })
 t.Run("host musl", func(t *testing.T) {
  check(t, "linux_musl_x86_64", android.GroupFixturePreparers(preparer, PrepareForTestWithHostMusl))
 })
}

type MemtagNoteType int

const (
 None MemtagNoteType = iota + 1
 Sync
 Async
)

func (t MemtagNoteType) str() string {
 switch t {
 case None:
  return "none"
 case Sync:
  return "sync"
 case Async:
  return "async"
 default:
  panic("type_note_invalid")
 }
}

func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) {
 t.Helper()

 found := None
 ldFlags := m.Rule("ld").Args["ldFlags"]
 if strings.Contains(ldFlags, "-fsanitize-memtag-mode=async") {
  found = Async
 } else if strings.Contains(ldFlags, "-fsanitize-memtag-mode=sync") {
  found = Sync
 }

 if found != expected {
  t.Errorf("Wrong Memtag note in target %q: found %q, expected %q", m.Module().(*Module).Name(), found.str(), expected.str())
 }
}

var prepareForTestWithMemtagHeap = android.GroupFixturePreparers(
 android.FixtureModifyMockFS(func(fs android.MockFS) {
  templateBp := `
  cc_test {
   name: "unset_test_%[1]s",
   gtest: false,
  }

  cc_test {
   name: "no_memtag_test_%[1]s",
   gtest: false,
   sanitize: { memtag_heap: false },
  }

  cc_test {
   name: "set_memtag_test_%[1]s",
   gtest: false,
   sanitize: { memtag_heap: true },
  }

  cc_test {
   name: "set_memtag_set_async_test_%[1]s",
   gtest: false,
   sanitize: { memtag_heap: true, diag: { memtag_heap: false }  },
  }

  cc_test {
   name: "set_memtag_set_sync_test_%[1]s",
   gtest: false,
   sanitize: { memtag_heap: true, diag: { memtag_heap: true }  },
  }

  cc_test {
   name: "unset_memtag_set_sync_test_%[1]s",
   gtest: false,
   sanitize: { diag: { memtag_heap: true }  },
  }

  cc_binary {
   name: "unset_binary_%[1]s",
  }

  cc_binary {
   name: "no_memtag_binary_%[1]s",
   sanitize: { memtag_heap: false },
  }

  cc_binary {
   name: "set_memtag_binary_%[1]s",
   sanitize: { memtag_heap: true },
  }

  cc_binary {
   name: "set_memtag_set_async_binary_%[1]s",
   sanitize: { memtag_heap: true, diag: { memtag_heap: false }  },
  }

  cc_binary {
   name: "set_memtag_set_sync_binary_%[1]s",
   sanitize: { memtag_heap: true, diag: { memtag_heap: true }  },
  }

  cc_binary {
   name: "unset_memtag_set_sync_binary_%[1]s",
   sanitize: { diag: { memtag_heap: true }  },
  }
  `
  subdirNoOverrideBp := fmt.Sprintf(templateBp, "no_override")
  subdirOverrideDefaultDisableBp := fmt.Sprintf(templateBp, "override_default_disable")
  subdirSyncBp := fmt.Sprintf(templateBp, "override_default_sync")
  subdirAsyncBp := fmt.Sprintf(templateBp, "override_default_async")

  fs.Merge(android.MockFS{
   "subdir_no_override/Android.bp":              []byte(subdirNoOverrideBp),
   "subdir_override_default_disable/Android.bp": []byte(subdirOverrideDefaultDisableBp),
   "subdir_sync/Android.bp":                     []byte(subdirSyncBp),
   "subdir_async/Android.bp":                    []byte(subdirAsyncBp),
  })
 }),
 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
  variables.MemtagHeapExcludePaths = []string{"subdir_override_default_disable"}
  // "subdir_override_default_disable" is covered by both include and override_default_disable paths. override_default_disable wins.
  variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_override_default_disable"}
  variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_override_default_disable"}
 }),
)

func TestSanitizeMemtagHeap(t *testing.T) {
 t.Parallel()
 variant := "android_arm64_armv8-a"

 result := android.GroupFixturePreparers(
  prepareForCcTest,
  prepareForTestWithMemtagHeap,
 ).RunTest(t)
 ctx := result.TestContext

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_no_override", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_async", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_disable", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_sync", variant), None)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_no_override", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_async", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_disable", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_sync", variant), None)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_no_override", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_async", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_disable", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_no_override", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_async", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_disable", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_sync", variant), Async)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_no_override", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_async", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_disable", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_sync", variant), Async)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_sync", variant), Sync)

 // should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_no_override", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_async", variant), Sync)
 // should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_disable", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_no_override", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_async", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_disable", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_sync", variant), Sync)
}

func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
 t.Parallel()
 variant := "android_arm64_armv8-a"

 result := android.GroupFixturePreparers(
  prepareForCcTest,
  prepareForTestWithMemtagHeap,
  android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
   variables.SanitizeDevice = []string{"memtag_heap"}
  }),
 ).RunTest(t)
 ctx := result.TestContext

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_no_override", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_async", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_disable", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_sync", variant), None)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_no_override", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_async", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_disable", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_sync", variant), None)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_no_override", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_async", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_disable", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_no_override", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_async", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_disable", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_sync", variant), Async)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_no_override", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_async", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_disable", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_sync", variant), Async)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_async", variant), Sync)
 // should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_disable", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_no_override", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_async", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_disable", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_sync", variant), Sync)
}

func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
 t.Parallel()
 variant := "android_arm64_armv8-a"

 result := android.GroupFixturePreparers(
  prepareForCcTest,
  prepareForTestWithMemtagHeap,
  android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
   variables.SanitizeDevice = []string{"memtag_heap"}
   variables.SanitizeDeviceDiag = []string{"memtag_heap"}
  }),
 ).RunTest(t)
 ctx := result.TestContext

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_no_override", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_async", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_disable", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_sync", variant), None)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_no_override", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_async", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_disable", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_sync", variant), None)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_no_override", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_async", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_disable", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_sync", variant), Async)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_no_override", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_async", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_disable", variant), Async)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_sync", variant), Async)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_async", variant), Sync)
 // should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_disable", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_disable", variant), None)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_sync", variant), Sync)

 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_no_override", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_async", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_disable", variant), Sync)
 checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_sync", variant), Sync)
}

func TestCfi(t *testing.T) {
 t.Parallel()

 bp := `
 cc_library_shared {
  name: "shared_with_cfi",
  static_libs: [
   "static_dep_with_cfi",
   "static_dep_no_cfi",
  ],
  sanitize: {
   cfi: true,
  },
 }

 cc_library_shared {
  name: "shared_no_cfi",
  static_libs: [
   "static_dep_with_cfi",
   "static_dep_no_cfi",
  ],
 }

 cc_library_static {
  name: "static_dep_with_cfi",
  sanitize: {
   cfi: true,
  },
 }

 cc_library_static {
  name: "static_dep_no_cfi",
 }

 cc_library_shared {
  name: "shared_rdep_no_cfi",
  static_libs: ["static_dep_with_cfi_2"],
 }

 cc_library_static {
  name: "static_dep_with_cfi_2",
  sanitize: {
   cfi: true,
  },
 }
`
 preparer := android.GroupFixturePreparers(
  prepareForCcTest,
 )
 result := preparer.RunTestWithBp(t, bp)
 ctx := result.TestContext

 buildOs := "android_arm64_armv8-a"
 shared_suffix := "_shared"
 cfi_suffix := "_cfi"
 static_suffix := "_static"

 sharedWithCfiLib := result.ModuleForTests(t, "shared_with_cfi", buildOs+shared_suffix+cfi_suffix)
 sharedNoCfiLib := result.ModuleForTests(t, "shared_no_cfi", buildOs+shared_suffix)
 staticWithCfiLib := result.ModuleForTests(t, "static_dep_with_cfi", buildOs+static_suffix)
 staticWithCfiLibCfiVariant := result.ModuleForTests(t, "static_dep_with_cfi", buildOs+static_suffix+cfi_suffix)
 staticNoCfiLib := result.ModuleForTests(t, "static_dep_no_cfi", buildOs+static_suffix)
 staticNoCfiLibCfiVariant := result.ModuleForTests(t, "static_dep_no_cfi", buildOs+static_suffix+cfi_suffix)
 sharedRdepNoCfi := result.ModuleForTests(t, "shared_rdep_no_cfi", buildOs+shared_suffix)
 staticDepWithCfi2Lib := result.ModuleForTests(t, "static_dep_with_cfi_2", buildOs+static_suffix)

 // Confirm assumptions about propagation of CFI enablement
 expectStaticLinkDep(t, ctx, sharedWithCfiLib, staticWithCfiLibCfiVariant)
 expectStaticLinkDep(t, ctx, sharedNoCfiLib, staticWithCfiLib)
 expectStaticLinkDep(t, ctx, sharedWithCfiLib, staticNoCfiLibCfiVariant)
 expectStaticLinkDep(t, ctx, sharedNoCfiLib, staticNoCfiLib)
 expectStaticLinkDep(t, ctx, sharedRdepNoCfi, staticDepWithCfi2Lib)

 // Confirm that non-CFI variants do not add CFI flags
 bazLibCflags := staticWithCfiLib.Rule("cc").Args["cFlags"]
 if strings.Contains(bazLibCflags, "-fsanitize-cfi-cross-dso") {
  t.Errorf("non-CFI variant of baz not expected to contain CFI flags ")
 }
}

func TestHwasan(t *testing.T) {
 t.Parallel()

 bp := `
 cc_library_shared {
  name: "shared_with_hwaddress",
  static_libs: [
   "static_dep_with_hwaddress",
   "static_dep_no_hwaddress",
  ],
  sanitize: {
   hwaddress: true,
  },
  sdk_version: "current",
   stl: "c++_shared",
   split_all_variants: true,
 }

 cc_library_static {
  name: "static_dep_with_hwaddress",
  sanitize: {
   hwaddress: true,
  },
  sdk_version: "current",
   stl: "c++_shared",
   split_all_variants: true,
 }

 cc_library_static {
  name: "static_dep_no_hwaddress",
  sdk_version: "current",
   stl: "c++_shared",
   split_all_variants: true,
 }
`

 androidArm := "android_arm_armv7-a-neon"
 androidArm64 := "android_arm64_armv8-a"
 androidX86 := "android_x86_silvermont"
 sharedSuffix := "_shared"
 hwasanSuffix := "_hwasan"
 staticSuffix := "_static"
 sdkSuffix := "_sdk"

 sharedWithHwasanVariant := sharedSuffix + hwasanSuffix
 sharedWithSdkVariant := sdkSuffix + sharedSuffix
 staticWithHwasanVariant := staticSuffix + hwasanSuffix
 staticWithSdkVariant := sdkSuffix + staticSuffix

 testCases := []struct {
  buildOs          string
  extraPreparer    android.FixturePreparer
  expectedVariants map[string][]string
 }{
  {
   buildOs: androidArm64,
   expectedVariants: map[string][]string{
    "shared_with_hwaddress": []string{
     androidArm64 + sharedWithHwasanVariant,
     androidArm64 + sharedWithSdkVariant,
     androidArm + sharedSuffix,
     androidArm + sharedWithSdkVariant,
    },
    "static_dep_with_hwaddress": []string{
     androidArm64 + staticSuffix,
     androidArm64 + staticWithHwasanVariant,
     androidArm64 + staticWithSdkVariant,
     androidArm + staticSuffix,
     androidArm + staticWithSdkVariant,
    },
    "static_dep_no_hwaddress": []string{
     androidArm64 + staticSuffix,
     androidArm64 + staticWithHwasanVariant,
     androidArm64 + staticWithSdkVariant,
     androidArm + staticSuffix,
     androidArm + staticWithSdkVariant,
    },
   },
  },
  {
   buildOs: androidX86,
   extraPreparer: android.FixtureModifyConfig(func(config android.Config) {
    config.Targets[android.Android] = []android.Target{
     {
      Os:   android.Android,
      Arch: android.Arch{ArchType: android.X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}},
     },
    }
   }),
   expectedVariants: map[string][]string{
    "shared_with_hwaddress": []string{
     androidX86 + sharedSuffix,
     androidX86 + sharedWithSdkVariant,
    },
    "static_dep_with_hwaddress": []string{
     androidX86 + staticSuffix,
     androidX86 + staticWithSdkVariant,
    },
    "static_dep_no_hwaddress": []string{
     androidX86 + staticSuffix,
     androidX86 + staticWithSdkVariant,
    },
   },
  },
 }

 for _, tc := range testCases {
  preparer := android.GroupFixturePreparers(
   prepareForCcTest,
   android.OptionalFixturePreparer(tc.extraPreparer),
  )
  result := preparer.RunTestWithBp(t, bp)

  for m, v := range tc.expectedVariants {
   variants := result.ModuleVariantsForTests(m)
   if !reflect.DeepEqual(variants, v) {
    t.Errorf("Expected variants of %q to be %q, but got %q", m, v, variants)
   }
  }
 }
}

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