Add Rust fuzzing support.

Add a rust_fuzz module which builds a libfuzzer binary that enabes
asan+sancov. This relies on the libfuzzer-sys crate.

Bug: 147140513
Test: Local rust_fuzz example builds, fuzzes with asan+sancov.
Change-Id: I57db3b8d25869791824ccfab768d13b0bb9d42fa
diff --git a/rust/Android.bp b/rust/Android.bp
index df731db..ad3040a 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -15,6 +15,7 @@
         "clippy.go",
         "compiler.go",
         "coverage.go",
+        "fuzz.go",
         "image.go",
         "library.go",
         "prebuilt.go",
@@ -22,6 +23,7 @@
         "project_json.go",
         "protobuf.go",
         "rust.go",
+        "sanitize.go",
         "strip.go",
         "source_provider.go",
         "test.go",
@@ -34,6 +36,7 @@
         "clippy_test.go",
         "compiler_test.go",
         "coverage_test.go",
+        "fuzz_test.go",
         "image_test.go",
         "library_test.go",
         "project_json_test.go",
diff --git a/rust/builder.go b/rust/builder.go
index baeab69..77d339a 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -86,7 +86,7 @@
 
 func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath, linkDirs []string) buildOutput {
-	flags.RustFlags = append(flags.RustFlags, "-C lto")
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
 
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", linkDirs)
 }
@@ -103,13 +103,13 @@
 
 func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath, linkDirs []string) buildOutput {
-	flags.RustFlags = append(flags.RustFlags, "-C lto")
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", linkDirs)
 }
 
 func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath, linkDirs []string) buildOutput {
-	flags.RustFlags = append(flags.RustFlags, "-C lto")
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", linkDirs)
 }
 
diff --git a/rust/compiler.go b/rust/compiler.go
index bcea6cc..2d9575c 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -146,6 +146,7 @@
 	relative string
 	path     android.InstallPath
 	location installLocation
+	sanitize *sanitize
 
 	coverageOutputZipFile android.OptionalPath
 	distFile              android.OptionalPath
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index 21b22a4..5066428 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -26,8 +26,8 @@
 	Arm64LinkFlags            = []string{}
 
 	Arm64ArchVariantRustFlags = map[string][]string{
-		"armv8-a":  []string{},
-		"armv8-2a": []string{},
+		"armv8-a":          []string{},
+		"armv8-2a":         []string{},
 		"armv8-2a-dotprod": []string{},
 	}
 )
@@ -71,6 +71,10 @@
 	return true
 }
 
