123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560 |
- // 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 (
- "path/filepath"
- "sort"
- "strings"
- "github.com/google/blueprint/proptools"
- "android/soong/android"
- "android/soong/cc/config"
- "android/soong/fuzz"
- )
- func init() {
- android.RegisterModuleType("cc_fuzz", LibFuzzFactory)
- android.RegisterParallelSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
- }
- type FuzzProperties struct {
- FuzzFramework fuzz.Framework `blueprint:"mutated"`
- }
- type fuzzer struct {
- Properties FuzzProperties
- }
- func (fuzzer *fuzzer) flags(ctx ModuleContext, flags Flags) Flags {
- if fuzzer.Properties.FuzzFramework == fuzz.AFL {
- flags.Local.CFlags = append(flags.Local.CFlags, []string{
- "-fsanitize-coverage=trace-pc-guard",
- "-Wno-unused-result",
- "-Wno-unused-parameter",
- "-Wno-unused-function",
- }...)
- }
- return flags
- }
- func (fuzzer *fuzzer) props() []interface{} {
- return []interface{}{&fuzzer.Properties}
- }
- func fuzzMutatorDeps(mctx android.TopDownMutatorContext) {
- currentModule, ok := mctx.Module().(*Module)
- if !ok {
- return
- }
- if currentModule.fuzzer == nil {
- return
- }
- mctx.WalkDeps(func(child android.Module, parent android.Module) bool {
- c, ok := child.(*Module)
- if !ok {
- return false
- }
- if c.sanitize == nil {
- return false
- }
- isFuzzerPointer := c.sanitize.getSanitizerBoolPtr(Fuzzer)
- if isFuzzerPointer == nil || !*isFuzzerPointer {
- return false
- }
- if c.fuzzer == nil {
- return false
- }
- c.fuzzer.Properties.FuzzFramework = currentModule.fuzzer.Properties.FuzzFramework
- return true
- })
- }
- // cc_fuzz creates a host/device fuzzer binary. Host binaries can be found at
- // $ANDROID_HOST_OUT/fuzz/, and device binaries can be found at /data/fuzz on
- // your device, or $ANDROID_PRODUCT_OUT/data/fuzz in your build tree.
- func LibFuzzFactory() android.Module {
- module := NewFuzzer(android.HostAndDeviceSupported)
- return module.Init()
- }
- type fuzzBinary struct {
- *binaryDecorator
- *baseCompiler
- fuzzPackagedModule fuzz.FuzzPackagedModule
- installedSharedDeps []string
- sharedLibraries android.RuleBuilderInstalls
- }
- func (fuzz *fuzzBinary) fuzzBinary() bool {
- return true
- }
- func (fuzz *fuzzBinary) linkerProps() []interface{} {
- props := fuzz.binaryDecorator.linkerProps()
- props = append(props, &fuzz.fuzzPackagedModule.FuzzProperties)
- return props
- }
- func (fuzz *fuzzBinary) linkerInit(ctx BaseModuleContext) {
- fuzz.binaryDecorator.linkerInit(ctx)
- }
- func (fuzzBin *fuzzBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
- if ctx.Config().Getenv("FUZZ_FRAMEWORK") == "AFL" {
- deps.HeaderLibs = append(deps.HeaderLibs, "libafl_headers")
- } else {
- deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeLibrary(ctx.toolchain()))
- // Fuzzers built with HWASAN should use the interceptors for better
- // mutation based on signals in strcmp, memcpy, etc. This is only needed for
- // fuzz targets, not generic HWASAN-ified binaries or libraries.
- if module, ok := ctx.Module().(*Module); ok {
- if module.IsSanitizerEnabled(Hwasan) {
- deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeInterceptors(ctx.toolchain()))
- }
- }
- }
- deps = fuzzBin.binaryDecorator.linkerDeps(ctx, deps)
- return deps
- }
- func (fuzz *fuzzBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
- flags = fuzz.binaryDecorator.linkerFlags(ctx, flags)
- // RunPaths on devices isn't instantiated by the base linker. `../lib` for
- // installed fuzz targets (both host and device), and `./lib` for fuzz
- // target packages.
- flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
- // When running on device, fuzz targets with vendor: true set will be in
- // fuzzer_name/vendor/fuzzer_name (note the extra 'vendor' and thus need to
- // link with libraries in ../../lib/. Non-vendor binaries only need to look
- // one level up, in ../lib/.
- if ctx.inVendor() {
- flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../../lib`)
- } else {
- flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
- }
- return flags
- }
- // IsValidSharedDependency takes a module and determines if it is a unique shared library
- // that should be installed in the fuzz target output directories. This function
- // returns true, unless:
- // - The module is not an installable shared library, or
- // - The module is a header or stub, or
- // - The module is a prebuilt and its source is available, or
- // - The module is a versioned member of an SDK snapshot.
- func IsValidSharedDependency(dependency android.Module) bool {
- // TODO(b/144090547): We should be parsing these modules using
- // ModuleDependencyTag instead of the current brute-force checking.
- linkable, ok := dependency.(LinkableInterface)
- if !ok || !linkable.CcLibraryInterface() {
- // Discard non-linkables.
- return false
- }
- if !linkable.Shared() {
- // Discard static libs.
- return false
- }
- if lib := moduleLibraryInterface(dependency); lib != nil && lib.buildStubs() && linkable.CcLibrary() {
- // Discard stubs libs (only CCLibrary variants). Prebuilt libraries should not
- // be excluded on the basis of they're not CCLibrary()'s.
- return false
- }
- // We discarded module stubs libraries above, but the LLNDK prebuilts stubs
- // libraries must be handled differently - by looking for the stubDecorator.
- // Discard LLNDK prebuilts stubs as well.
- if ccLibrary, isCcLibrary := dependency.(*Module); isCcLibrary {
- if _, isLLndkStubLibrary := ccLibrary.linker.(*stubDecorator); isLLndkStubLibrary {
- return false
- }
- // Discard installable:false libraries because they are expected to be absent
- // in runtime.
- if !proptools.BoolDefault(ccLibrary.Installable(), true) {
- return false
- }
- }
- // If the same library is present both as source and a prebuilt we must pick
- // only one to avoid a conflict. Always prefer the source since the prebuilt
- // probably won't be built with sanitizers enabled.
- if prebuilt := android.GetEmbeddedPrebuilt(dependency); prebuilt != nil && prebuilt.SourceExists() {
- return false
- }
- return true
- }
- func SharedLibraryInstallLocation(
- libraryBase string, isHost bool, fuzzDir string, archString string) string {
- installLocation := "$(PRODUCT_OUT)/data"
- if isHost {
- installLocation = "$(HOST_OUT)"
- }
- installLocation = filepath.Join(
- installLocation, fuzzDir, archString, "lib", libraryBase)
- return installLocation
- }
- // Get the device-only shared library symbols install directory.
- func SharedLibrarySymbolsInstallLocation(libraryBase string, fuzzDir string, archString string) string {
- return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryBase)
- }
- func (fuzzBin *fuzzBinary) install(ctx ModuleContext, file android.Path) {
- installBase := "fuzz"
- fuzzBin.binaryDecorator.baseInstaller.dir = filepath.Join(
- installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
- fuzzBin.binaryDecorator.baseInstaller.dir64 = filepath.Join(
- installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
- fuzzBin.binaryDecorator.baseInstaller.install(ctx, file)
- fuzzBin.fuzzPackagedModule = PackageFuzzModule(ctx, fuzzBin.fuzzPackagedModule, pctx)
- // Grab the list of required shared libraries.
- fuzzBin.sharedLibraries, _ = CollectAllSharedDependencies(ctx)
- for _, ruleBuilderInstall := range fuzzBin.sharedLibraries {
- install := ruleBuilderInstall.To
- fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
- SharedLibraryInstallLocation(
- install, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
- // Also add the dependency on the shared library symbols dir.
- if !ctx.Host() {
- fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
- SharedLibrarySymbolsInstallLocation(install, installBase, ctx.Arch().ArchType.String()))
- }
- }
- }
- func PackageFuzzModule(ctx android.ModuleContext, fuzzPackagedModule fuzz.FuzzPackagedModule, pctx android.PackageContext) fuzz.FuzzPackagedModule {
- fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Corpus)
- builder := android.NewRuleBuilder(pctx, ctx)
- intermediateDir := android.PathForModuleOut(ctx, "corpus")
- for _, entry := range fuzzPackagedModule.Corpus {
- builder.Command().Text("cp").
- Input(entry).
- Output(intermediateDir.Join(ctx, entry.Base()))
- }
- builder.Build("copy_corpus", "copy corpus")
- fuzzPackagedModule.CorpusIntermediateDir = intermediateDir
- fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Data)
- builder = android.NewRuleBuilder(pctx, ctx)
- intermediateDir = android.PathForModuleOut(ctx, "data")
- for _, entry := range fuzzPackagedModule.Data {
- builder.Command().Text("cp").
- Input(entry).
- Output(intermediateDir.Join(ctx, entry.Rel()))
- }
- builder.Build("copy_data", "copy data")
- fuzzPackagedModule.DataIntermediateDir = intermediateDir
- if fuzzPackagedModule.FuzzProperties.Dictionary != nil {
- fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzzPackagedModule.FuzzProperties.Dictionary)
- if fuzzPackagedModule.Dictionary.Ext() != ".dict" {
- ctx.PropertyErrorf("dictionary",
- "Fuzzer dictionary %q does not have '.dict' extension",
- fuzzPackagedModule.Dictionary.String())
- }
- }
- if fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
- configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
- android.WriteFileRule(ctx, configPath, fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
- fuzzPackagedModule.Config = configPath
- }
- return fuzzPackagedModule
- }
- func NewFuzzer(hod android.HostOrDeviceSupported) *Module {
- module, binary := newBinary(hod, false)
- baseInstallerPath := "fuzz"
- binary.baseInstaller = NewBaseInstaller(baseInstallerPath, baseInstallerPath, InstallInData)
- fuzzBin := &fuzzBinary{
- binaryDecorator: binary,
- baseCompiler: NewBaseCompiler(),
- }
- module.compiler = fuzzBin
- module.linker = fuzzBin
- module.installer = fuzzBin
- module.fuzzer.Properties.FuzzFramework = fuzz.LibFuzzer
- // The fuzzer runtime is not present for darwin host modules, disable cc_fuzz modules when targeting darwin.
- android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- extraProps := struct {
- Sanitize struct {
- Fuzzer *bool
- }
- Target struct {
- Darwin struct {
- Enabled *bool
- }
- Linux_bionic struct {
- Enabled *bool
- }
- }
- }{}
- extraProps.Sanitize.Fuzzer = BoolPtr(true)
- extraProps.Target.Darwin.Enabled = BoolPtr(false)
- extraProps.Target.Linux_bionic.Enabled = BoolPtr(false)
- ctx.AppendProperties(&extraProps)
- targetFramework := fuzz.GetFramework(ctx, fuzz.Cc)
- if !fuzz.IsValidFrameworkForModule(targetFramework, fuzz.Cc, fuzzBin.fuzzPackagedModule.FuzzProperties.Fuzzing_frameworks) {
- ctx.Module().Disable()
- return
- }
- if targetFramework == fuzz.AFL {
- fuzzBin.baseCompiler.Properties.Srcs = append(fuzzBin.baseCompiler.Properties.Srcs, ":aflpp_driver", ":afl-compiler-rt")
- module.fuzzer.Properties.FuzzFramework = fuzz.AFL
- }
- })
- return module
- }
- // Responsible for generating GNU Make rules that package fuzz targets into
- // their architecture & target/host specific zip file.
- type ccRustFuzzPackager struct {
- fuzz.FuzzPackager
- fuzzPackagingArchModules string
- fuzzTargetSharedDepsInstallPairs string
- allFuzzTargetsName string
- }
- func fuzzPackagingFactory() android.Singleton {
- fuzzPackager := &ccRustFuzzPackager{
- fuzzPackagingArchModules: "SOONG_FUZZ_PACKAGING_ARCH_MODULES",
- fuzzTargetSharedDepsInstallPairs: "FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
- allFuzzTargetsName: "ALL_FUZZ_TARGETS",
- }
- return fuzzPackager
- }
- func (s *ccRustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
- // Map between each architecture + host/device combination, and the files that
- // need to be packaged (in the tuple of {source file, destination folder in
- // archive}).
- archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
- // List of individual fuzz targets, so that 'make fuzz' also installs the targets
- // to the correct output directories as well.
- s.FuzzTargets = make(map[string]bool)
- // Map tracking whether each shared library has an install rule to avoid duplicate install rules from
- // multiple fuzzers that depend on the same shared library.
- sharedLibraryInstalled := make(map[string]bool)
- ctx.VisitAllModules(func(module android.Module) {
- ccModule, ok := module.(LinkableInterface)
- if !ok || ccModule.PreventInstall() {
- return
- }
- // Discard non-fuzz targets.
- if ok := fuzz.IsValid(ccModule.FuzzModuleStruct()); !ok {
- return
- }
- sharedLibsInstallDirPrefix := "lib"
- if !ccModule.IsFuzzModule() {
- return
- }
- hostOrTargetString := "target"
- if ccModule.Host() {
- hostOrTargetString = "host"
- }
- fpm := fuzz.FuzzPackagedModule{}
- if ok {
- fpm = ccModule.FuzzPackagedModule()
- }
- intermediatePath := "fuzz"
- archString := ccModule.Target().Arch.ArchType.String()
- archDir := android.PathForIntermediates(ctx, intermediatePath, hostOrTargetString, archString)
- archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
- var files []fuzz.FileToZip
- builder := android.NewRuleBuilder(pctx, ctx)
- // Package the corpus, data, dict and config into a zipfile.
- files = s.PackageArtifacts(ctx, module, fpm, archDir, builder)
- // Package shared libraries
- files = append(files, GetSharedLibsToZip(ccModule.FuzzSharedLibraries(), ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
- // The executable.
- files = append(files, fuzz.FileToZip{SourceFilePath: android.OutputFileForModule(ctx, ccModule, "unstripped")})
- archDirs[archOs], ok = s.BuildZipFile(ctx, module, fpm, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
- if !ok {
- return
- }
- })
- s.CreateFuzzPackage(ctx, archDirs, fuzz.Cc, pctx)
- }
- func (s *ccRustFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
- packages := s.Packages.Strings()
- sort.Strings(packages)
- sort.Strings(s.FuzzPackager.SharedLibInstallStrings)
- // TODO(mitchp): Migrate this to use MakeVarsContext::DistForGoal() when it's
- // ready to handle phony targets created in Soong. In the meantime, this
- // exports the phony 'fuzz' target and dependencies on packages to
- // core/main.mk so that we can use dist-for-goals.
- ctx.Strict(s.fuzzPackagingArchModules, strings.Join(packages, " "))
- ctx.Strict(s.fuzzTargetSharedDepsInstallPairs,
- strings.Join(s.FuzzPackager.SharedLibInstallStrings, " "))
- // Preallocate the slice of fuzz targets to minimise memory allocations.
- s.PreallocateSlice(ctx, s.allFuzzTargetsName)
- }
- // GetSharedLibsToZip finds and marks all the transiently-dependent shared libraries for
- // packaging.
- func GetSharedLibsToZip(sharedLibraries android.RuleBuilderInstalls, module LinkableInterface, s *fuzz.FuzzPackager, archString string, destinationPathPrefix string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
- var files []fuzz.FileToZip
- fuzzDir := "fuzz"
- for _, ruleBuilderInstall := range sharedLibraries {
- library := ruleBuilderInstall.From
- install := ruleBuilderInstall.To
- files = append(files, fuzz.FileToZip{
- SourceFilePath: library,
- DestinationPathPrefix: destinationPathPrefix,
- DestinationPath: install,
- })
- // For each architecture-specific shared library dependency, we need to
- // install it to the output directory. Setup the install destination here,
- // which will be used by $(copy-many-files) in the Make backend.
- installDestination := SharedLibraryInstallLocation(
- install, module.Host(), fuzzDir, archString)
- if (*sharedLibraryInstalled)[installDestination] {
- continue
- }
- (*sharedLibraryInstalled)[installDestination] = true
- // Escape all the variables, as the install destination here will be called
- // via. $(eval) in Make.
- installDestination = strings.ReplaceAll(
- installDestination, "$", "$$")
- s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
- library.String()+":"+installDestination)
- // Ensure that on device, the library is also reinstalled to the /symbols/
- // dir. Symbolized DSO's are always installed to the device when fuzzing, but
- // we want symbolization tools (like `stack`) to be able to find the symbols
- // in $ANDROID_PRODUCT_OUT/symbols automagically.
- if !module.Host() {
- symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(install, fuzzDir, archString)
- symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
- s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
- library.String()+":"+symbolsInstallDestination)
- }
- }
- return files
- }
- // CollectAllSharedDependencies search over the provided module's dependencies using
- // VisitDirectDeps and WalkDeps to enumerate all shared library dependencies.
- // VisitDirectDeps is used first to avoid incorrectly using the core libraries (sanitizer
- // runtimes, libc, libdl, etc.) from a dependency. This may cause issues when dependencies
- // have explicit sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
- func CollectAllSharedDependencies(ctx android.ModuleContext) (android.RuleBuilderInstalls, []android.Module) {
- seen := make(map[string]bool)
- recursed := make(map[string]bool)
- deps := []android.Module{}
- var sharedLibraries android.RuleBuilderInstalls
- // Enumerate the first level of dependencies, as we discard all non-library
- // modules in the BFS loop below.
- ctx.VisitDirectDeps(func(dep android.Module) {
- if !IsValidSharedDependency(dep) {
- return
- }
- if !ctx.OtherModuleHasProvider(dep, SharedLibraryInfoProvider) {
- return
- }
- if seen[ctx.OtherModuleName(dep)] {
- return
- }
- seen[ctx.OtherModuleName(dep)] = true
- deps = append(deps, dep)
- sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
- installDestination := sharedLibraryInfo.SharedLibrary.Base()
- ruleBuilderInstall := android.RuleBuilderInstall{android.OutputFileForModule(ctx, dep, "unstripped"), installDestination}
- sharedLibraries = append(sharedLibraries, ruleBuilderInstall)
- })
- ctx.WalkDeps(func(child, parent android.Module) bool {
- if !IsValidSharedDependency(child) {
- return false
- }
- if !ctx.OtherModuleHasProvider(child, SharedLibraryInfoProvider) {
- return false
- }
- if !seen[ctx.OtherModuleName(child)] {
- seen[ctx.OtherModuleName(child)] = true
- deps = append(deps, child)
- sharedLibraryInfo := ctx.OtherModuleProvider(child, SharedLibraryInfoProvider).(SharedLibraryInfo)
- installDestination := sharedLibraryInfo.SharedLibrary.Base()
- ruleBuilderInstall := android.RuleBuilderInstall{android.OutputFileForModule(ctx, child, "unstripped"), installDestination}
- sharedLibraries = append(sharedLibraries, ruleBuilderInstall)
- }
- if recursed[ctx.OtherModuleName(child)] {
- return false
- }
- recursed[ctx.OtherModuleName(child)] = true
- return true
- })
- return sharedLibraries, deps
- }
|