1// Copyright 2020 The Android Open Source Project 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 15package rust 16 17import ( 18 "path/filepath" 19 "sort" 20 "strings" 21 22 "android/soong/android" 23 "android/soong/cc" 24 "android/soong/rust/config" 25) 26 27func init() { 28 android.RegisterModuleType("rust_fuzz", RustFuzzFactory) 29 android.RegisterSingletonType("rust_fuzz_packaging", rustFuzzPackagingFactory) 30} 31 32type fuzzDecorator struct { 33 *binaryDecorator 34 35 Properties cc.FuzzProperties 36 dictionary android.Path 37 corpus android.Paths 38 corpusIntermediateDir android.Path 39 config android.Path 40 data android.Paths 41 dataIntermediateDir android.Path 42} 43 44var _ compiler = (*binaryDecorator)(nil) 45 46// rust_binary produces a binary that is runnable on a device. 47func RustFuzzFactory() android.Module { 48 module, _ := NewRustFuzz(android.HostAndDeviceSupported) 49 return module.Init() 50} 51 52func NewRustFuzz(hod android.HostOrDeviceSupported) (*Module, *fuzzDecorator) { 53 module, binary := NewRustBinary(hod) 54 fuzz := &fuzzDecorator{ 55 binaryDecorator: binary, 56 } 57 58 // Change the defaults for the binaryDecorator's baseCompiler 59 fuzz.binaryDecorator.baseCompiler.dir = "fuzz" 60 fuzz.binaryDecorator.baseCompiler.dir64 = "fuzz" 61 fuzz.binaryDecorator.baseCompiler.location = InstallInData 62 module.sanitize.SetSanitizer(cc.Fuzzer, true) 63 module.compiler = fuzz 64 return module, fuzz 65} 66 67func (fuzzer *fuzzDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags { 68 flags = fuzzer.binaryDecorator.compilerFlags(ctx, flags) 69 70 // `../lib` for installed fuzz targets (both host and device), and `./lib` for fuzz target packages. 71 flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../lib`) 72 flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/lib`) 73 74 return flags 75} 76 77func (fuzzer *fuzzDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps { 78 if libFuzzerRuntimeLibrary := config.LibFuzzerRuntimeLibrary(ctx.toolchain()); libFuzzerRuntimeLibrary != "" { 79 deps.StaticLibs = append(deps.StaticLibs, libFuzzerRuntimeLibrary) 80 } 81 deps.SharedLibs = append(deps.SharedLibs, "libc++") 82 deps.Rlibs = append(deps.Rlibs, "liblibfuzzer_sys") 83 84 deps = fuzzer.binaryDecorator.compilerDeps(ctx, deps) 85 86 return deps 87} 88 89func (fuzzer *fuzzDecorator) compilerProps() []interface{} { 90 return append(fuzzer.binaryDecorator.compilerProps(), 91 &fuzzer.Properties) 92} 93 94func (fuzzer *fuzzDecorator) stdLinkage(ctx *depsContext) RustLinkage { 95 return RlibLinkage 96} 97 98func (fuzzer *fuzzDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep { 99 return rlibAutoDep 100} 101 102// Responsible for generating GNU Make rules that package fuzz targets into 103// their architecture & target/host specific zip file. 104type rustFuzzPackager struct { 105 packages android.Paths 106 fuzzTargets map[string]bool 107} 108 109func rustFuzzPackagingFactory() android.Singleton { 110 return &rustFuzzPackager{} 111} 112 113type fileToZip struct { 114 SourceFilePath android.Path 115 DestinationPathPrefix string 116} 117 118type archOs struct { 119 hostOrTarget string 120 arch string 121 dir string 122} 123 124func (s *rustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) { 125 126 // Map between each architecture + host/device combination. 127 archDirs := make(map[archOs][]fileToZip) 128 129 // List of individual fuzz targets. 130 s.fuzzTargets = make(map[string]bool) 131 132 ctx.VisitAllModules(func(module android.Module) { 133 // Discard non-fuzz targets. 134 rustModule, ok := module.(*Module) 135 if !ok { 136 return 137 } 138 139 fuzzModule, ok := rustModule.compiler.(*fuzzDecorator) 140 if !ok { 141 return 142 } 143 144 // Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of 145 // fuzz targets we're going to package anyway. 146 if !rustModule.Enabled() || rustModule.Properties.PreventInstall || 147 rustModule.InRamdisk() || rustModule.InVendorRamdisk() || rustModule.InRecovery() { 148 return 149 } 150 151 // Discard modules that are in an unavailable namespace. 152 if !rustModule.ExportedToMake() { 153 return 154 } 155 156 hostOrTargetString := "target" 157 if rustModule.Host() { 158 hostOrTargetString = "host" 159 } 160 161 archString := rustModule.Arch().ArchType.String() 162 archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString) 163 archOs := archOs{hostOrTarget: hostOrTargetString, arch: archString, dir: archDir.String()} 164 165 var files []fileToZip 166 builder := android.NewRuleBuilder(pctx, ctx) 167 168 // Package the corpora into a zipfile. 169 if fuzzModule.corpus != nil { 170 corpusZip := archDir.Join(ctx, module.Name()+"_seed_corpus.zip") 171 command := builder.Command().BuiltTool("soong_zip"). 172 Flag("-j"). 173 FlagWithOutput("-o ", corpusZip) 174 rspFile := corpusZip.ReplaceExtension(ctx, "rsp") 175 command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.corpus) 176 files = append(files, fileToZip{corpusZip, ""}) 177 } 178 179 // Package the data into a zipfile. 180 if fuzzModule.data != nil { 181 dataZip := archDir.Join(ctx, module.Name()+"_data.zip") 182 command := builder.Command().BuiltTool("soong_zip"). 183 FlagWithOutput("-o ", dataZip) 184 for _, f := range fuzzModule.data { 185 intermediateDir := strings.TrimSuffix(f.String(), f.Rel()) 186 command.FlagWithArg("-C ", intermediateDir) 187 command.FlagWithInput("-f ", f) 188 } 189 files = append(files, fileToZip{dataZip, ""}) 190 } 191 192 // The executable. 193 files = append(files, fileToZip{rustModule.unstrippedOutputFile.Path(), ""}) 194 195 // The dictionary. 196 if fuzzModule.dictionary != nil { 197 files = append(files, fileToZip{fuzzModule.dictionary, ""}) 198 } 199 200 // Additional fuzz config. 201 if fuzzModule.config != nil { 202 files = append(files, fileToZip{fuzzModule.config, ""}) 203 } 204 205 fuzzZip := archDir.Join(ctx, module.Name()+".zip") 206 207 command := builder.Command().BuiltTool("soong_zip"). 208 Flag("-j"). 209 FlagWithOutput("-o ", fuzzZip) 210 211 for _, file := range files { 212 if file.DestinationPathPrefix != "" { 213 command.FlagWithArg("-P ", file.DestinationPathPrefix) 214 } else { 215 command.Flag("-P ''") 216 } 217 command.FlagWithInput("-f ", file.SourceFilePath) 218 } 219 220 builder.Build("create-"+fuzzZip.String(), 221 "Package "+module.Name()+" for "+archString+"-"+hostOrTargetString) 222 223 // Don't add modules to 'make haiku-rust' that are set to not be 224 // exported to the fuzzing infrastructure. 225 if config := fuzzModule.Properties.Fuzz_config; config != nil { 226 if rustModule.Host() && !BoolDefault(config.Fuzz_on_haiku_host, true) { 227 return 228 } else if !BoolDefault(config.Fuzz_on_haiku_device, true) { 229 return 230 } 231 } 232 233 s.fuzzTargets[module.Name()] = true 234 archDirs[archOs] = append(archDirs[archOs], fileToZip{fuzzZip, ""}) 235 }) 236 237 var archOsList []archOs 238 for archOs := range archDirs { 239 archOsList = append(archOsList, archOs) 240 } 241 sort.Slice(archOsList, func(i, j int) bool { return archOsList[i].dir < archOsList[j].dir }) 242 243 for _, archOs := range archOsList { 244 filesToZip := archDirs[archOs] 245 arch := archOs.arch 246 hostOrTarget := archOs.hostOrTarget 247 builder := android.NewRuleBuilder(pctx, ctx) 248 outputFile := android.PathForOutput(ctx, "fuzz-rust-"+hostOrTarget+"-"+arch+".zip") 249 s.packages = append(s.packages, outputFile) 250 251 command := builder.Command().BuiltTool("soong_zip"). 252 Flag("-j"). 253 FlagWithOutput("-o ", outputFile). 254 Flag("-L 0") // No need to try and re-compress the zipfiles. 255 256 for _, fileToZip := range filesToZip { 257 if fileToZip.DestinationPathPrefix != "" { 258 command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix) 259 } else { 260 command.Flag("-P ''") 261 } 262 command.FlagWithInput("-f ", fileToZip.SourceFilePath) 263 } 264 builder.Build("create-fuzz-package-"+arch+"-"+hostOrTarget, 265 "Create fuzz target packages for "+arch+"-"+hostOrTarget) 266 } 267 268} 269 270func (s *rustFuzzPackager) MakeVars(ctx android.MakeVarsContext) { 271 packages := s.packages.Strings() 272 sort.Strings(packages) 273 274 ctx.Strict("SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " ")) 275 276 // Preallocate the slice of fuzz targets to minimise memory allocations. 277 fuzzTargets := make([]string, 0, len(s.fuzzTargets)) 278 for target, _ := range s.fuzzTargets { 279 fuzzTargets = append(fuzzTargets, target) 280 } 281 sort.Strings(fuzzTargets) 282 ctx.Strict("ALL_RUST_FUZZ_TARGETS", strings.Join(fuzzTargets, " ")) 283} 284 285func (fuzz *fuzzDecorator) install(ctx ModuleContext) { 286 fuzz.binaryDecorator.baseCompiler.dir = filepath.Join( 287 "fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName()) 288 fuzz.binaryDecorator.baseCompiler.dir64 = filepath.Join( 289 "fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName()) 290 fuzz.binaryDecorator.baseCompiler.install(ctx) 291 292 if fuzz.Properties.Corpus != nil { 293 fuzz.corpus = android.PathsForModuleSrc(ctx, fuzz.Properties.Corpus) 294 } 295 if fuzz.Properties.Data != nil { 296 fuzz.data = android.PathsForModuleSrc(ctx, fuzz.Properties.Data) 297 } 298 if fuzz.Properties.Dictionary != nil { 299 fuzz.dictionary = android.PathForModuleSrc(ctx, *fuzz.Properties.Dictionary) 300 } 301} 302