+func (toolchainArm64) LibclangRuntimeLibraryArch() string {
+	return "aarch64"
+}
+
 func Arm64ToolchainFactory(arch android.Arch) Toolchain {
 	archVariant := arch.ArchVariant
 	if archVariant == "" {
diff --git a/rust/config/arm_device.go b/rust/config/arm_device.go
index adfe917..42c1c02 100644
--- a/rust/config/arm_device.go
+++ b/rust/config/arm_device.go
@@ -72,6 +72,10 @@
 	return true
 }
 
+func (toolchainArm) LibclangRuntimeLibraryArch() string {
+	return "arm"
+}
+
 func ArmToolchainFactory(arch android.Arch) Toolchain {
 	toolchainRustFlags := []string{
 		"${config.ArmToolchainRustFlags}",
diff --git a/rust/config/toolchain.go b/rust/config/toolchain.go
index 616d88b..9525c38 100644
--- a/rust/config/toolchain.go
+++ b/rust/config/toolchain.go
@@ -34,6 +34,8 @@
 	Supported() bool
 
 	Bionic() bool
+
+	LibclangRuntimeLibraryArch() string
 }
 
 type toolchainBase struct {
@@ -106,6 +108,36 @@
 	return false
 }
 
+func (toolchainBase) LibclangRuntimeLibraryArch() string {
+	return ""
+}
+
+func LibFuzzerRuntimeLibrary(t Toolchain) string {
+	return LibclangRuntimeLibrary(t, "fuzzer")
+}
+
+func LibclangRuntimeLibrary(t Toolchain, library string) string {
+	arch := t.LibclangRuntimeLibraryArch()
+	if arch == "" {
+		return ""
+	}
+	if !t.Bionic() {
+		return "libclang_rt." + library + "-" + arch
+	}
+	return "libclang_rt." + library + "-" + arch + "-android"
+}
+
+func LibRustRuntimeLibrary(t Toolchain, library string) string {
+	arch := t.LibclangRuntimeLibraryArch()
+	if arch == "" {
+		return ""
+	}
+	if !t.Bionic() {
+		return "librustc_rt." + library + "-" + arch
+	}
+	return "librustc_rt." + library + "-" + arch + "-android"
+}
+
 func toolchainBaseFactory() Toolchain {
 	return &toolchainBase{}
 }
diff --git a/rust/config/x86_64_device.go b/rust/config/x86_64_device.go
index 5f6e85a..94b719f 100644
--- a/rust/config/x86_64_device.go
+++ b/rust/config/x86_64_device.go
@@ -77,6 +77,10 @@
 	return true
 }
 
+func (toolchainX86_64) LibclangRuntimeLibraryArch() string {
+	return "x86_64"
+}
+
 func x86_64ToolchainFactory(arch android.Arch) Toolchain {
 	toolchainRustFlags := []string{
 		"${config.X86_64ToolchainRustFlags}",
diff --git a/rust/config/x86_device.go b/rust/config/x86_device.go
index daeeb14..aae1125 100644
--- a/rust/config/x86_device.go
+++ b/rust/config/x86_device.go
@@ -80,6 +80,10 @@
 	return true
 }
 
+func (toolchainX86) LibclangRuntimeLibraryArch() string {
+	return "i686"
+}
+
 func x86ToolchainFactory(arch android.Arch) Toolchain {
 	toolchainRustFlags := []string{
 		"${config.X86ToolchainRustFlags}",
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index e7f26ce..b63e14d 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -103,6 +103,14 @@
 	return "x86"
 }
 
+func (toolchainLinuxX86) LibclangRuntimeLibraryArch() string {
+	return "i386"
+}
+
+func (toolchainLinuxX8664) LibclangRuntimeLibraryArch() string {
+	return "x86_64"
+}
+
 func (t *toolchainLinuxX86) RustTriple() string {
 	return "i686-unknown-linux-gnu"
 }
diff --git a/rust/fuzz.go b/rust/fuzz.go
new file mode 100644
index 0000000..da8f209
--- /dev/null
+++ b/rust/fuzz.go
@@ -0,0 +1,96 @@
+// Copyright 2020 The Android Open Source Project
+//
+// 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 rust
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+	"android/soong/rust/config"
+)
+
+func init() {
+	android.RegisterModuleType("rust_fuzz", RustFuzzFactory)
+}
+
+type fuzzDecorator struct {
+	*binaryDecorator
+
+	Properties            cc.FuzzProperties
+	dictionary            android.Path
+	corpus                android.Paths
+	corpusIntermediateDir android.Path
+	config                android.Path
+	data                  android.Paths
+	dataIntermediateDir   android.Path
+}
+
+var _ compiler = (*binaryDecorator)(nil)
+
+// rust_binary produces a binary that is runnable on a device.
+func RustFuzzFactory() android.Module {
+	module, _ := NewRustFuzz(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+func NewRustFuzz(hod android.HostOrDeviceSupported) (*Module, *fuzzDecorator) {
+	module, binary := NewRustBinary(hod)
+	fuzz := &fuzzDecorator{
+		binaryDecorator: binary,
+	}
+
+	// Change the defaults for the binaryDecorator's baseCompiler
+	fuzz.binaryDecorator.baseCompiler.dir = "fuzz"
+	fuzz.binaryDecorator.baseCompiler.dir64 = "fuzz"
+	fuzz.binaryDecorator.baseCompiler.location = InstallInData
+	module.sanitize.SetSanitizer(cc.Fuzzer, true)
+	module.compiler = fuzz
+	return module, fuzz
+}
+
+func (fuzzer *fuzzDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = fuzzer.binaryDecorator.compilerFlags(ctx, flags)
+
+	// `../lib` for installed fuzz targets (both host and device), and `./lib` for fuzz target packages.
+	flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
+	flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
+
+	return flags
+}
+
+func (fuzzer *fuzzDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+	deps.StaticLibs = append(deps.StaticLibs,
+		config.LibFuzzerRuntimeLibrary(ctx.toolchain()))
+	deps.SharedLibs = append(deps.SharedLibs,
+		config.LibclangRuntimeLibrary(ctx.toolchain(), "asan"))
+	deps.SharedLibs = append(deps.SharedLibs, "libc++")
+	deps.Rlibs = append(deps.Rlibs, "liblibfuzzer_sys")
+
+	deps = fuzzer.binaryDecorator.compilerDeps(ctx, deps)
+
+	return deps
+}
+
+func (fuzzer *fuzzDecorator) compilerProps() []interface{} {
+	return append(fuzzer.binaryDecorator.compilerProps(),
+		&fuzzer.Properties)
+}
+
+func (fuzzer *fuzzDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+	return RlibLinkage
+}
+
+func (fuzzer *fuzzDecorator) autoDep(ctx BaseModuleContext) autoDep {
+	return rlibAutoDep
+}
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
new file mode 100644
index 0000000..f93ccc7
--- /dev/null
+++ b/rust/fuzz_test.go
@@ -0,0 +1,66 @@
+// Copyright 2020 The Android Open Source Project
+//
+// 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 rust
+
+import (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestRustFuzz(t *testing.T) {
+	ctx := testRust(t, `
+			rust_library {
+				name: "libtest_fuzzing",
+				crate_name: "test_fuzzing",
+				srcs: ["foo.rs"],
+			}
+			rust_fuzz {
+				name: "fuzz_libtest",
+				srcs: ["foo.rs"],
+				rustlibs: ["libtest_fuzzing"],
+			}
+	`)
+
+	// Check that appropriate dependencies are added and that the rustlib linkage is correct.
+	fuzz_libtest_mod := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Module().(*Module)
+	if !android.InList("libclang_rt.asan-aarch64-android", fuzz_libtest_mod.Properties.AndroidMkSharedLibs) {
+		t.Errorf("libclang_rt.asan-aarch64-android shared library dependency missing for rust_fuzz module.")
+	}
+	if !android.InList("liblibfuzzer_sys.rlib-std", fuzz_libtest_mod.Properties.AndroidMkRlibs) {
+		t.Errorf("liblibfuzzer_sys rlib library dependency missing for rust_fuzz module. %#v", fuzz_libtest_mod.Properties.AndroidMkRlibs)
+	}
+	if !android.InList("libtest_fuzzing.rlib-std", fuzz_libtest_mod.Properties.AndroidMkRlibs) {
+		t.Errorf("rustlibs not linked as rlib for rust_fuzz module.")
+	}
+
+	// Check that compiler flags are set appropriately .
+	fuzz_libtest := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Output("fuzz_libtest")
+	if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-Z sanitizer=address") ||
+		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov'") ||
+		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "--cfg fuzzing") {
+		t.Errorf("rust_fuzz module does not contain the expected flags (sancov, cfg fuzzing, address sanitizer).")
+
+	}
+
+	// Check that dependencies have 'fuzzer' variants produced for them as well.
+	libtest_fuzzer := ctx.ModuleForTests("libtest_fuzzing", "android_arm64_armv8-a_rlib_rlib-std_fuzzer").Output("libtest_fuzzing.rlib")
+	if !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-Z sanitizer=address") ||
+		!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov'") ||
+		!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "--cfg fuzzing") {
+		t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov, cfg fuzzing, address sanitizer).")
+	}
+}
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 0c6ec99..f753e7f 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -51,6 +51,8 @@
 		flagExporter: NewFlagExporter(),
 	}
 
+	// Don't sanitize procMacros
+	module.sanitize = nil
 	module.compiler = procMacro
 
 	return module, procMacro
diff --git a/rust/rust.go b/rust/rust.go
index 83add87..cda01d8 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -42,6 +42,10 @@
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
 		ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel()
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
+
+	})
+	android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
 	})
 	pctx.Import("android/soong/rust/config")
 	pctx.ImportAs("cc_config", "android/soong/cc/config")
