123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604 |
- // Copyright 2021 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 fuzz
- // This file contains the common code for compiling C/C++ and Rust fuzzers for Android.
- import (
- "encoding/json"
- "fmt"
- "sort"
- "strings"
- "github.com/google/blueprint/proptools"
- "android/soong/android"
- )
- type Lang string
- const (
- Cc Lang = "cc"
- Rust Lang = "rust"
- Java Lang = "java"
- )
- type Framework string
- const (
- AFL Framework = "afl"
- LibFuzzer Framework = "libfuzzer"
- Jazzer Framework = "jazzer"
- UnknownFramework Framework = "unknownframework"
- )
- var BoolDefault = proptools.BoolDefault
- type FuzzModule struct {
- android.ModuleBase
- android.DefaultableModuleBase
- android.ApexModuleBase
- }
- type FuzzPackager struct {
- Packages android.Paths
- FuzzTargets map[string]bool
- SharedLibInstallStrings []string
- }
- type FileToZip struct {
- SourceFilePath android.Path
- DestinationPathPrefix string
- DestinationPath string
- }
- type ArchOs struct {
- HostOrTarget string
- Arch string
- Dir string
- }
- type Vector string
- const (
- unknown_access_vector Vector = "unknown_access_vector"
- // The code being fuzzed is reachable from a remote source, or using data
- // provided by a remote source. For example: media codecs process media files
- // from the internet, SMS processing handles remote message data.
- // See
- // https://source.android.com/docs/security/overview/updates-resources#local-vs-remote
- // for an explanation of what's considered "remote."
- remote = "remote"
- // The code being fuzzed can only be reached locally, such as from an
- // installed app. As an example, if it's fuzzing a Binder interface, it's
- // assumed that you'd need a local app to make arbitrary Binder calls.
- // And the app that's calling the fuzzed code does not require any privileges;
- // any 3rd party app could make these calls.
- local_no_privileges_required = "local_no_privileges_required"
- // The code being fuzzed can only be called locally, and the calling process
- // requires additional permissions that prevent arbitrary 3rd party apps from
- // calling the code. For instance: this requires a privileged or signature
- // permission to reach, or SELinux restrictions prevent the untrusted_app
- // domain from calling it.
- local_privileges_required = "local_privileges_required"
- // The code is only callable on a PC host, not on a production Android device.
- // For instance, this is fuzzing code used during the build process, or
- // tooling that does not exist on a user's actual Android device.
- host_access = "host_access"
- // The code being fuzzed is only reachable if the user has enabled Developer
- // Options, or has enabled a persistent Developer Options setting.
- local_with_developer_options = "local_with_developer_options"
- )
- func (vector Vector) isValidVector() bool {
- switch vector {
- case "",
- unknown_access_vector,
- remote,
- local_no_privileges_required,
- local_privileges_required,
- host_access,
- local_with_developer_options:
- return true
- }
- return false
- }
- type ServicePrivilege string
- const (
- unknown_service_privilege ServicePrivilege = "unknown_service_privilege"
- // The code being fuzzed runs on a Secure Element. This has access to some
- // of the most privileged data on the device, such as authentication keys.
- // Not all devices have a Secure Element.
- secure_element = "secure_element"
- // The code being fuzzed runs in the TEE. The TEE is designed to be resistant
- // to a compromised kernel, and stores sensitive data.
- trusted_execution = "trusted_execution"
- // The code being fuzzed has privileges beyond what arbitrary 3rd party apps
- // have. For instance, it's running as the System UID, or it's in an SELinux
- // domain that's able to perform calls that can't be made by 3rd party apps.
- privileged = "privileged"
- // The code being fuzzed is equivalent to a 3rd party app. It runs in the
- // untrusted_app SELinux domain, or it only has privileges that are equivalent
- // to what a 3rd party app could have.
- unprivileged = "unprivileged"
- // The code being fuzzed is significantly constrained, and even if it's
- // compromised, it has significant restrictions that prevent it from
- // performing most actions. This is significantly more restricted than
- // UNPRIVILEGED. An example is the isolatedProcess=true setting in a 3rd
- // party app. Or a process that's very restricted by SELinux, such as
- // anything in the mediacodec SELinux domain.
- constrained = "constrained"
- // The code being fuzzed always has Negligible Security Impact. Even
- // arbitrary out of bounds writes and full code execution would not be
- // considered a security vulnerability. This typically only makes sense if
- // FuzzedCodeUsage is set to FUTURE_VERSION or EXPERIMENTAL, and if
- // AutomaticallyRouteTo is set to ALWAYS_NSI.
- nsi = "nsi"
- // The code being fuzzed only runs on a PC host, not on a production Android
- // device. For instance, the fuzzer is fuzzing code used during the build
- // process, or tooling that does not exist on a user's actual Android device.
- host_only = "host_only"
- )
- func (service_privilege ServicePrivilege) isValidServicePrivilege() bool {
- switch service_privilege {
- case "",
- unknown_service_privilege,
- secure_element,
- trusted_execution,
- privileged,
- unprivileged,
- constrained,
- nsi,
- host_only:
- return true
- }
- return false
- }
- type UsePlatformLibs string
- const (
- unknown_use_platform_libs UsePlatformLibs = "unknown_use_platform_libs"
- // Use the native libraries on the device, typically in /system directory
- use_platform_libs = "use_platform_libs"
- // Do not use any native libraries (ART will not be initialized)
- use_none = "use_none"
- )
- func (use_platform_libs UsePlatformLibs) isValidUsePlatformLibs() bool {
- switch use_platform_libs {
- case "",
- unknown_use_platform_libs,
- use_platform_libs,
- use_none:
- return true
- }
- return false
- }
- type UserData string
- const (
- unknown_user_data UserData = "unknown_user_data"
- // The process being fuzzed only handles data from a single user, or from a
- // single process or app. It's possible the process shuts down before
- // handling data from another user/process/app, or it's possible the process
- // only ever handles one user's/process's/app's data. As an example, some
- // print spooler processes are started for a single document and terminate
- // when done, so each instance only handles data from a single user/app.
- single_user = "single_user"
- // The process handles data from multiple users, or from multiple other apps
- // or processes. Media processes, for instance, can handle media requests
- // from multiple different apps without restarting. Wi-Fi and network
- // processes handle data from multiple users, and processes, and apps.
- multi_user = "multi_user"
- )
- func (user_data UserData) isValidUserData() bool {
- switch user_data {
- case "",
- unknown_user_data,
- single_user,
- multi_user:
- return true
- }
- return false
- }
- type FuzzedCodeUsage string
- const (
- undefined FuzzedCodeUsage = "undefined"
- unknown = "unknown"
- // The code being fuzzed exists in a shipped version of Android and runs on
- // devices in production.
- shipped = "shipped"
- // The code being fuzzed is not yet in a shipping version of Android, but it
- // will be at some point in the future.
- future_version = "future_version"
- // The code being fuzzed is not in a shipping version of Android, and there
- // are no plans to ship it in the future.
- experimental = "experimental"
- )
- func (fuzzed_code_usage FuzzedCodeUsage) isValidFuzzedCodeUsage() bool {
- switch fuzzed_code_usage {
- case "",
- undefined,
- unknown,
- shipped,
- future_version,
- experimental:
- return true
- }
- return false
- }
- type AutomaticallyRouteTo string
- const (
- undefined_routing AutomaticallyRouteTo = "undefined_routing"
- // Automatically route this to the Android Automotive security team for
- // assessment.
- android_automotive = "android_automotive"
- // This should not be used in fuzzer configurations. It is used internally
- // by Severity Assigner to flag memory leak reports.
- memory_leak = "memory_leak"
- // Route this vulnerability to our Ittiam vendor team for assessment.
- ittiam = "ittiam"
- // Reports from this fuzzer are always NSI (see the NSI ServicePrivilegeEnum
- // value for additional context). It is not possible for this code to ever
- // have a security vulnerability.
- always_nsi = "always_nsi"
- // Route this vulnerability to AIDL team for assessment.
- aidl = "aidl"
- )
- func (automatically_route_to AutomaticallyRouteTo) isValidAutomaticallyRouteTo() bool {
- switch automatically_route_to {
- case "",
- undefined_routing,
- android_automotive,
- memory_leak,
- ittiam,
- always_nsi,
- aidl:
- return true
- }
- return false
- }
- func IsValidConfig(fuzzModule FuzzPackagedModule, moduleName string) bool {
- var config = fuzzModule.FuzzProperties.Fuzz_config
- if config != nil {
- if !config.Vector.isValidVector() {
- panic(fmt.Errorf("Invalid vector in fuzz config in %s", moduleName))
- }
- if !config.Service_privilege.isValidServicePrivilege() {
- panic(fmt.Errorf("Invalid service_privilege in fuzz config in %s", moduleName))
- }
- if !config.Users.isValidUserData() {
- panic(fmt.Errorf("Invalid users (user_data) in fuzz config in %s", moduleName))
- }
- if !config.Fuzzed_code_usage.isValidFuzzedCodeUsage() {
- panic(fmt.Errorf("Invalid fuzzed_code_usage in fuzz config in %s", moduleName))
- }
- if !config.Automatically_route_to.isValidAutomaticallyRouteTo() {
- panic(fmt.Errorf("Invalid automatically_route_to in fuzz config in %s", moduleName))
- }
- if !config.Use_platform_libs.isValidUsePlatformLibs() {
- panic(fmt.Errorf("Invalid use_platform_libs in fuzz config in %s", moduleName))
- }
- }
- return true
- }
- type FuzzConfig struct {
- // Email address of people to CC on bugs or contact about this fuzz target.
- Cc []string `json:"cc,omitempty"`
- // A brief description of what the fuzzed code does.
- Description string `json:"description,omitempty"`
- // Whether the code being fuzzed is remotely accessible or requires privileges
- // to access locally.
- Vector Vector `json:"vector,omitempty"`
- // How privileged the service being fuzzed is.
- Service_privilege ServicePrivilege `json:"service_privilege,omitempty"`
- // Whether the service being fuzzed handles data from multiple users or only
- // a single one.
- Users UserData `json:"users,omitempty"`
- // Specifies the use state of the code being fuzzed. This state factors into
- // how an issue is handled.
- Fuzzed_code_usage FuzzedCodeUsage `json:"fuzzed_code_usage,omitempty"`
- // Comment describing how we came to these settings for this fuzzer.
- Config_comment string
- // Which team to route this to, if it should be routed automatically.
- Automatically_route_to AutomaticallyRouteTo `json:"automatically_route_to,omitempty"`
- // Can third party/untrusted apps supply data to fuzzed code.
- Untrusted_data *bool `json:"untrusted_data,omitempty"`
- // When code was released or will be released.
- Production_date string `json:"production_date,omitempty"`
- // Prevents critical service functionality like phone calls, bluetooth, etc.
- Critical *bool `json:"critical,omitempty"`
- // Specify whether to enable continuous fuzzing on devices. Defaults to true.
- Fuzz_on_haiku_device *bool `json:"fuzz_on_haiku_device,omitempty"`
- // Specify whether to enable continuous fuzzing on host. Defaults to true.
- Fuzz_on_haiku_host *bool `json:"fuzz_on_haiku_host,omitempty"`
- // Component in Google's bug tracking system that bugs should be filed to.
- Componentid *int64 `json:"componentid,omitempty"`
- // Hotlist(s) in Google's bug tracking system that bugs should be marked with.
- Hotlists []string `json:"hotlists,omitempty"`
- // Specify whether this fuzz target was submitted by a researcher. Defaults
- // to false.
- Researcher_submitted *bool `json:"researcher_submitted,omitempty"`
- // Specify who should be acknowledged for CVEs in the Android Security
- // Bulletin.
- Acknowledgement []string `json:"acknowledgement,omitempty"`
- // Additional options to be passed to libfuzzer when run in Haiku.
- Libfuzzer_options []string `json:"libfuzzer_options,omitempty"`
- // Additional options to be passed to HWASAN when running on-device in Haiku.
- 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"`
- // List of modules for monitoring coverage drops in directories (e.g. "libicu")
- Target_modules []string `json:"target_modules,omitempty"`
- // Specifies a bug assignee to replace default ISE assignment
- Triage_assignee string `json:"triage_assignee,omitempty"`
- // Specifies libs used to initialize ART (java only, 'use_none' for no initialization)
- Use_platform_libs UsePlatformLibs `json:"use_platform_libs,omitempty"`
- // Specifies whether fuzz target should check presubmitted code changes for crashes.
- // Defaults to false.
- Use_for_presubmit *bool `json:"use_for_presubmit,omitempty"`
- }
- type FuzzFrameworks struct {
- Afl *bool
- Libfuzzer *bool
- Jazzer *bool
- }
- type FuzzProperties struct {
- // Optional list of seed files to be installed to the fuzz target's output
- // directory.
- Corpus []string `android:"path"`
- // Optional list of data files to be installed to the fuzz target's output
- // directory. Directory structure relative to the module is preserved.
- Data []string `android:"path"`
- // Optional dictionary to be installed to the fuzz target's output directory.
- Dictionary *string `android:"path"`
- // Define the fuzzing frameworks this fuzz target can be built for. If
- // empty then the fuzz target will be available to be built for all fuzz
- // frameworks available
- Fuzzing_frameworks *FuzzFrameworks
- // Config for running the target on fuzzing infrastructure.
- Fuzz_config *FuzzConfig
- }
- type FuzzPackagedModule struct {
- FuzzProperties FuzzProperties
- Dictionary android.Path
- Corpus android.Paths
- CorpusIntermediateDir android.Path
- Config android.Path
- Data android.Paths
- DataIntermediateDir android.Path
- }
- func GetFramework(ctx android.LoadHookContext, lang Lang) Framework {
- framework := ctx.Config().Getenv("FUZZ_FRAMEWORK")
- if lang == Cc {
- switch strings.ToLower(framework) {
- case "":
- return LibFuzzer
- case "libfuzzer":
- return LibFuzzer
- case "afl":
- return AFL
- }
- } else if lang == Rust {
- return LibFuzzer
- } else if lang == Java {
- return Jazzer
- }
- ctx.ModuleErrorf(fmt.Sprintf("%s is not a valid fuzzing framework for %s", framework, lang))
- return UnknownFramework
- }
- func IsValidFrameworkForModule(targetFramework Framework, lang Lang, moduleFrameworks *FuzzFrameworks) bool {
- if targetFramework == UnknownFramework {
- return false
- }
- if moduleFrameworks == nil {
- return true
- }
- switch targetFramework {
- case LibFuzzer:
- return proptools.BoolDefault(moduleFrameworks.Libfuzzer, true)
- case AFL:
- return proptools.BoolDefault(moduleFrameworks.Afl, true)
- case Jazzer:
- return proptools.BoolDefault(moduleFrameworks.Jazzer, true)
- default:
- panic("%s is not supported as a fuzz framework")
- }
- }
- func IsValid(fuzzModule FuzzModule) bool {
- // Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of
- // fuzz targets we're going to package anyway.
- if !fuzzModule.Enabled() || fuzzModule.InRamdisk() || fuzzModule.InVendorRamdisk() || fuzzModule.InRecovery() {
- return false
- }
- // Discard modules that are in an unavailable namespace.
- if !fuzzModule.ExportedToMake() {
- return false
- }
- return true
- }
- func (s *FuzzPackager) PackageArtifacts(ctx android.SingletonContext, module android.Module, fuzzModule FuzzPackagedModule, archDir android.OutputPath, builder *android.RuleBuilder) []FileToZip {
- // Package the corpora into a zipfile.
- var files []FileToZip
- if fuzzModule.Corpus != nil {
- corpusZip := archDir.Join(ctx, module.Name()+"_seed_corpus.zip")
- command := builder.Command().BuiltTool("soong_zip").
- Flag("-j").
- FlagWithOutput("-o ", corpusZip)
- rspFile := corpusZip.ReplaceExtension(ctx, "rsp")
- command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.Corpus)
- files = append(files, FileToZip{SourceFilePath: corpusZip})
- }
- // Package the data into a zipfile.
- if fuzzModule.Data != nil {
- dataZip := archDir.Join(ctx, module.Name()+"_data.zip")
- command := builder.Command().BuiltTool("soong_zip").
- FlagWithOutput("-o ", dataZip)
- for _, f := range fuzzModule.Data {
- intermediateDir := strings.TrimSuffix(f.String(), f.Rel())
- command.FlagWithArg("-C ", intermediateDir)
- command.FlagWithInput("-f ", f)
- }
- files = append(files, FileToZip{SourceFilePath: dataZip})
- }
- // The dictionary.
- if fuzzModule.Dictionary != nil {
- files = append(files, FileToZip{SourceFilePath: fuzzModule.Dictionary})
- }
- // Additional fuzz config.
- if fuzzModule.Config != nil && IsValidConfig(fuzzModule, module.Name()) {
- files = append(files, FileToZip{SourceFilePath: fuzzModule.Config})
- }
- return files
- }
- func (s *FuzzPackager) BuildZipFile(ctx android.SingletonContext, module android.Module, fuzzModule FuzzPackagedModule, files []FileToZip, builder *android.RuleBuilder, archDir android.OutputPath, archString string, hostOrTargetString string, archOs ArchOs, archDirs map[ArchOs][]FileToZip) ([]FileToZip, bool) {
- fuzzZip := archDir.Join(ctx, module.Name()+".zip")
- command := builder.Command().BuiltTool("soong_zip").
- Flag("-j").
- FlagWithOutput("-o ", fuzzZip)
- for _, file := range files {
- if file.DestinationPathPrefix != "" {
- command.FlagWithArg("-P ", file.DestinationPathPrefix)
- } else {
- command.Flag("-P ''")
- }
- if file.DestinationPath != "" {
- command.FlagWithArg("-e ", file.DestinationPath)
- }
- command.FlagWithInput("-f ", file.SourceFilePath)
- }
- builder.Build("create-"+fuzzZip.String(),
- "Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
- // Don't add modules to 'make haiku-rust' that are set to not be
- // exported to the fuzzing infrastructure.
- if config := fuzzModule.FuzzProperties.Fuzz_config; config != nil {
- if strings.Contains(hostOrTargetString, "host") && !BoolDefault(config.Fuzz_on_haiku_host, true) {
- return archDirs[archOs], false
- } else if !BoolDefault(config.Fuzz_on_haiku_device, true) {
- return archDirs[archOs], false
- }
- }
- s.FuzzTargets[module.Name()] = true
- archDirs[archOs] = append(archDirs[archOs], FileToZip{SourceFilePath: fuzzZip})
- return archDirs[archOs], true
- }
- func (f *FuzzConfig) String() string {
- b, err := json.Marshal(f)
- if err != nil {
- panic(err)
- }
- return string(b)
- }
- func (s *FuzzPackager) CreateFuzzPackage(ctx android.SingletonContext, archDirs map[ArchOs][]FileToZip, fuzzType Lang, pctx android.PackageContext) {
- var archOsList []ArchOs
- for archOs := range archDirs {
- archOsList = append(archOsList, archOs)
- }
- sort.Slice(archOsList, func(i, j int) bool { return archOsList[i].Dir < archOsList[j].Dir })
- for _, archOs := range archOsList {
- filesToZip := archDirs[archOs]
- arch := archOs.Arch
- hostOrTarget := archOs.HostOrTarget
- builder := android.NewRuleBuilder(pctx, ctx)
- zipFileName := "fuzz-" + hostOrTarget + "-" + arch + ".zip"
- if fuzzType == Rust {
- zipFileName = "fuzz-rust-" + hostOrTarget + "-" + arch + ".zip"
- }
- if fuzzType == Java {
- zipFileName = "fuzz-java-" + hostOrTarget + "-" + arch + ".zip"
- }
- outputFile := android.PathForOutput(ctx, zipFileName)
- s.Packages = append(s.Packages, outputFile)
- command := builder.Command().BuiltTool("soong_zip").
- Flag("-j").
- FlagWithOutput("-o ", outputFile).
- Flag("-L 0") // No need to try and re-compress the zipfiles.
- for _, fileToZip := range filesToZip {
- if fileToZip.DestinationPathPrefix != "" {
- command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix)
- } else {
- command.Flag("-P ''")
- }
- command.FlagWithInput("-f ", fileToZip.SourceFilePath)
- }
- builder.Build("create-fuzz-package-"+arch+"-"+hostOrTarget,
- "Create fuzz target packages for "+arch+"-"+hostOrTarget)
- }
- }
- func (s *FuzzPackager) PreallocateSlice(ctx android.MakeVarsContext, targets string) {
- fuzzTargets := make([]string, 0, len(s.FuzzTargets))
- for target, _ := range s.FuzzTargets {
- fuzzTargets = append(fuzzTargets, target)
- }
- sort.Strings(fuzzTargets)
- ctx.Strict(targets, strings.Join(fuzzTargets, " "))
- }
|