Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39b0bf03 authored by Ivan Lozano's avatar Ivan Lozano
Browse files

rust: Package shared libraries with fuzzer zips

Rust fuzzers were not packaging up their CC shared dependencies.
This would lead to fuzzers using the shared libraries included on
system, which may not be sanitized, leading to incorrect behavior.

This refactors the relevant code from CC and calls it from the Rust
build logic.

Bug: 202282599
Test: output rust fuzzer zip file includes shared dependencies.
Change-Id: I92196eb0141733797a67eae24f8e9aedea94c3bc
parent f7bc97ce
Loading
Loading
Loading
Loading
+55 −81
Original line number Original line Diff line number Diff line
@@ -79,54 +79,21 @@ func (fuzz *fuzzBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
	return flags
	return flags
}
}


// This function performs a breadth-first search over the provided module's
func UnstrippedOutputFile(module android.Module) android.Path {
// dependencies using `visitDirectDeps` to enumerate all shared library
	if mod, ok := module.(LinkableInterface); ok {
// dependencies. We require breadth-first expansion, as otherwise we may
		return mod.UnstrippedOutputFile()
// incorrectly use 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.SingletonContext, module android.Module) android.Paths {
	var fringe []android.Module

	seen := make(map[string]bool)

	// Enumerate the first level of dependencies, as we discard all non-library
	// modules in the BFS loop below.
	ctx.VisitDirectDeps(module, func(dep android.Module) {
		if isValidSharedDependency(dep) {
			fringe = append(fringe, dep)
		}
	})

	var sharedLibraries android.Paths

	for i := 0; i < len(fringe); i++ {
		module := fringe[i]
		if seen[module.Name()] {
			continue
		}
		seen[module.Name()] = true

		ccModule := module.(*Module)
		sharedLibraries = append(sharedLibraries, ccModule.UnstrippedOutputFile())
		ctx.VisitDirectDeps(module, func(dep android.Module) {
			if isValidSharedDependency(dep) && !seen[dep.Name()] {
				fringe = append(fringe, dep)
	}
	}
		})
	panic("UnstrippedOutputFile called on non-LinkableInterface module: " + module.Name())
}
}


	return sharedLibraries
// IsValidSharedDependency takes a module and determines if it is a unique shared library
}