@@ -97,6 +101,7 @@
 	compiler         compiler
 	coverage         *coverage
 	clippy           *clippy
+	sanitize         *sanitize
 	cachedToolchain  config.Toolchain
 	sourceProvider   SourceProvider
 	subAndroidMkOnce map[SubAndroidMkProvider]bool
@@ -125,7 +130,9 @@
 }
 
 func (mod *Module) SanitizePropDefined() bool {
-	return false
+	// Because compiler is not set for some Rust modules where sanitize might be set, check that compiler is also not
+	// nil since we need compiler to actually sanitize.
+	return mod.sanitize != nil && mod.compiler != nil
 }
 
 func (mod *Module) IsDependencyRoot() bool {
@@ -420,6 +427,7 @@
 		&cc.CoverageProperties{},
 		&cc.RustBindgenClangProperties{},
 		&ClippyProperties{},
+		&SanitizeProperties{},
 	)
 
 	android.InitDefaultsModule(module)
@@ -548,6 +556,9 @@
 	if mod.sourceProvider != nil {
 		mod.AddProperties(mod.sourceProvider.SourceProviderProps()...)
 	}
+	if mod.sanitize != nil {
+		mod.AddProperties(mod.sanitize.props()...)
+	}
 
 	android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
 	android.InitApexModule(mod)
