1// Copyright 2016 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package cc 16 17import ( 18 "path/filepath" 19 "sort" 20 "strings" 21 22 "github.com/google/blueprint/proptools" 23 24 "android/soong/android" 25 "android/soong/cc/config" 26 "android/soong/fuzz" 27) 28 29func init() { 30 android.RegisterModuleType("cc_fuzz", LibFuzzFactory) 31 android.RegisterSingletonType("cc_fuzz_packaging", fuzzPackagingFactory) 32} 33 34type FuzzProperties struct { 35 FuzzFramework fuzz.Framework `blueprint:"mutated"` 36} 37 38type fuzzer struct { 39 Properties FuzzProperties 40} 41 42func (fuzzer *fuzzer) flags(ctx ModuleContext, flags Flags) Flags { 43 if fuzzer.Properties.FuzzFramework == fuzz.AFL { 44 flags.Local.CFlags = append(flags.Local.CFlags, []string{ 45 "-fsanitize-coverage=trace-pc-guard", 46 "-Wno-unused-result", 47 "-Wno-unused-parameter", 48 "-Wno-unused-function", 49 }...) 50 } 51 52 return flags 53} 54 55func (fuzzer *fuzzer) props() []interface{} { 56 return []interface{}{&fuzzer.Properties} 57} 58 59func fuzzMutatorDeps(mctx android.TopDownMutatorContext) { 60 currentModule, ok := mctx.Module().(*Module) 61 if !ok { 62 return 63 } 64 65 if currentModule.fuzzer == nil { 66 return 67 } 68 69 mctx.WalkDeps(func(child android.Module, parent android.Module) bool { 70 c, ok := child.(*Module) 71 if !ok { 72 return false 73 } 74 75 if c.sanitize == nil { 76 return false 77 } 78 79 isFuzzerPointer := c.sanitize.getSanitizerBoolPtr(Fuzzer) 80 if isFuzzerPointer == nil || !*isFuzzerPointer { 81 return false 82 } 83 84 if c.fuzzer == nil { 85 return false 86 } 87 88 c.fuzzer.Properties.FuzzFramework = currentModule.fuzzer.Properties.FuzzFramework 89 return true 90 }) 91} 92 93// cc_fuzz creates a host/device fuzzer binary. Host binaries can be found at 94// $ANDROID_HOST_OUT/fuzz/, and device binaries can be found at /data/fuzz on 95// your device, or $ANDROID_PRODUCT_OUT/data/fuzz in your build tree. 96func LibFuzzFactory() android.Module { 97 module := NewFuzzer(android.HostAndDeviceSupported) 98 return module.Init() 99} 100 101type fuzzBinary struct { 102 *binaryDecorator 103 *baseCompiler 104 fuzzPackagedModule fuzz.FuzzPackagedModule 105 installedSharedDeps []string 106 sharedLibraries android.Paths 107} 108 109func (fuzz *fuzzBinary) fuzzBinary() bool { 110 return true 111} 112 113func (fuzz *fuzzBinary) linkerProps() []interface{} { 114 props := fuzz.binaryDecorator.linkerProps() 115 props = append(props, &fuzz.fuzzPackagedModule.FuzzProperties) 116 117 return props 118} 119 120func (fuzz *fuzzBinary) linkerInit(ctx BaseModuleContext) { 121 fuzz.binaryDecorator.linkerInit(ctx) 122} 123 124func (fuzzBin *fuzzBinary) linkerDeps(ctx DepsContext, deps Deps) Deps { 125 if ctx.Config().Getenv("FUZZ_FRAMEWORK") == "AFL" { 126 deps.HeaderLibs = append(deps.HeaderLibs, "libafl_headers") 127 } else { 128 deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeLibrary(ctx.toolchain())) 129 // Fuzzers built with HWASAN should use the interceptors for better 130 // mutation based on signals in strcmp, memcpy, etc. This is only needed for 131 // fuzz targets, not generic HWASAN-ified binaries or libraries. 132 if module, ok := ctx.Module().(*Module); ok { 133 if module.IsSanitizerEnabled(Hwasan) { 134 deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeInterceptors(ctx.toolchain())) 135 } 136 } 137 } 138 139 deps = fuzzBin.binaryDecorator.linkerDeps(ctx, deps) 140 return deps 141} 142 143func (fuzz *fuzzBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags { 144 flags = fuzz.binaryDecorator.linkerFlags(ctx, flags) 145 // RunPaths on devices isn't instantiated by the base linker. `../lib` for 146 // installed fuzz targets (both host and device), and `./lib` for fuzz 147 // target packages. 148 flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/lib`) 149 150 // When running on device, fuzz targets with vendor: true set will be in 151 // fuzzer_name/vendor/fuzzer_name (note the extra 'vendor' and thus need to 152 // link with libraries in ../../lib/. Non-vendor binaries only need to look 153 // one level up, in ../lib/. 154 if ctx.inVendor() { 155 flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../../lib`) 156 } else { 157 flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../lib`) 158 } 159 160 return flags 161} 162 163// IsValidSharedDependency takes a module and determines if it is a unique shared library 164// that should be installed in the fuzz target output directories. This function 165// returns true, unless: 166// - The module is not an installable shared library, or 167// - The module is a header or stub, or 168// - The module is a prebuilt and its source is available, or 169// - The module is a versioned member of an SDK snapshot. 170func IsValidSharedDependency(dependency android.Module) bool { 171 // TODO(b/144090547): We should be parsing these modules using 172 // ModuleDependencyTag instead of the current brute-force checking. 173 174 linkable, ok := dependency.(LinkableInterface) 175 if !ok || !linkable.CcLibraryInterface() { 176 // Discard non-linkables. 177 return false 178 } 179 180 if !linkable.Shared() { 181 // Discard static libs. 182 return false 183 } 184 185 if lib := moduleLibraryInterface(dependency); lib != nil && lib.buildStubs() && linkable.CcLibrary() { 186 // Discard stubs libs (only CCLibrary variants). Prebuilt libraries should not 187 // be excluded on the basis of they're not CCLibrary()'s. 188 return false 189 } 190 191 // We discarded module stubs libraries above, but the LLNDK prebuilts stubs 192 // libraries must be handled differently - by looking for the stubDecorator. 193 // Discard LLNDK prebuilts stubs as well. 194 if ccLibrary, isCcLibrary := dependency.(*Module); isCcLibrary { 195 if _, isLLndkStubLibrary := ccLibrary.linker.(*stubDecorator); isLLndkStubLibrary { 196 return false 197 } 198 // Discard installable:false libraries because they are expected to be absent 199 // in runtime. 200 if !proptools.BoolDefault(ccLibrary.Installable(), true) { 201 return false 202 } 203 } 204 205 // If the same library is present both as source and a prebuilt we must pick 206 // only one to avoid a conflict. Always prefer the source since the prebuilt 207 // probably won't be built with sanitizers enabled. 208 if prebuilt := android.GetEmbeddedPrebuilt(dependency); prebuilt != nil && prebuilt.SourceExists() { 209 return false 210 } 211 212 return true 213} 214 215func SharedLibraryInstallLocation( 216 libraryPath android.Path, isHost bool, fuzzDir string, archString string) string { 217 installLocation := "$(PRODUCT_OUT)/data" 218 if isHost { 219 installLocation = "$(HOST_OUT)" 220 } 221 installLocation = filepath.Join( 222 installLocation, fuzzDir, archString, "lib", libraryPath.Base()) 223 return installLocation 224} 225 226// Get the device-only shared library symbols install directory. 227func SharedLibrarySymbolsInstallLocation(libraryPath android.Path, fuzzDir string, archString string) string { 228 return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryPath.Base()) 229} 230 231func (fuzzBin *fuzzBinary) install(ctx ModuleContext, file android.Path) { 232 installBase := "fuzz" 233 234 fuzzBin.binaryDecorator.baseInstaller.dir = filepath.Join( 235 installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName()) 236 fuzzBin.binaryDecorator.baseInstaller.dir64 = filepath.Join( 237 installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName()) 238 fuzzBin.binaryDecorator.baseInstaller.install(ctx, file) 239 240 fuzzBin.fuzzPackagedModule = PackageFuzzModule(ctx, fuzzBin.fuzzPackagedModule, pctx) 241 242 // Grab the list of required shared libraries. 243 fuzzBin.sharedLibraries, _ = CollectAllSharedDependencies(ctx) 244 245 for _, lib := range fuzzBin.sharedLibraries { 246 fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps, 247 SharedLibraryInstallLocation( 248 lib, ctx.Host(), installBase, ctx.Arch().ArchType.String())) 249 250 // Also add the dependency on the shared library symbols dir. 251 if !ctx.Host() { 252 fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps, 253 SharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String())) 254 } 255 } 256} 257 258func PackageFuzzModule(ctx android.ModuleContext, fuzzPackagedModule fuzz.FuzzPackagedModule, pctx android.PackageContext) fuzz.FuzzPackagedModule { 259 fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Corpus) 260 builder := android.NewRuleBuilder(pctx, ctx) 261 intermediateDir := android.PathForModuleOut(ctx, "corpus") 262 for _, entry := range fuzzPackagedModule.Corpus { 263 builder.Command().Text("cp"). 264 Input(entry). 265 Output(intermediateDir.Join(ctx, entry.Base())) 266 } 267 builder.Build("copy_corpus", "copy corpus") 268 fuzzPackagedModule.CorpusIntermediateDir = intermediateDir 269 270 fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Data) 271 builder = android.NewRuleBuilder(pctx, ctx) 272 intermediateDir = android.PathForModuleOut(ctx, "data") 273 for _, entry := range fuzzPackagedModule.Data { 274 builder.Command().Text("cp"). 275 Input(entry). 276 Output(intermediateDir.Join(ctx, entry.Rel())) 277 } 278 builder.Build("copy_data", "copy data") 279 fuzzPackagedModule.DataIntermediateDir = intermediateDir 280 281 if fuzzPackagedModule.FuzzProperties.Dictionary != nil { 282 fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzzPackagedModule.FuzzProperties.Dictionary) 283 if fuzzPackagedModule.Dictionary.Ext() != ".dict" { 284 ctx.PropertyErrorf("dictionary", 285 "Fuzzer dictionary %q does not have '.dict' extension", 286 fuzzPackagedModule.Dictionary.String()) 287 } 288 } 289 290 if fuzzPackagedModule.FuzzProperties.Fuzz_config != nil { 291 configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json") 292 android.WriteFileRule(ctx, configPath, fuzzPackagedModule.FuzzProperties.Fuzz_config.String()) 293 fuzzPackagedModule.Config = configPath 294 } 295 return fuzzPackagedModule 296} 297 298func NewFuzzer(hod android.HostOrDeviceSupported) *Module { 299 module, binary := newBinary(hod, false) 300 baseInstallerPath := "fuzz" 301 302 binary.baseInstaller = NewBaseInstaller(baseInstallerPath, baseInstallerPath, InstallInData) 303 304 fuzzBin := &fuzzBinary{ 305 binaryDecorator: binary, 306 baseCompiler: NewBaseCompiler(), 307 } 308 module.compiler = fuzzBin 309 module.linker = fuzzBin 310 module.installer = fuzzBin 311 312 module.fuzzer.Properties.FuzzFramework = fuzz.LibFuzzer 313 314 // The fuzzer runtime is not present for darwin host modules, disable cc_fuzz modules when targeting darwin. 315 android.AddLoadHook(module, func(ctx android.LoadHookContext) { 316 317 extraProps := struct { 318 Sanitize struct { 319 Fuzzer *bool 320 } 321 Target struct { 322 Darwin struct { 323 Enabled *bool 324 } 325 Linux_bionic struct { 326 Enabled *bool 327 } 328 } 329 }{} 330 extraProps.Sanitize.Fuzzer = BoolPtr(true) 331 extraProps.Target.Darwin.Enabled = BoolPtr(false) 332 extraProps.Target.Linux_bionic.Enabled = BoolPtr(false) 333 ctx.AppendProperties(&extraProps) 334 335 targetFramework := fuzz.GetFramework(ctx, fuzz.Cc) 336 if !fuzz.IsValidFrameworkForModule(targetFramework, fuzz.Cc, fuzzBin.fuzzPackagedModule.FuzzProperties.Fuzzing_frameworks) { 337 ctx.Module().Disable() 338 return 339 } 340 341 if targetFramework == fuzz.AFL { 342 fuzzBin.baseCompiler.Properties.Srcs = append(fuzzBin.baseCompiler.Properties.Srcs, ":aflpp_driver", ":afl-compiler-rt") 343 module.fuzzer.Properties.FuzzFramework = fuzz.AFL 344 } 345 }) 346 347 return module 348} 349 350// Responsible for generating GNU Make rules that package fuzz targets into 351// their architecture & target/host specific zip file. 352type ccRustFuzzPackager struct { 353 fuzz.FuzzPackager 354 fuzzPackagingArchModules string 355 fuzzTargetSharedDepsInstallPairs string 356 allFuzzTargetsName string 357} 358 359func fuzzPackagingFactory() android.Singleton { 360 361 fuzzPackager := &ccRustFuzzPackager{ 362 fuzzPackagingArchModules: "SOONG_FUZZ_PACKAGING_ARCH_MODULES", 363 fuzzTargetSharedDepsInstallPairs: "FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS", 364 allFuzzTargetsName: "ALL_FUZZ_TARGETS", 365 } 366 return fuzzPackager 367} 368 369func (s *ccRustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) { 370 // Map between each architecture + host/device combination, and the files that 371 // need to be packaged (in the tuple of {source file, destination folder in 372 // archive}). 373 archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip) 374 375 // List of individual fuzz targets, so that 'make fuzz' also installs the targets 376 // to the correct output directories as well. 377 s.FuzzTargets = make(map[string]bool) 378 379 // Map tracking whether each shared library has an install rule to avoid duplicate install rules from 380 // multiple fuzzers that depend on the same shared library. 381 sharedLibraryInstalled := make(map[string]bool) 382 383 ctx.VisitAllModules(func(module android.Module) { 384 ccModule, ok := module.(LinkableInterface) 385 if !ok || ccModule.PreventInstall() { 386 return 387 } 388 389 // Discard non-fuzz targets. 390 if ok := fuzz.IsValid(ccModule.FuzzModuleStruct()); !ok { 391 return 392 } 393 394 sharedLibsInstallDirPrefix := "lib" 395 if !ccModule.IsFuzzModule() { 396 return 397 } 398 399 hostOrTargetString := "target" 400 if ccModule.Host() { 401 hostOrTargetString = "host" 402 } 403 404 fpm := fuzz.FuzzPackagedModule{} 405 if ok { 406 fpm = ccModule.FuzzPackagedModule() 407 } 408 409 intermediatePath := "fuzz" 410 411 archString := ccModule.Target().Arch.ArchType.String() 412 archDir := android.PathForIntermediates(ctx, intermediatePath, hostOrTargetString, archString) 413 archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()} 414 415 var files []fuzz.FileToZip 416 builder := android.NewRuleBuilder(pctx, ctx) 417 418 // Package the corpus, data, dict and config into a zipfile. 419 files = s.PackageArtifacts(ctx, module, fpm, archDir, builder) 420 421 // Package shared libraries 422 files = append(files, GetSharedLibsToZip(ccModule.FuzzSharedLibraries(), ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...) 423 424 // The executable. 425 files = append(files, fuzz.FileToZip{android.OutputFileForModule(ctx, ccModule, "unstripped"), ""}) 426 427 archDirs[archOs], ok = s.BuildZipFile(ctx, module, fpm, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs) 428 if !ok { 429 return 430 } 431 }) 432 433 s.CreateFuzzPackage(ctx, archDirs, fuzz.Cc, pctx) 434} 435 436func (s *ccRustFuzzPackager) MakeVars(ctx android.MakeVarsContext) { 437 packages := s.Packages.Strings() 438 sort.Strings(packages) 439 sort.Strings(s.FuzzPackager.SharedLibInstallStrings) 440 // TODO(mitchp): Migrate this to use MakeVarsContext::DistForGoal() when it's 441 // ready to handle phony targets created in Soong. In the meantime, this 442 // exports the phony 'fuzz' target and dependencies on packages to 443 // core/main.mk so that we can use dist-for-goals. 444 445 ctx.Strict(s.fuzzPackagingArchModules, strings.Join(packages, " ")) 446 447 ctx.Strict(s.fuzzTargetSharedDepsInstallPairs, 448 strings.Join(s.FuzzPackager.SharedLibInstallStrings, " ")) 449 450 // Preallocate the slice of fuzz targets to minimise memory allocations. 451 s.PreallocateSlice(ctx, s.allFuzzTargetsName) 452} 453 454// GetSharedLibsToZip finds and marks all the transiently-dependent shared libraries for 455// packaging. 456func GetSharedLibsToZip(sharedLibraries android.Paths, module LinkableInterface, s *fuzz.FuzzPackager, archString string, destinationPathPrefix string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip { 457 var files []fuzz.FileToZip 458 459 fuzzDir := "fuzz" 460 461 for _, library := range sharedLibraries { 462 files = append(files, fuzz.FileToZip{library, destinationPathPrefix}) 463 464 // For each architecture-specific shared library dependency, we need to 465 // install it to the output directory. Setup the install destination here, 466 // which will be used by $(copy-many-files) in the Make backend. 467 installDestination := SharedLibraryInstallLocation( 468 library, module.Host(), fuzzDir, archString) 469 if (*sharedLibraryInstalled)[installDestination] { 470 continue 471 } 472 (*sharedLibraryInstalled)[installDestination] = true 473 474 // Escape all the variables, as the install destination here will be called 475 // via. $(eval) in Make. 476 installDestination = strings.ReplaceAll( 477 installDestination, "$", "$$") 478 s.SharedLibInstallStrings = append(s.SharedLibInstallStrings, 479 library.String()+":"+installDestination) 480 481 // Ensure that on device, the library is also reinstalled to the /symbols/ 482 // dir. Symbolized DSO's are always installed to the device when fuzzing, but 483 // we want symbolization tools (like `stack`) to be able to find the symbols 484 // in $ANDROID_PRODUCT_OUT/symbols automagically. 485 if !module.Host() { 486 symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(library, fuzzDir, archString) 487 symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$") 488 s.SharedLibInstallStrings = append(s.SharedLibInstallStrings, 489 library.String()+":"+symbolsInstallDestination) 490 } 491 } 492 return files 493} 494 495// CollectAllSharedDependencies search over the provided module's dependencies using 496// VisitDirectDeps and WalkDeps to enumerate all shared library dependencies. 497// VisitDirectDeps is used first to avoid incorrectly using the core libraries (sanitizer 498// runtimes, libc, libdl, etc.) from a dependency. This may cause issues when dependencies 499// have explicit sanitizer tags, as we may get a dependency on an unsanitized libc, etc. 500func CollectAllSharedDependencies(ctx android.ModuleContext) (android.Paths, []android.Module) { 501 seen := make(map[string]bool) 502 recursed := make(map[string]bool) 503 deps := []android.Module{} 504 505 var sharedLibraries android.Paths 506 507 // Enumerate the first level of dependencies, as we discard all non-library 508 // modules in the BFS loop below. 509 ctx.VisitDirectDeps(func(dep android.Module) { 510 if !IsValidSharedDependency(dep) { 511 return 512 } 513 if seen[ctx.OtherModuleName(dep)] { 514 return 515 } 516 seen[ctx.OtherModuleName(dep)] = true 517 deps = append(deps, dep) 518 sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, dep, "unstripped")) 519 }) 520 521 ctx.WalkDeps(func(child, parent android.Module) bool { 522 if !IsValidSharedDependency(child) { 523 return false 524 } 525 if !seen[ctx.OtherModuleName(child)] { 526 seen[ctx.OtherModuleName(child)] = true 527 deps = append(deps, child) 528 sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, child, "unstripped")) 529 } 530 531 if recursed[ctx.OtherModuleName(child)] { 532 return false 533 } 534 recursed[ctx.OtherModuleName(child)] = true 535 return true 536 }) 537 538 return sharedLibraries, deps 539} 540