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