@@ -566,6 +577,7 @@
 	module := newBaseModule(hod, multilib)
 	module.coverage = &coverage{}
 	module.clippy = &clippy{}
+	module.sanitize = &sanitize{}
 	return module
 }
 
@@ -680,6 +692,9 @@
 	if mod.clippy != nil {
 		flags, deps = mod.clippy.flags(ctx, flags, deps)
 	}
+	if mod.sanitize != nil {
+		flags, deps = mod.sanitize.flags(ctx, flags, deps)
+	}
 
 	// SourceProvider needs to call GenerateSource() before compiler calls
 	// compile() so it can provide the source. A SourceProvider has
@@ -723,6 +738,10 @@
 		deps = mod.coverage.deps(ctx, deps)
 	}
 
+	if mod.sanitize != nil {
+		deps = mod.sanitize.deps(ctx, deps)
+	}
+
 	deps.Rlibs = android.LastUniqueStrings(deps.Rlibs)
 	deps.Dylibs = android.LastUniqueStrings(deps.Dylibs)
 	deps.Rustlibs = android.LastUniqueStrings(deps.Rustlibs)
@@ -783,6 +802,9 @@
 	if mod.coverage != nil {
 		mod.coverage.begin(ctx)
 	}
+	if mod.sanitize != nil {
+		mod.sanitize.begin(ctx)
+	}
 }
 
 func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
