diff --git a/cc/cc.go b/cc/cc.go index 00b82d552..154c353f6 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -746,6 +746,7 @@ var ( runtimeDepTag = installDependencyTag{name: "runtime lib"} testPerSrcDepTag = dependencyTag{name: "test_per_src"} stubImplDepTag = dependencyTag{name: "stub_impl"} + JniFuzzLibTag = dependencyTag{name: "jni_fuzz_lib_tag"} ) func IsSharedDepTag(depTag blueprint.DependencyTag) bool { diff --git a/cc/sanitize.go b/cc/sanitize.go index 814fef6a9..53169de48 100644 --- a/cc/sanitize.go +++ b/cc/sanitize.go @@ -969,6 +969,22 @@ func sanitizerDepsMutator(t SanitizerType) func(android.TopDownMutatorContext) { }) } } else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok { + // If it's a Java module with native dependencies through jni, + // set the sanitizer for them + if jniSanitizeable, ok := mctx.Module().(JniSanitizeable); ok { + if jniSanitizeable.IsSanitizerEnabledForJni(mctx, t.name()) { + mctx.VisitDirectDeps(func(child android.Module) { + if c, ok := child.(PlatformSanitizeable); ok && + mctx.OtherModuleDependencyTag(child) == JniFuzzLibTag && + c.SanitizePropDefined() && + !c.SanitizeNever() && + !c.IsSanitizerExplicitlyDisabled(t) { + c.SetSanitizeDep(true) + } + }) + } + } + // If an APEX module includes a lib which is enabled for a sanitizer T, then // the APEX module is also enabled for the same sanitizer type. mctx.VisitDirectDeps(func(child android.Module) { @@ -1280,6 +1296,11 @@ type Sanitizeable interface { 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 } @@ -1407,7 +1428,7 @@ func sanitizerMutator(t SanitizerType) func(android.BottomUpMutatorContext) { } c.SetSanitizeDep(false) } else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok && sanitizeable.IsSanitizerEnabled(mctx, t.name()) { - // APEX modules fall here + // APEX and Java fuzz modules fall here sanitizeable.AddSanitizerDependencies(mctx, t.name()) mctx.CreateVariations(t.variationName()) } else if c, ok := mctx.Module().(*Module); ok { diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go index 89f81879f..700cdf0a7 100644 --- a/fuzz/fuzz_common.go +++ b/fuzz/fuzz_common.go @@ -82,6 +82,9 @@ type FuzzConfig struct { Hwasan_options []string `json:"hwasan_options,omitempty"` // Additional options to be passed to HWASAN when running on host in Haiku. Asan_options []string `json:"asan_options,omitempty"` + // If there's a Java fuzzer with JNI, a different version of Jazzer would + // need to be added to the fuzzer package than one without JNI + IsJni *bool `json:"is_jni,omitempty"` } type FuzzProperties struct { diff --git a/java/androidmk.go b/java/androidmk.go index 80b828d45..f6ea6a930 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -132,6 +132,16 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { return entriesList } +func (j *JavaFuzzLibrary) AndroidMkEntries() []android.AndroidMkEntries { + entriesList := j.Library.AndroidMkEntries() + entries := &entriesList[0] + entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", "null-suite") + androidMkWriteTestData(j.jniFilePaths, entries) + }) + return entriesList +} + // Called for modules that are a component of a test suite. func testSuiteComponent(entries *android.AndroidMkEntries, test_suites []string, perTestcaseDirectory bool) { entries.SetString("LOCAL_MODULE_TAGS", "tests") diff --git a/java/fuzz.go b/java/fuzz.go index 257f34356..584c80b0c 100644 --- a/java/fuzz.go +++ b/java/fuzz.go @@ -15,14 +15,25 @@ package java import ( - "github.com/google/blueprint/proptools" "sort" "strings" + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" + "android/soong/android" + "android/soong/cc" "android/soong/fuzz" ) +type jniProperties struct { + // list of jni libs + Jni_libs []string + + // sanitization + Sanitizers []string +} + func init() { RegisterJavaFuzzBuildComponents(android.InitRegistrationContext) } @@ -35,11 +46,60 @@ func RegisterJavaFuzzBuildComponents(ctx android.RegistrationContext) { type JavaFuzzLibrary struct { Library fuzzPackagedModule fuzz.FuzzPackagedModule + jniProperties jniProperties + jniFilePaths android.Paths +} + +// IsSanitizerEnabled implemented to make JavaFuzzLibrary implement +// cc.Sanitizeable +func (j *JavaFuzzLibrary) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool { + for _, s := range j.jniProperties.Sanitizers { + if sanitizerName == s { + return true + } + } + return false +} + +// IsSanitizerEnabledForJni implemented to make JavaFuzzLibrary implement +// cc.JniSanitizeable. It returns a bool for whether a cc dependency should be +// sanitized for the given sanitizer or not. +func (j *JavaFuzzLibrary) IsSanitizerEnabledForJni(ctx android.BaseModuleContext, sanitizerName string) bool { + return j.IsSanitizerEnabled(ctx, sanitizerName) +} + +// EnableSanitizer implemented to make JavaFuzzLibrary implement +// cc.Sanitizeable +func (j *JavaFuzzLibrary) EnableSanitizer(sanitizerName string) { +} + +// AddSanitizerDependencies implemented to make JavaFuzzLibrary implement +// cc.Sanitizeable +func (j *JavaFuzzLibrary) AddSanitizerDependencies(mctx android.BottomUpMutatorContext, sanitizerName string) { +} + +// To verify that JavaFuzzLibrary implements cc.Sanitizeable +var _ cc.Sanitizeable = (*JavaFuzzLibrary)(nil) + +func (j *JavaFuzzLibrary) DepsMutator(mctx android.BottomUpMutatorContext) { + if len(j.jniProperties.Jni_libs) > 0 { + if j.fuzzPackagedModule.FuzzProperties.Fuzz_config == nil { + config := &fuzz.FuzzConfig{} + j.fuzzPackagedModule.FuzzProperties.Fuzz_config = config + } + // this will be used by the ingestion pipeline to determine the version + // of jazzer to add to the fuzzer package + j.fuzzPackagedModule.FuzzProperties.Fuzz_config.IsJni = proptools.BoolPtr(true) + + for _, target := range mctx.MultiTargets() { + sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) + mctx.AddFarVariationDependencies(sharedLibVariations, cc.JniFuzzLibTag, j.jniProperties.Jni_libs...) + } + } + j.Library.DepsMutator(mctx) } func (j *JavaFuzzLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { - j.Library.GenerateAndroidBuildActions(ctx) - if j.fuzzPackagedModule.FuzzProperties.Corpus != nil { j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus) } @@ -55,6 +115,23 @@ func (j *JavaFuzzLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) android.WriteFileRule(ctx, configPath, j.fuzzPackagedModule.FuzzProperties.Fuzz_config.String()) j.fuzzPackagedModule.Config = configPath } + + ctx.VisitDirectDepsWithTag(cc.JniFuzzLibTag, func(dep android.Module) { + sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo) + if sharedLibInfo.SharedLibrary != nil { + libPath := android.PathForModuleOut(ctx, sharedLibInfo.SharedLibrary.Base()) + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Input: sharedLibInfo.SharedLibrary, + Output: libPath, + }) + j.jniFilePaths = append(j.jniFilePaths, libPath) + } else { + ctx.PropertyErrorf("jni_libs", "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep)) + } + }) + + j.Library.GenerateAndroidBuildActions(ctx) } // java_fuzz builds and links sources into a `.jar` file for the host. @@ -65,7 +142,8 @@ func FuzzFactory() android.Module { module := &JavaFuzzLibrary{} module.addHostProperties() - module.Module.properties.Installable = proptools.BoolPtr(false) + module.AddProperties(&module.jniProperties) + module.Module.properties.Installable = proptools.BoolPtr(true) module.AddProperties(&module.fuzzPackagedModule.FuzzProperties) // java_fuzz packaging rules collide when both linux_glibc and linux_bionic are enabled, disable the linux_bionic variants. @@ -83,7 +161,7 @@ func FuzzFactory() android.Module { module.initModuleAndImport(module) android.InitSdkAwareModule(module) - InitJavaModule(module, android.HostSupported) + InitJavaModuleMultiTargets(module, android.HostSupported) return module } @@ -106,26 +184,26 @@ func (s *javaFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) { ctx.VisitAllModules(func(module android.Module) { // Discard non-fuzz targets. - javaModule, ok := module.(*JavaFuzzLibrary) + javaFuzzModule, ok := module.(*JavaFuzzLibrary) if !ok { return } fuzzModuleValidator := fuzz.FuzzModule{ - javaModule.ModuleBase, - javaModule.DefaultableModuleBase, - javaModule.ApexModuleBase, + javaFuzzModule.ModuleBase, + javaFuzzModule.DefaultableModuleBase, + javaFuzzModule.ApexModuleBase, } - if ok := fuzz.IsValid(fuzzModuleValidator); !ok || *javaModule.Module.properties.Installable { + if ok := fuzz.IsValid(fuzzModuleValidator); !ok { return } hostOrTargetString := "target" - if javaModule.Host() { + if javaFuzzModule.Host() { hostOrTargetString = "host" } - archString := javaModule.Arch().ArchType.String() + archString := javaFuzzModule.Arch().ArchType.String() archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString) archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()} @@ -134,12 +212,17 @@ func (s *javaFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) { builder := android.NewRuleBuilder(pctx, ctx) // Package the artifacts (data, corpus, config and dictionary into a zipfile. - files = s.PackageArtifacts(ctx, module, javaModule.fuzzPackagedModule, archDir, builder) + files = s.PackageArtifacts(ctx, module, javaFuzzModule.fuzzPackagedModule, archDir, builder) // Add .jar - files = append(files, fuzz.FileToZip{javaModule.outputFile, ""}) + files = append(files, fuzz.FileToZip{javaFuzzModule.outputFile, ""}) - archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaModule.fuzzPackagedModule, files, builder, archDir, archString, "host", archOs, archDirs) + // Add jni .so files + for _, fPath := range javaFuzzModule.jniFilePaths { + files = append(files, fuzz.FileToZip{fPath, ""}) + } + + archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaFuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs) if !ok { return } diff --git a/java/fuzz_test.go b/java/fuzz_test.go index cf063ebe2..0a2c94517 100644 --- a/java/fuzz_test.go +++ b/java/fuzz_test.go @@ -15,13 +15,17 @@ package java import ( - "android/soong/android" "path/filepath" + "runtime" "testing" + + "android/soong/android" + "android/soong/cc" ) var prepForJavaFuzzTest = android.GroupFixturePreparers( PrepareForTestWithJavaDefaultModules, + cc.PrepareForTestWithCcBuildComponents, android.FixtureRegisterWithContext(RegisterJavaFuzzBuildComponents), ) @@ -32,6 +36,13 @@ func TestJavaFuzz(t *testing.T) { srcs: ["a.java"], libs: ["bar"], static_libs: ["baz"], + jni_libs: [ + "libjni", + ], + sanitizers: [ + "address", + "fuzzer", + ], } java_library_host { @@ -42,11 +53,21 @@ func TestJavaFuzz(t *testing.T) { java_library_host { name: "baz", srcs: ["c.java"], - }`) + } + + cc_library_shared { + name: "libjni", + host_supported: true, + device_supported: false, + stl: "none", + } + `) osCommonTarget := result.Config.BuildOSCommonTarget.String() - javac := result.ModuleForTests("foo", osCommonTarget).Rule("javac") - combineJar := result.ModuleForTests("foo", osCommonTarget).Description("for javac") + + osCommonTargetWithSan := osCommonTarget + "_asan" + "_fuzzer" + javac := result.ModuleForTests("foo", osCommonTargetWithSan).Rule("javac") + combineJar := result.ModuleForTests("foo", osCommonTargetWithSan).Description("for javac") if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" { t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs) @@ -62,4 +83,18 @@ func TestJavaFuzz(t *testing.T) { if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz { t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz) } + + ctx := result.TestContext + foo := ctx.ModuleForTests("foo", osCommonTargetWithSan).Module().(*JavaFuzzLibrary) + + expected := "libjni.so" + if runtime.GOOS == "darwin" { + expected = "libjni.dylib" + } + + fooJniFilePaths := foo.jniFilePaths + if len(fooJniFilePaths) != 1 || fooJniFilePaths[0].Rel() != expected { + t.Errorf(`expected foo test data relative path [%q], got %q`, + expected, fooJniFilePaths.Strings()) + } }