// This function takes a module and determines if it is a unique shared library
// that should be installed in the fuzz target output directories. This function
// that should be installed in the fuzz target output directories. This function
// returns true, unless:
// returns true, unless:
//  - The module is not an installable shared library, or
//  - The module is not an installable shared library, or
//  - The module is a header or stub, or
//  - The module is a header or stub, or
//  - The module is a prebuilt and its source is available, or
//  - The module is a prebuilt and its source is available, or
//  - The module is a versioned member of an SDK snapshot.
//  - The module is a versioned member of an SDK snapshot.
func isValidSharedDependency(dependency android.Module) bool {
func IsValidSharedDependency(dependency android.Module) bool {
	// TODO(b/144090547): We should be parsing these modules using
	// TODO(b/144090547): We should be parsing these modules using
	// ModuleDependencyTag instead of the current brute-force checking.
	// ModuleDependencyTag instead of the current brute-force checking.


@@ -246,7 +213,7 @@ func (fuzz *fuzzBinary) install(ctx ModuleContext, file android.Path) {
		}
		}
		seen[child.Name()] = true
		seen[child.Name()] = true


		if isValidSharedDependency(child) {
		if IsValidSharedDependency(child) {
			sharedLibraries = append(sharedLibraries, child.(*Module).UnstrippedOutputFile())
			sharedLibraries = append(sharedLibraries, child.(*Module).UnstrippedOutputFile())
			return true
			return true
		}
		}
@@ -304,7 +271,6 @@ func NewFuzz(hod android.HostOrDeviceSupported) *Module {
// their architecture & target/host specific zip file.
// their architecture & target/host specific zip file.
type ccFuzzPackager struct {
type ccFuzzPackager struct {
	fuzz.FuzzPackager
	fuzz.FuzzPackager
	sharedLibInstallStrings []string
}
}


func fuzzPackagingFactory() android.Singleton {
func fuzzPackagingFactory() android.Singleton {
@@ -317,14 +283,14 @@ func (s *ccFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
	// archive}).
	// archive}).
	archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
	archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)


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

	// List of individual fuzz targets, so that 'make fuzz' also installs the targets
	// List of individual fuzz targets, so that 'make fuzz' also installs the targets
	// to the correct output directories as well.
	// to the correct output directories as well.
	s.FuzzTargets = make(map[string]bool)
	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) {
	ctx.VisitAllModules(func(module android.Module) {
		ccModule, ok := module.(*Module)
		ccModule, ok := module.(*Module)
		if !ok || ccModule.Properties.PreventInstall {
		if !ok || ccModule.Properties.PreventInstall {
@@ -351,7 +317,7 @@ func (s *ccFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}


		// Grab the list of required shared libraries.
		// Grab the list of required shared libraries.
		sharedLibraries := collectAllSharedDependencies(ctx, module)
		sharedLibraries := fuzz.CollectAllSharedDependencies(ctx, module, UnstrippedOutputFile, IsValidSharedDependency)


		var files []fuzz.FileToZip
		var files []fuzz.FileToZip
		builder := android.NewRuleBuilder(pctx, ctx)
		builder := android.NewRuleBuilder(pctx, ctx)
@@ -359,39 +325,8 @@ func (s *ccFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
		// Package the corpus, data, dict and config into a zipfile.
		// Package the corpus, data, dict and config into a zipfile.
		files = s.PackageArtifacts(ctx, module, fuzzModule.fuzzPackagedModule, archDir, builder)
		files = s.PackageArtifacts(ctx, module, fuzzModule.fuzzPackagedModule, archDir, builder)


		// Find and mark all the transiently-dependent shared libraries for
		// Package shared libraries
		// packaging.
		files = append(files, GetSharedLibsToZip(sharedLibraries, ccModule, &s.FuzzPackager, archString, &sharedLibraryInstalled)...)
		for _, library := range sharedLibraries {
			files = append(files, fuzz.FileToZip{library, "lib"})

			// 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(
				library, ccModule.Host(), 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 !ccModule.Host() {
				symbolsInstallDestination := sharedLibrarySymbolsInstallLocation(library, archString)
				symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
				s.sharedLibInstallStrings = append(s.sharedLibInstallStrings,
					library.String()+":"+symbolsInstallDestination)
			}
		}


		// The executable.
		// The executable.
		files = append(files, fuzz.FileToZip{ccModule.UnstrippedOutputFile(), ""})
		files = append(files, fuzz.FileToZip{ccModule.UnstrippedOutputFile(), ""})
@@ -409,15 +344,54 @@ func (s *ccFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
func (s *ccFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
func (s *ccFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
	packages := s.Packages.Strings()
	packages := s.Packages.Strings()
	sort.Strings(packages)
	sort.Strings(packages)
	sort.Strings(s.sharedLibInstallStrings)
	sort.Strings(s.FuzzPackager.SharedLibInstallStrings)
	// TODO(mitchp): Migrate this to use MakeVarsContext::DistForGoal() when it's
	// TODO(mitchp): Migrate this to use MakeVarsContext::DistForGoal() when it's
	// ready to handle phony targets created in Soong. In the meantime, this
	// ready to handle phony targets created in Soong. In the meantime, this
	// exports the phony 'fuzz' target and dependencies on packages to
	// exports the phony 'fuzz' target and dependencies on packages to
	// core/main.mk so that we can use dist-for-goals.
	// core/main.mk so that we can use dist-for-goals.
	ctx.Strict("SOONG_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
	ctx.Strict("SOONG_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
	ctx.Strict("FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
	ctx.Strict("FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
		strings.Join(s.sharedLibInstallStrings, " "))
		strings.Join(s.FuzzPackager.SharedLibInstallStrings, " "))


	// Preallocate the slice of fuzz targets to minimise memory allocations.
	// Preallocate the slice of fuzz targets to minimise memory allocations.
	s.PreallocateSlice(ctx, "ALL_FUZZ_TARGETS")
	s.PreallocateSlice(ctx, "ALL_FUZZ_TARGETS")
}
}

// GetSharedLibsToZip finds and marks all the transiently-dependent shared libraries for
// packaging.
func GetSharedLibsToZip(sharedLibraries android.Paths, module LinkableInterface, s *fuzz.FuzzPackager, archString string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
	var files []fuzz.FileToZip

	for _, library := range sharedLibraries {
		files = append(files, fuzz.FileToZip{library, "lib"})

		// 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(
			library, module.Host(), 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(library, archString)
			symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
			s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
				library.String()+":"+symbolsInstallDestination)
		}
	}
	return files
}
+1 −0
Original line number Original line Diff line number Diff line
@@ -110,6 +110,7 @@ type LinkableInterface interface {
	BaseModuleName() string
	BaseModuleName() string


	OutputFile() android.OptionalPath
	OutputFile() android.OptionalPath
	UnstrippedOutputFile() android.Path
	CoverageFiles() android.Paths
	CoverageFiles() android.Paths


	NonCcVariants() bool
	NonCcVariants() bool
+42 −2
Original line number Original line Diff line number Diff line
@@ -44,6 +44,7 @@ type FuzzModule struct {
type FuzzPackager struct {
type FuzzPackager struct {
	Packages                android.Paths
	Packages                android.Paths
	FuzzTargets             map[string]bool
	FuzzTargets             map[string]bool
	SharedLibInstallStrings []string
}
}


type FileToZip struct {
type FileToZip struct {
@@ -251,3 +252,42 @@ func (s *FuzzPackager) PreallocateSlice(ctx android.MakeVarsContext, targets str
	sort.Strings(fuzzTargets)
	sort.Strings(fuzzTargets)
	ctx.Strict(targets, strings.Join(fuzzTargets, " "))
	ctx.Strict(targets, strings.Join(fuzzTargets, " "))
}
}

// CollectAllSharedDependencies performs a breadth-first search over the provided module's
// dependencies using `visitDirectDeps` to enumerate all shared library
// dependencies. We require breadth-first expansion, as otherwise we may
// incorrectly use 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.SingletonContext, module android.Module, unstrippedOutputFile func(module android.Module) android.Path, isValidSharedDependency func(dependency android.Module) bool) android.Paths {
	var fringe []android.Module

	seen := make(map[string]bool)

	// Enumerate the first level of dependencies, as we discard all non-library
	// modules in the BFS loop below.
	ctx.VisitDirectDeps(module, func(dep android.Module) {
		if isValidSharedDependency(dep) {
			fringe = append(fringe, dep)
		}
	})

	var sharedLibraries android.Paths

	for i := 0; i < len(fringe); i++ {
		module := fringe[i]
		if seen[module.Name()] {
			continue
		}
		seen[module.Name()] = true

		sharedLibraries = append(sharedLibraries, unstrippedOutputFile(module))
		ctx.VisitDirectDeps(module, func(dep android.Module) {
			if isValidSharedDependency(dep) && !seen[dep.Name()] {
				fringe = append(fringe, dep)
			}
		})
	}

	return sharedLibraries
}
+10 −0
Original line number Original line Diff line number Diff line
@@ -111,6 +111,10 @@ func (s *rustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
	// List of individual fuzz targets.
	// List of individual fuzz targets.
	s.FuzzTargets = make(map[string]bool)
	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) {
	ctx.VisitAllModules(func(module android.Module) {
		// Discard non-fuzz targets.
		// Discard non-fuzz targets.
		rustModule, ok := module.(*Module)
		rustModule, ok := module.(*Module)
@@ -145,6 +149,12 @@ func (s *rustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
		// The executable.
		// The executable.
		files = append(files, fuzz.FileToZip{rustModule.unstrippedOutputFile.Path(), ""})
		files = append(files, fuzz.FileToZip{rustModule.unstrippedOutputFile.Path(), ""})


		// Grab the list of required shared libraries.
		sharedLibraries := fuzz.CollectAllSharedDependencies(ctx, module, cc.UnstrippedOutputFile, cc.IsValidSharedDependency)

		// Package shared libraries
		files = append(files, cc.GetSharedLibsToZip(sharedLibraries, rustModule, &s.FuzzPackager, archString, &sharedLibraryInstalled)...)

		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
		if !ok {
		if !ok {
			return
			return
+7 −0
Original line number Original line Diff line number Diff line
@@ -594,6 +594,13 @@ func (mod *Module) CcLibraryInterface() bool {
	return false
	return false
}
}


func (mod *Module) UnstrippedOutputFile() android.Path {
	if mod.unstrippedOutputFile.Valid() {
		return mod.unstrippedOutputFile.Path()
	}
	return nil
}

func (mod *Module) IncludeDirs() android.Paths {
func (mod *Module) IncludeDirs() android.Paths {
	if mod.compiler != nil {
	if mod.compiler != nil {
		if library, ok := mod.compiler.(*libraryDecorator); ok {
		if library, ok := mod.compiler.(*libraryDecorator); ok {