blob: 801176e3aa5402d4898db58a069f2fb28db22f08 [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 Barker9cfcf6d2022-07-22 17:22:02 +000030type Lang string
hamzeh41ad8812021-07-07 14:00:07 -070031
32const (
Cory Barker9cfcf6d2022-07-22 17:22:02 +000033 Cc Lang = "cc"
34 Rust Lang = "rust"
35 Java Lang = "java"
36)
37
38type Framework string
39
40const (
41 AFL Framework = "afl"
42 LibFuzzer Framework = "libfuzzer"
43 Jazzer Framework = "jazzer"
44 UnknownFramework Framework = "unknownframework"
hamzeh41ad8812021-07-07 14:00:07 -070045)
46
hamzehc0a671f2021-07-22 12:05:08 -070047var BoolDefault = proptools.BoolDefault
48
hamzeh41ad8812021-07-07 14:00:07 -070049type FuzzModule struct {
50 android.ModuleBase
51 android.DefaultableModuleBase
52 android.ApexModuleBase
53}
54
55type FuzzPackager struct {
Ivan Lozano39b0bf02021-10-14 12:22:09 -040056 Packages android.Paths
57 FuzzTargets map[string]bool
58 SharedLibInstallStrings []string
hamzeh41ad8812021-07-07 14:00:07 -070059}
60
61type FileToZip struct {
62 SourceFilePath android.Path
63 DestinationPathPrefix string
64}
65
66type ArchOs struct {
67 HostOrTarget string
68 Arch string
69 Dir string
70}
71
hamzehe8a1bfa2022-06-21 12:22:06 -070072type PrivilegedLevel string
73
74const (
75 // Environment with the most minimal permissions.
76 Constrained PrivilegedLevel = "Constrained"
77 // Typical execution environment running unprivileged code.
78 Unprivileged = "Unprivileged"
79 // May have access to elevated permissions.
80 Privileged = "Privileged"
81 // Trusted computing base.
82 Tcb = "TCB"
83 // Bootloader chain.
84 Bootloader = "Bootloader"
85 // Tusted execution environment.
86 Tee = "Tee"
87 // Secure enclave.
88 Se = "Se"
89 // Other.
90 Other = "Other"
91)
92
93func IsValidConfig(fuzzModule FuzzPackagedModule, moduleName string) bool {
94 var config = fuzzModule.FuzzProperties.Fuzz_config
95 if config != nil {
96 var level = PrivilegedLevel(config.Privilege_level)
97 if level != "" {
98 switch level {
99 case Constrained, Unprivileged, Privileged, Tcb, Bootloader, Tee, Se, Other:
100 return true
101 }
102 panic(fmt.Errorf("Invalid privileged level in fuzz config in %s", moduleName))
103 }
104 return true
105 } else {
106 return false
107 }
108}
109
hamzehc0a671f2021-07-22 12:05:08 -0700110type FuzzConfig struct {
111 // Email address of people to CC on bugs or contact about this fuzz target.
112 Cc []string `json:"cc,omitempty"`
hamzehe8a1bfa2022-06-21 12:22:06 -0700113 // A brief description of what the fuzzed code does.
114 Description string `json:"description,omitempty"`
115 // Can this code be triggered remotely or only locally.
hamzeh3c983d22022-07-26 14:19:22 -0700116 Remotely_accessible *bool `json:"remotely_accessible,omitempty"`
hamzehe8a1bfa2022-06-21 12:22:06 -0700117 // Is the fuzzed code host only, i.e. test frameworks or support utilities.
hamzeh3c983d22022-07-26 14:19:22 -0700118 Host_only *bool `json:"host_only,omitempty"`
hamzehe8a1bfa2022-06-21 12:22:06 -0700119 // Can third party/untrusted apps supply data to fuzzed code.
hamzeh3c983d22022-07-26 14:19:22 -0700120 Untrusted_data *bool `json:"untrusted_data,omitempty"`
hamzehe8a1bfa2022-06-21 12:22:06 -0700121 // Is the code being fuzzed in a privileged, constrained or any other
122 // context from:
123 // https://source.android.com/security/overview/updates-resources#context_types.
124 Privilege_level PrivilegedLevel `json:"privilege_level,omitempty"`
Jon Bottarinia0b44cb2022-10-19 03:13:14 +0000125 // Is the fuzzed code isolated or can it be called by multiple users/processes.
hamzeh3c983d22022-07-26 14:19:22 -0700126 Isolated *bool `json:"users_isolation,omitempty"`
Jon Bottarinia0b44cb2022-10-19 03:13:14 +0000127 // When code was released or will be released.
hamzehe8a1bfa2022-06-21 12:22:06 -0700128 Production_date string `json:"production_date,omitempty"`
129 // Prevents critical service functionality like phone calls, bluetooth, etc.
hamzeh3c983d22022-07-26 14:19:22 -0700130 Critical *bool `json:"critical,omitempty"`
hamzehc0a671f2021-07-22 12:05:08 -0700131 // Specify whether to enable continuous fuzzing on devices. Defaults to true.
132 Fuzz_on_haiku_device *bool `json:"fuzz_on_haiku_device,omitempty"`
133 // Specify whether to enable continuous fuzzing on host. Defaults to true.
134 Fuzz_on_haiku_host *bool `json:"fuzz_on_haiku_host,omitempty"`
135 // Component in Google's bug tracking system that bugs should be filed to.
136 Componentid *int64 `json:"componentid,omitempty"`
Jon Bottarinia0b44cb2022-10-19 03:13:14 +0000137 // Hotlist(s) in Google's bug tracking system that bugs should be marked with.
hamzehc0a671f2021-07-22 12:05:08 -0700138 Hotlists []string `json:"hotlists,omitempty"`
139 // Specify whether this fuzz target was submitted by a researcher. Defaults
140 // to false.
141 Researcher_submitted *bool `json:"researcher_submitted,omitempty"`
142 // Specify who should be acknowledged for CVEs in the Android Security
143 // Bulletin.
144 Acknowledgement []string `json:"acknowledgement,omitempty"`
145 // Additional options to be passed to libfuzzer when run in Haiku.
146 Libfuzzer_options []string `json:"libfuzzer_options,omitempty"`
147 // Additional options to be passed to HWASAN when running on-device in Haiku.
148 Hwasan_options []string `json:"hwasan_options,omitempty"`
149 // Additional options to be passed to HWASAN when running on host in Haiku.
150 Asan_options []string `json:"asan_options,omitempty"`
Muhammad Haseeb Ahmad7e744052022-03-25 22:50:53 +0000151 // If there's a Java fuzzer with JNI, a different version of Jazzer would
152 // need to be added to the fuzzer package than one without JNI
153 IsJni *bool `json:"is_jni,omitempty"`
Mark74c0ad22022-09-30 21:13:01 +0000154 // List of modules for monitoring coverage drops in directories (e.g. "libicu")
155 Target_modules []string `json:"target_modules,omitempty"`
hamzehc0a671f2021-07-22 12:05:08 -0700156}
157
Cory Barker9cfcf6d2022-07-22 17:22:02 +0000158type FuzzFrameworks struct {
159 Afl *bool
160 Libfuzzer *bool
161 Jazzer *bool
162}
163
hamzehc0a671f2021-07-22 12:05:08 -0700164type FuzzProperties struct {
165 // Optional list of seed files to be installed to the fuzz target's output
166 // directory.
167 Corpus []string `android:"path"`
168 // Optional list of data files to be installed to the fuzz target's output
169 // directory. Directory structure relative to the module is preserved.
170 Data []string `android:"path"`
171 // Optional dictionary to be installed to the fuzz target's output directory.
172 Dictionary *string `android:"path"`
Cory Barker9cfcf6d2022-07-22 17:22:02 +0000173 // Define the fuzzing frameworks this fuzz target can be built for. If
174 // empty then the fuzz target will be available to be built for all fuzz
175 // frameworks available
176 Fuzzing_frameworks *FuzzFrameworks
hamzehc0a671f2021-07-22 12:05:08 -0700177 // Config for running the target on fuzzing infrastructure.
178 Fuzz_config *FuzzConfig
179}
180
hamzeh41ad8812021-07-07 14:00:07 -0700181type FuzzPackagedModule struct {
182 FuzzProperties FuzzProperties
183 Dictionary android.Path
184 Corpus android.Paths
185 CorpusIntermediateDir android.Path
186 Config android.Path
187 Data android.Paths
188 DataIntermediateDir android.Path
189}
190
Cory Barker9cfcf6d2022-07-22 17:22:02 +0000191func GetFramework(ctx android.LoadHookContext, lang Lang) Framework {
192 framework := ctx.Config().Getenv("FUZZ_FRAMEWORK")
193
194 if lang == Cc {
195 switch strings.ToLower(framework) {
196 case "":
197 return LibFuzzer
198 case "libfuzzer":
199 return LibFuzzer
200 case "afl":
201 return AFL
202 }
203 } else if lang == Rust {
204 return LibFuzzer
205 } else if lang == Java {
206 return Jazzer
207 }
208
209 ctx.ModuleErrorf(fmt.Sprintf("%s is not a valid fuzzing framework for %s", framework, lang))
210 return UnknownFramework
211}
212
213func IsValidFrameworkForModule(targetFramework Framework, lang Lang, moduleFrameworks *FuzzFrameworks) bool {
214 if targetFramework == UnknownFramework {
215 return false
216 }
217
218 if moduleFrameworks == nil {
219 return true
220 }
221
222 switch targetFramework {
223 case LibFuzzer:
224 return proptools.BoolDefault(moduleFrameworks.Libfuzzer, true)
225 case AFL:
226 return proptools.BoolDefault(moduleFrameworks.Afl, true)
227 case Jazzer:
228 return proptools.BoolDefault(moduleFrameworks.Jazzer, true)
229 default:
230 panic("%s is not supported as a fuzz framework")
231 }
232}
233
hamzeh41ad8812021-07-07 14:00:07 -0700234func IsValid(fuzzModule FuzzModule) bool {
235 // Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of
236 // fuzz targets we're going to package anyway.
237 if !fuzzModule.Enabled() || fuzzModule.InRamdisk() || fuzzModule.InVendorRamdisk() || fuzzModule.InRecovery() {
238 return false
239 }
240
241 // Discard modules that are in an unavailable namespace.
242 if !fuzzModule.ExportedToMake() {
243 return false
244 }
245
246 return true
247}
248
249func (s *FuzzPackager) PackageArtifacts(ctx android.SingletonContext, module android.Module, fuzzModule FuzzPackagedModule, archDir android.OutputPath, builder *android.RuleBuilder) []FileToZip {
250 // Package the corpora into a zipfile.
251 var files []FileToZip
252 if fuzzModule.Corpus != nil {
253 corpusZip := archDir.Join(ctx, module.Name()+"_seed_corpus.zip")
254 command := builder.Command().BuiltTool("soong_zip").
255 Flag("-j").
256 FlagWithOutput("-o ", corpusZip)
257 rspFile := corpusZip.ReplaceExtension(ctx, "rsp")
258 command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.Corpus)
259 files = append(files, FileToZip{corpusZip, ""})
260 }
261
262 // Package the data into a zipfile.
263 if fuzzModule.Data != nil {
264 dataZip := archDir.Join(ctx, module.Name()+"_data.zip")
265 command := builder.Command().BuiltTool("soong_zip").
266 FlagWithOutput("-o ", dataZip)
267 for _, f := range fuzzModule.Data {
268 intermediateDir := strings.TrimSuffix(f.String(), f.Rel())
269 command.FlagWithArg("-C ", intermediateDir)
270 command.FlagWithInput("-f ", f)
271 }
272 files = append(files, FileToZip{dataZip, ""})
273 }
274
275 // The dictionary.
276 if fuzzModule.Dictionary != nil {
277 files = append(files, FileToZip{fuzzModule.Dictionary, ""})
278 }
279
280 // Additional fuzz config.
hamzehe8a1bfa2022-06-21 12:22:06 -0700281 if fuzzModule.Config != nil && IsValidConfig(fuzzModule, module.Name()) {
hamzeh41ad8812021-07-07 14:00:07 -0700282 files = append(files, FileToZip{fuzzModule.Config, ""})
283 }
284
285 return files
286}
287
288func (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) {
289 fuzzZip := archDir.Join(ctx, module.Name()+".zip")
290
291 command := builder.Command().BuiltTool("soong_zip").
292 Flag("-j").
293 FlagWithOutput("-o ", fuzzZip)
294
295 for _, file := range files {
296 if file.DestinationPathPrefix != "" {
297 command.FlagWithArg("-P ", file.DestinationPathPrefix)
298 } else {
299 command.Flag("-P ''")
300 }
301 command.FlagWithInput("-f ", file.SourceFilePath)
302 }
303
304 builder.Build("create-"+fuzzZip.String(),
305 "Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
306
307 // Don't add modules to 'make haiku-rust' that are set to not be
308 // exported to the fuzzing infrastructure.
309 if config := fuzzModule.FuzzProperties.Fuzz_config; config != nil {
310 if strings.Contains(hostOrTargetString, "host") && !BoolDefault(config.Fuzz_on_haiku_host, true) {
311 return archDirs[archOs], false
312 } else if !BoolDefault(config.Fuzz_on_haiku_device, true) {
313 return archDirs[archOs], false
314 }
315 }
316
317 s.FuzzTargets[module.Name()] = true
318 archDirs[archOs] = append(archDirs[archOs], FileToZip{fuzzZip, ""})
319
320 return archDirs[archOs], true
321}
322
hamzehc0a671f2021-07-22 12:05:08 -0700323func (f *FuzzConfig) String() string {
324 b, err := json.Marshal(f)
325 if err != nil {
326 panic(err)
327 }
328
329 return string(b)
330}
331
Cory Barker9cfcf6d2022-07-22 17:22:02 +0000332func (s *FuzzPackager) CreateFuzzPackage(ctx android.SingletonContext, archDirs map[ArchOs][]FileToZip, fuzzType Lang, pctx android.PackageContext) {
hamzeh41ad8812021-07-07 14:00:07 -0700333 var archOsList []ArchOs
334 for archOs := range archDirs {
335 archOsList = append(archOsList, archOs)
336 }
337 sort.Slice(archOsList, func(i, j int) bool { return archOsList[i].Dir < archOsList[j].Dir })
338
339 for _, archOs := range archOsList {
340 filesToZip := archDirs[archOs]
341 arch := archOs.Arch
342 hostOrTarget := archOs.HostOrTarget
343 builder := android.NewRuleBuilder(pctx, ctx)
344 zipFileName := "fuzz-" + hostOrTarget + "-" + arch + ".zip"
Cory Barkera1da26f2022-06-07 20:12:06 +0000345 if fuzzType == Rust {
hamzeh41ad8812021-07-07 14:00:07 -0700346 zipFileName = "fuzz-rust-" + hostOrTarget + "-" + arch + ".zip"
347 }
Cory Barkera1da26f2022-06-07 20:12:06 +0000348 if fuzzType == Java {
Muhammad Haseeb Ahmade3803102022-01-10 21:37:07 +0000349 zipFileName = "fuzz-java-" + hostOrTarget + "-" + arch + ".zip"
350 }
Cory Barker9cfcf6d2022-07-22 17:22:02 +0000351
hamzeh41ad8812021-07-07 14:00:07 -0700352 outputFile := android.PathForOutput(ctx, zipFileName)
353
354 s.Packages = append(s.Packages, outputFile)
355
356 command := builder.Command().BuiltTool("soong_zip").
357 Flag("-j").
358 FlagWithOutput("-o ", outputFile).
359 Flag("-L 0") // No need to try and re-compress the zipfiles.
360
361 for _, fileToZip := range filesToZip {
hamzeh41ad8812021-07-07 14:00:07 -0700362 if fileToZip.DestinationPathPrefix != "" {
363 command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix)
364 } else {
365 command.Flag("-P ''")
366 }
367 command.FlagWithInput("-f ", fileToZip.SourceFilePath)
368
369 }
370 builder.Build("create-fuzz-package-"+arch+"-"+hostOrTarget,
371 "Create fuzz target packages for "+arch+"-"+hostOrTarget)
372 }
373}
374
375func (s *FuzzPackager) PreallocateSlice(ctx android.MakeVarsContext, targets string) {
376 fuzzTargets := make([]string, 0, len(s.FuzzTargets))
377 for target, _ := range s.FuzzTargets {
378 fuzzTargets = append(fuzzTargets, target)
379 }
Cory Barkera1da26f2022-06-07 20:12:06 +0000380
hamzeh41ad8812021-07-07 14:00:07 -0700381 sort.Strings(fuzzTargets)
382 ctx.Strict(targets, strings.Join(fuzzTargets, " "))
383}