blob: 2474cbc9e9315a78f4331a86e961cf072ff8e727 [file] [log] [blame]
hamzeh41ad8812021-07-07 14:00:07 -07001// Copyright 2021 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
hamzehc0a671f2021-07-22 12:05:08 -070015package fuzz
hamzeh41ad8812021-07-07 14:00:07 -070016
17// This file contains the common code for compiling C/C++ and Rust fuzzers for Android.
18
19import (
hamzehc0a671f2021-07-22 12:05:08 -070020 "encoding/json"
hamzehe8a1bfa2022-06-21 12:22:06 -070021 "fmt"
hamzeh41ad8812021-07-07 14:00:07 -070022 "sort"
23 "strings"
24
hamzehc0a671f2021-07-22 12:05:08 -070025 "github.com/google/blueprint/proptools"
26
hamzeh41ad8812021-07-07 14:00:07 -070027 "android/soong/android"
28)
29
Cory Barkera1da26f2022-06-07 20:12:06 +000030type FuzzType string
hamzeh41ad8812021-07-07 14:00:07 -070031
32const (
Cory Barkera1da26f2022-06-07 20:12:06 +000033 Cc FuzzType = ""
34 Rust FuzzType = "rust"
35 Java FuzzType = "java"
36 AFL FuzzType = "AFL"
hamzeh41ad8812021-07-07 14:00:07 -070037)
38
hamzehc0a671f2021-07-22 12:05:08 -070039var BoolDefault = proptools.BoolDefault
40
hamzeh41ad8812021-07-07 14:00:07 -070041type FuzzModule struct {
42 android.ModuleBase
43 android.DefaultableModuleBase
44 android.ApexModuleBase
45}
46
47type FuzzPackager struct {
Ivan Lozano39b0bf02021-10-14 12:22:09 -040048 Packages android.Paths
49 FuzzTargets map[string]bool
50 SharedLibInstallStrings []string
Cory Barkera1da26f2022-06-07 20:12:06 +000051 FuzzType FuzzType
hamzeh41ad8812021-07-07 14:00:07 -070052}
53
54type FileToZip struct {
55 SourceFilePath android.Path
56 DestinationPathPrefix string
57}
58
59type ArchOs struct {
60 HostOrTarget string
61 Arch string
62 Dir string
63}
64
hamzehe8a1bfa2022-06-21 12:22:06 -070065type PrivilegedLevel string
66
67const (
68 // Environment with the most minimal permissions.
69 Constrained PrivilegedLevel = "Constrained"
70 // Typical execution environment running unprivileged code.
71 Unprivileged = "Unprivileged"
72 // May have access to elevated permissions.
73 Privileged = "Privileged"
74 // Trusted computing base.
75 Tcb = "TCB"
76 // Bootloader chain.
77 Bootloader = "Bootloader"
78 // Tusted execution environment.
79 Tee = "Tee"
80 // Secure enclave.
81 Se = "Se"
82 // Other.
83 Other = "Other"
84)
85
86func IsValidConfig(fuzzModule FuzzPackagedModule, moduleName string) bool {
87 var config = fuzzModule.FuzzProperties.Fuzz_config
88 if config != nil {
89 var level = PrivilegedLevel(config.Privilege_level)
90 if level != "" {
91 switch level {
92 case Constrained, Unprivileged, Privileged, Tcb, Bootloader, Tee, Se, Other:
93 return true
94 }
95 panic(fmt.Errorf("Invalid privileged level in fuzz config in %s", moduleName))
96 }
97 return true
98 } else {
99 return false
100 }
101}
102
hamzehc0a671f2021-07-22 12:05:08 -0700103type FuzzConfig struct {
104 // Email address of people to CC on bugs or contact about this fuzz target.
105 Cc []string `json:"cc,omitempty"`
hamzehe8a1bfa2022-06-21 12:22:06 -0700106 // A brief description of what the fuzzed code does.
107 Description string `json:"description,omitempty"`
108 // Can this code be triggered remotely or only locally.
hamzeh3c983d22022-07-26 14:19:22 -0700109 Remotely_accessible *bool `json:"remotely_accessible,omitempty"`
hamzehe8a1bfa2022-06-21 12:22:06 -0700110 // Is the fuzzed code host only, i.e. test frameworks or support utilities.
hamzeh3c983d22022-07-26 14:19:22 -0700111 Host_only *bool `json:"host_only,omitempty"`
hamzehe8a1bfa2022-06-21 12:22:06 -0700112 // Can third party/untrusted apps supply data to fuzzed code.
hamzeh3c983d22022-07-26 14:19:22 -0700113 Untrusted_data *bool `json:"untrusted_data,omitempty"`
hamzehe8a1bfa2022-06-21 12:22:06 -0700114 // Is the code being fuzzed in a privileged, constrained or any other
115 // context from:
116 // https://source.android.com/security/overview/updates-resources#context_types.
117 Privilege_level PrivilegedLevel `json:"privilege_level,omitempty"`
118 // Can the fuzzed code isolated or can be called by multiple users/processes.
hamzeh3c983d22022-07-26 14:19:22 -0700119 Isolated *bool `json:"users_isolation,omitempty"`
hamzehe8a1bfa2022-06-21 12:22:06 -0700120 // When code was relaeased or will be released.
121 Production_date string `json:"production_date,omitempty"`
122 // Prevents critical service functionality like phone calls, bluetooth, etc.
hamzeh3c983d22022-07-26 14:19:22 -0700123 Critical *bool `json:"critical,omitempty"`
hamzehc0a671f2021-07-22 12:05:08 -0700124 // Specify whether to enable continuous fuzzing on devices. Defaults to true.
125 Fuzz_on_haiku_device *bool `json:"fuzz_on_haiku_device,omitempty"`
126 // Specify whether to enable continuous fuzzing on host. Defaults to true.
127 Fuzz_on_haiku_host *bool `json:"fuzz_on_haiku_host,omitempty"`
128 // Component in Google's bug tracking system that bugs should be filed to.
129 Componentid *int64 `json:"componentid,omitempty"`
130 // Hotlists in Google's bug tracking system that bugs should be marked with.
131 Hotlists []string `json:"hotlists,omitempty"`
132 // Specify whether this fuzz target was submitted by a researcher. Defaults
133 // to false.
134 Researcher_submitted *bool `json:"researcher_submitted,omitempty"`
135 // Specify who should be acknowledged for CVEs in the Android Security
136 // Bulletin.
137 Acknowledgement []string `json:"acknowledgement,omitempty"`
138 // Additional options to be passed to libfuzzer when run in Haiku.
139 Libfuzzer_options []string `json:"libfuzzer_options,omitempty"`
140 // Additional options to be passed to HWASAN when running on-device in Haiku.
141 Hwasan_options []string `json:"hwasan_options,omitempty"`
142 // Additional options to be passed to HWASAN when running on host in Haiku.
143 Asan_options []string `json:"asan_options,omitempty"`
Muhammad Haseeb Ahmad7e744052022-03-25 22:50:53 +0000144 // If there's a Java fuzzer with JNI, a different version of Jazzer would
145 // need to be added to the fuzzer package than one without JNI
146 IsJni *bool `json:"is_jni,omitempty"`
hamzehc0a671f2021-07-22 12:05:08 -0700147}
148
149type FuzzProperties struct {
150 // Optional list of seed files to be installed to the fuzz target's output
151 // directory.
152 Corpus []string `android:"path"`
153 // Optional list of data files to be installed to the fuzz target's output
154 // directory. Directory structure relative to the module is preserved.
155 Data []string `android:"path"`
156 // Optional dictionary to be installed to the fuzz target's output directory.
157 Dictionary *string `android:"path"`
158 // Config for running the target on fuzzing infrastructure.
159 Fuzz_config *FuzzConfig
160}
161
hamzeh41ad8812021-07-07 14:00:07 -0700162type FuzzPackagedModule struct {
163 FuzzProperties FuzzProperties
164 Dictionary android.Path
165 Corpus android.Paths
166 CorpusIntermediateDir android.Path
167 Config android.Path
168 Data android.Paths
169 DataIntermediateDir android.Path
170}
171
172func IsValid(fuzzModule FuzzModule) bool {
173 // Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of
174 // fuzz targets we're going to package anyway.
175 if !fuzzModule.Enabled() || fuzzModule.InRamdisk() || fuzzModule.InVendorRamdisk() || fuzzModule.InRecovery() {
176 return false
177 }
178
179 // Discard modules that are in an unavailable namespace.
180 if !fuzzModule.ExportedToMake() {
181 return false
182 }
183
184 return true
185}
186
187func (s *FuzzPackager) PackageArtifacts(ctx android.SingletonContext, module android.Module, fuzzModule FuzzPackagedModule, archDir android.OutputPath, builder *android.RuleBuilder) []FileToZip {
188 // Package the corpora into a zipfile.
189 var files []FileToZip
190 if fuzzModule.Corpus != nil {
191 corpusZip := archDir.Join(ctx, module.Name()+"_seed_corpus.zip")
192 command := builder.Command().BuiltTool("soong_zip").
193 Flag("-j").
194 FlagWithOutput("-o ", corpusZip)
195 rspFile := corpusZip.ReplaceExtension(ctx, "rsp")
196 command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.Corpus)
197 files = append(files, FileToZip{corpusZip, ""})
198 }
199
200 // Package the data into a zipfile.
201 if fuzzModule.Data != nil {
202 dataZip := archDir.Join(ctx, module.Name()+"_data.zip")
203 command := builder.Command().BuiltTool("soong_zip").
204 FlagWithOutput("-o ", dataZip)
205 for _, f := range fuzzModule.Data {
206 intermediateDir := strings.TrimSuffix(f.String(), f.Rel())
207 command.FlagWithArg("-C ", intermediateDir)
208 command.FlagWithInput("-f ", f)
209 }
210 files = append(files, FileToZip{dataZip, ""})
211 }
212
213 // The dictionary.
214 if fuzzModule.Dictionary != nil {
215 files = append(files, FileToZip{fuzzModule.Dictionary, ""})
216 }
217
218 // Additional fuzz config.
hamzehe8a1bfa2022-06-21 12:22:06 -0700219 if fuzzModule.Config != nil && IsValidConfig(fuzzModule, module.Name()) {
hamzeh41ad8812021-07-07 14:00:07 -0700220 files = append(files, FileToZip{fuzzModule.Config, ""})
221 }
222
223 return files
224}
225
226func (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) {
227 fuzzZip := archDir.Join(ctx, module.Name()+".zip")
228
229 command := builder.Command().BuiltTool("soong_zip").
230 Flag("-j").
231 FlagWithOutput("-o ", fuzzZip)
232
233 for _, file := range files {
234 if file.DestinationPathPrefix != "" {
235 command.FlagWithArg("-P ", file.DestinationPathPrefix)
236 } else {
237 command.Flag("-P ''")
238 }
239 command.FlagWithInput("-f ", file.SourceFilePath)
240 }
241
242 builder.Build("create-"+fuzzZip.String(),
243 "Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
244
245 // Don't add modules to 'make haiku-rust' that are set to not be
246 // exported to the fuzzing infrastructure.
247 if config := fuzzModule.FuzzProperties.Fuzz_config; config != nil {
248 if strings.Contains(hostOrTargetString, "host") && !BoolDefault(config.Fuzz_on_haiku_host, true) {
249 return archDirs[archOs], false
250 } else if !BoolDefault(config.Fuzz_on_haiku_device, true) {
251 return archDirs[archOs], false
252 }
253 }
254
255 s.FuzzTargets[module.Name()] = true
256 archDirs[archOs] = append(archDirs[archOs], FileToZip{fuzzZip, ""})
257
258 return archDirs[archOs], true
259}
260
hamzehc0a671f2021-07-22 12:05:08 -0700261func (f *FuzzConfig) String() string {
262 b, err := json.Marshal(f)
263 if err != nil {
264 panic(err)
265 }
266
267 return string(b)
268}
269
Cory Barkera1da26f2022-06-07 20:12:06 +0000270func (s *FuzzPackager) CreateFuzzPackage(ctx android.SingletonContext, archDirs map[ArchOs][]FileToZip, fuzzType FuzzType, pctx android.PackageContext) {
hamzeh41ad8812021-07-07 14:00:07 -0700271 var archOsList []ArchOs
272 for archOs := range archDirs {
273 archOsList = append(archOsList, archOs)
274 }
275 sort.Slice(archOsList, func(i, j int) bool { return archOsList[i].Dir < archOsList[j].Dir })
276
277 for _, archOs := range archOsList {
278 filesToZip := archDirs[archOs]
279 arch := archOs.Arch
280 hostOrTarget := archOs.HostOrTarget
281 builder := android.NewRuleBuilder(pctx, ctx)
282 zipFileName := "fuzz-" + hostOrTarget + "-" + arch + ".zip"
Cory Barkera1da26f2022-06-07 20:12:06 +0000283 if fuzzType == Rust {
hamzeh41ad8812021-07-07 14:00:07 -0700284 zipFileName = "fuzz-rust-" + hostOrTarget + "-" + arch + ".zip"
285 }
Cory Barkera1da26f2022-06-07 20:12:06 +0000286 if fuzzType == Java {
Muhammad Haseeb Ahmade3803102022-01-10 21:37:07 +0000287 zipFileName = "fuzz-java-" + hostOrTarget + "-" + arch + ".zip"
288 }
Cory Barkera1da26f2022-06-07 20:12:06 +0000289 if fuzzType == AFL {
290 zipFileName = "fuzz-afl-" + hostOrTarget + "-" + arch + ".zip"
291 }
hamzeh41ad8812021-07-07 14:00:07 -0700292 outputFile := android.PathForOutput(ctx, zipFileName)
293
294 s.Packages = append(s.Packages, outputFile)
295
296 command := builder.Command().BuiltTool("soong_zip").
297 Flag("-j").
298 FlagWithOutput("-o ", outputFile).
299 Flag("-L 0") // No need to try and re-compress the zipfiles.
300
301 for _, fileToZip := range filesToZip {
hamzeh41ad8812021-07-07 14:00:07 -0700302 if fileToZip.DestinationPathPrefix != "" {
303 command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix)
304 } else {
305 command.Flag("-P ''")
306 }
307 command.FlagWithInput("-f ", fileToZip.SourceFilePath)
308
309 }
310 builder.Build("create-fuzz-package-"+arch+"-"+hostOrTarget,
311 "Create fuzz target packages for "+arch+"-"+hostOrTarget)
312 }
313}
314
315func (s *FuzzPackager) PreallocateSlice(ctx android.MakeVarsContext, targets string) {
316 fuzzTargets := make([]string, 0, len(s.FuzzTargets))
317 for target, _ := range s.FuzzTargets {
318 fuzzTargets = append(fuzzTargets, target)
319 }
Cory Barkera1da26f2022-06-07 20:12:06 +0000320
hamzeh41ad8812021-07-07 14:00:07 -0700321 sort.Strings(fuzzTargets)
322 ctx.Strict(targets, strings.Join(fuzzTargets, " "))
323}
Ivan Lozano39b0bf02021-10-14 12:22:09 -0400324
325// CollectAllSharedDependencies performs a breadth-first search over the provided module's
326// dependencies using `visitDirectDeps` to enumerate all shared library
327// dependencies. We require breadth-first expansion, as otherwise we may
328// incorrectly use the core libraries (sanitizer runtimes, libc, libdl, etc.)
329// from a dependency. This may cause issues when dependencies have explicit
330// sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
331func CollectAllSharedDependencies(ctx android.SingletonContext, module android.Module, unstrippedOutputFile func(module android.Module) android.Path, isValidSharedDependency func(dependency android.Module) bool) android.Paths {
332 var fringe []android.Module
333
334 seen := make(map[string]bool)
335
336 // Enumerate the first level of dependencies, as we discard all non-library
337 // modules in the BFS loop below.
338 ctx.VisitDirectDeps(module, func(dep android.Module) {
339 if isValidSharedDependency(dep) {
340 fringe = append(fringe, dep)
341 }
342 })
343
344 var sharedLibraries android.Paths
345
346 for i := 0; i < len(fringe); i++ {
347 module := fringe[i]
348 if seen[module.Name()] {
349 continue
350 }
351 seen[module.Name()] = true
352
353 sharedLibraries = append(sharedLibraries, unstrippedOutputFile(module))
354 ctx.VisitDirectDeps(module, func(dep android.Module) {
355 if isValidSharedDependency(dep) && !seen[dep.Name()] {
356 fringe = append(fringe, dep)
357 }
358 })
359 }
360
361 return sharedLibraries
362}