diff --git a/rust/sanitize.go b/rust/sanitize.go
new file mode 100644
index 0000000..67460ba
--- /dev/null
+++ b/rust/sanitize.go
@@ -0,0 +1,258 @@
+// Copyright 2020 The Android Open Source Project
+//
+// 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 rust
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+	"android/soong/rust/config"
+	"fmt"
+	"github.com/google/blueprint"
+)
+
+type SanitizeProperties struct {
+	// enable AddressSanitizer, ThreadSanitizer, or UndefinedBehaviorSanitizer
+	Sanitize struct {
+		Address *bool `android:"arch_variant"`
+		Fuzzer  *bool `android:"arch_variant"`
+		Never   *bool `android:"arch_variant"`
+	}
+	SanitizerEnabled bool `blueprint:"mutated"`
+	SanitizeDep      bool `blueprint:"mutated"`
+
+	// Used when we need to place libraries in their own directory, such as ASAN.
+	InSanitizerDir bool `blueprint:"mutated"`
+}
+
+var fuzzerFlags = []string{
+	"-C passes='sancov'",
+
+	"--cfg fuzzing",
+	"-C llvm-args=-sanitizer-coverage-level=4",
+	"-C llvm-args=-sanitizer-coverage-trace-compares",
+	"-C llvm-args=-sanitizer-coverage-inline-8bit-counters",
+	"-C llvm-args=-sanitizer-coverage-trace-geps",
+	"-C llvm-args=-sanitizer-coverage-prune-blocks=0",
+	"-C llvm-args=-sanitizer-coverage-pc-table",
+	"-C link-dead-code=y",
+	"-Z sanitizer=address",
+
+	// Sancov breaks with lto
+	// TODO: Remove when https://bugs.llvm.org/show_bug.cgi?id=41734 is resolved and sancov works with LTO
+	"-C lto=no",
+}
+
+var asanFlags = []string{
+	"-Z sanitizer=address",
+}
+
+func boolPtr(v bool) *bool {
+	if v {
+		return &v
+	} else {
+		return nil
+	}
+}
+
+func init() {
+}
+func (sanitize *sanitize) props() []interface{} {
+	return []interface{}{&sanitize.Properties}
+}
+
+func (sanitize *sanitize) begin(ctx BaseModuleContext) {
+	s := sanitize.Properties.Sanitize
+
+	// TODO:(b/178369775)
+	// For now sanitizing is only supported on devices
+	if ctx.Os() == android.Android && Bool(s.Fuzzer) {
+		sanitize.Properties.SanitizerEnabled = true
+	}
+
+	if ctx.Os() == android.Android && Bool(s.Address) {
+		sanitize.Properties.SanitizerEnabled = true
+	}
+}
+
+type sanitize struct {
+	Properties SanitizeProperties
+}
+
+func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
+	if !sanitize.Properties.SanitizerEnabled {
+		return flags, deps
+	}
+	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
+		flags.RustFlags = append(flags.RustFlags, fuzzerFlags...)
+	}
+	if Bool(sanitize.Properties.Sanitize.Address) {
+		flags.RustFlags = append(flags.RustFlags, asanFlags...)
+	}
+	return flags, deps
+}
+
+func (sanitize *sanitize) deps(ctx BaseModuleContext, deps Deps) Deps {
+	return deps
+}
+
+func rustSanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) {
+	if mod, ok := mctx.Module().(*Module); ok && mod.sanitize != nil {
+		if !mod.Enabled() {
+			return
+		}
+		if Bool(mod.sanitize.Properties.Sanitize.Fuzzer) || Bool(mod.sanitize.Properties.Sanitize.Address) {
+			mctx.AddFarVariationDependencies(append(mctx.Target().Variations(), []blueprint.Variation{
+				{Mutator: "link", Variation: "shared"},
+			}...), cc.SharedDepTag(), config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan"))
+		}
+	}
+}
+
+func (sanitize *sanitize) SetSanitizer(t cc.SanitizerType, b bool) {
+	sanitizerSet := false
+	switch t {
+	case cc.Fuzzer:
+		sanitize.Properties.Sanitize.Fuzzer = boolPtr(b)
+		sanitizerSet = true
+	case cc.Asan:
+		sanitize.Properties.Sanitize.Address = boolPtr(b)
+		sanitizerSet = true
+	default:
+		panic(fmt.Errorf("setting unsupported sanitizerType %d", t))
+	}
+	if b && sanitizerSet {
+		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 cc.SanitizerType) bool {
+	if sanitize == nil {
+		return false
+	}
+	if Bool(sanitize.Properties.Sanitize.Never) {
+		return true
+	}
+	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 (sanitize *sanitize) isSanitizerEnabled(t cc.SanitizerType) bool {
+	if sanitize == nil || !sanitize.Properties.SanitizerEnabled {
+		return false
+	}
+
+	sanitizerVal := sanitize.getSanitizerBoolPtr(t)
+	return sanitizerVal != nil && *sanitizerVal == true
+}
+
+func (sanitize *sanitize) getSanitizerBoolPtr(t cc.SanitizerType) *bool {
+	switch t {
+	case cc.Fuzzer:
+		return sanitize.Properties.Sanitize.Fuzzer
+	case cc.Asan:
+		return sanitize.Properties.Sanitize.Address
+	default:
+		return nil
+	}
+}
+
+func (mod *Module) SanitizerSupported(t cc.SanitizerType) bool {
+	if mod.Host() {
+		return false
+	}
+	switch t {
+	case cc.Fuzzer:
+		return true
+	case cc.Asan:
+		return true
+	default:
+		return false
+	}
+}
+
+func (mod *Module) IsSanitizerEnabled(t cc.SanitizerType) bool {
+	return mod.sanitize.isSanitizerEnabled(t)
+}
+
+func (mod *Module) IsSanitizerExplicitlyDisabled(t cc.SanitizerType) bool {
+	if mod.Host() {
+		return true
+	}
+
+	// TODO(b/178365482): Rust/CC interop doesn't work just yet; don't sanitize rust_ffi modules until
+	// linkage issues are resolved.
+	if lib, ok := mod.compiler.(libraryInterface); ok {
+		if lib.shared() || lib.static() {
+			return true
+		}
+	}
+
+	return mod.sanitize.isSanitizerExplicitlyDisabled(t)
+}
+
+func (mod *Module) SanitizeDep() bool {
+	return mod.sanitize.Properties.SanitizeDep
+}
+
+func (mod *Module) SetSanitizer(t cc.SanitizerType, b bool) {
+	if !Bool(mod.sanitize.Properties.Sanitize.Never) {
+		mod.sanitize.SetSanitizer(t, b)
+	}
+}
+
+func (mod *Module) SetSanitizeDep(b bool) {
+	mod.sanitize.Properties.SanitizeDep = b
+}
+
+func (mod *Module) StaticallyLinked() bool {
+	if lib, ok := mod.compiler.(libraryInterface); ok {
+		if lib.rlib() || lib.static() {
+			return true
+		}
+	} else if Bool(mod.compiler.(*binaryDecorator).Properties.Static_executable) {
+		return true
+	}
+	return false
+}
+
+func (mod *Module) SetInSanitizerDir() {
+	mod.sanitize.Properties.InSanitizerDir = true
+}
+
+func (mod *Module) SanitizeNever() bool {
+	return Bool(mod.sanitize.Properties.Sanitize.Never)
+}
+
+var _ cc.PlatformSanitizeable = (*Module)(nil)
+
+func IsSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
+	switch t := tag.(type) {
+	case dependencyTag:
+		return t.library
+	default:
+		return cc.IsSanitizableDependencyTag(tag)
+	}
+}
+
+func (m *Module) SanitizableDepTagChecker() cc.SantizableDependencyTagChecker {
+	return IsSanitizableDependencyTag
+}
diff --git a/rust/testing.go b/rust/testing.go
index 07f557a..bb511b6 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -87,6 +87,13 @@
 			system_shared_libs: [],
 			export_include_dirs: ["libprotobuf-cpp-full-includes"],
 		}
+		cc_library {
+			name: "libclang_rt.asan-aarch64-android",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			export_include_dirs: ["libprotobuf-cpp-full-includes"],
+		}
 		rust_library {
 			name: "libstd",
 			crate_name: "std",
@@ -129,7 +136,12 @@
 			srcs: ["foo.rs"],
 			host_supported: true,
 		}
-
+		rust_library {
+			name: "liblibfuzzer_sys",
+			crate_name: "libfuzzer_sys",
+			srcs:["foo.rs"],
+			host_supported: true,
+		}
 `
 	return bp
 }
@@ -147,6 +159,7 @@
 	ctx.RegisterModuleType("rust_library_host", RustLibraryHostFactory)
 	ctx.RegisterModuleType("rust_library_host_dylib", RustLibraryDylibHostFactory)
 	ctx.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory)
+	ctx.RegisterModuleType("rust_fuzz", RustFuzzFactory)
 	ctx.RegisterModuleType("rust_ffi", RustFFIFactory)
 	ctx.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
 	ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory)