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 "fmt" 19 "strings" 20 21 "github.com/google/blueprint" 22 "github.com/google/blueprint/proptools" 23 24 "android/soong/android" 25 "android/soong/cc" 26 "android/soong/rust/config" 27) 28 29// TODO: When Rust has sanitizer-parity with CC, deduplicate this struct 30type SanitizeProperties struct { 31 // enable AddressSanitizer, HWAddressSanitizer, and others. 32 Sanitize struct { 33 Address *bool `android:"arch_variant"` 34 Hwaddress *bool `android:"arch_variant"` 35 36 // Memory-tagging, only available on arm64 37 // if diag.memtag unset or false, enables async memory tagging 38 Memtag_heap *bool `android:"arch_variant"` 39 Fuzzer *bool `android:"arch_variant"` 40 Never *bool `android:"arch_variant"` 41 42 // Sanitizers to run in the diagnostic mode (as opposed to the release mode). 43 // Replaces abort() on error with a human-readable error message. 44 // Address and Thread sanitizers always run in diagnostic mode. 45 Diag struct { 46 // Memory-tagging, only available on arm64 47 // requires sanitizer.memtag: true 48 // if set, enables sync memory tagging 49 Memtag_heap *bool `android:"arch_variant"` 50 } 51 } 52 SanitizerEnabled bool `blueprint:"mutated"` 53 54 // Used when we need to place libraries in their own directory, such as ASAN. 55 InSanitizerDir bool `blueprint:"mutated"` 56} 57 58var fuzzerFlags = []string{ 59 "-C passes='sancov-module'", 60 61 "--cfg fuzzing", 62 "-C llvm-args=-sanitizer-coverage-level=3", 63 "-C llvm-args=-sanitizer-coverage-trace-compares", 64 "-C llvm-args=-sanitizer-coverage-inline-8bit-counters", 65 "-C llvm-args=-sanitizer-coverage-trace-geps", 66 "-C llvm-args=-sanitizer-coverage-prune-blocks=0", 67 68 // See https://github.com/rust-fuzz/cargo-fuzz/pull/193 69 "-C link-dead-code", 70 71 // Sancov breaks with lto 72 // TODO: Remove when https://bugs.llvm.org/show_bug.cgi?id=41734 is resolved and sancov-module works with LTO 73 "-C lto=no", 74} 75 76var asanFlags = []string{ 77 "-Z sanitizer=address", 78} 79 80// See cc/sanitize.go's hwasanGlobalOptions for global hwasan options. 81var hwasanFlags = []string{ 82 "-Z sanitizer=hwaddress", 83 "-C target-feature=+tagged-globals", 84 85 // Flags from cc/sanitize.go hwasanFlags 86 "-C llvm-args=--aarch64-enable-global-isel-at-O=-1", 87 "-C llvm-args=-fast-isel=false", 88 "-C llvm-args=-instcombine-lower-dbg-declare=0", 89 90 // Additional flags for HWASAN-ified Rust/C interop 91 "-C llvm-args=--hwasan-with-ifunc", 92} 93 94func boolPtr(v bool) *bool { 95 if v { 96 return &v 97 } else { 98 return nil 99 } 100} 101 102func init() { 103} 104func (sanitize *sanitize) props() []interface{} { 105 return []interface{}{&sanitize.Properties} 106} 107 108func (sanitize *sanitize) begin(ctx BaseModuleContext) { 109 s := &sanitize.Properties.Sanitize 110 111 // Never always wins. 112 if Bool(s.Never) { 113 return 114 } 115 116 // rust_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {Memtag_heap}). 117 if binary, ok := ctx.RustModule().compiler.(binaryInterface); ok && binary.testBinary() { 118 if s.Memtag_heap == nil { 119 s.Memtag_heap = proptools.BoolPtr(true) 120 } 121 if s.Diag.Memtag_heap == nil { 122 s.Diag.Memtag_heap = proptools.BoolPtr(true) 123 } 124 } 125 126 var globalSanitizers []string 127 var globalSanitizersDiag []string 128 129 if ctx.Host() { 130 if !ctx.Windows() { 131 globalSanitizers = ctx.Config().SanitizeHost() 132 } 133 } else { 134 arches := ctx.Config().SanitizeDeviceArch() 135 if len(arches) == 0 || android.InList(ctx.Arch().ArchType.Name, arches) { 136 globalSanitizers = ctx.Config().SanitizeDevice() 137 globalSanitizersDiag = ctx.Config().SanitizeDeviceDiag() 138 } 139 } 140 141 if len(globalSanitizers) > 0 { 142 var found bool 143 144 // Global Sanitizers 145 if found, globalSanitizers = android.RemoveFromList("hwaddress", globalSanitizers); found && s.Hwaddress == nil { 146 // TODO(b/204776996): HWASan for static Rust binaries isn't supported yet. 147 if !ctx.RustModule().StaticExecutable() { 148 s.Hwaddress = proptools.BoolPtr(true) 149 } 150 } 151 152 if found, globalSanitizers = android.RemoveFromList("memtag_heap", globalSanitizers); found && s.Memtag_heap == nil { 153 if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) { 154 s.Memtag_heap = proptools.BoolPtr(true) 155 } 156 } 157 158 if found, globalSanitizers = android.RemoveFromList("address", globalSanitizers); found && s.Address == nil { 159 s.Address = proptools.BoolPtr(true) 160 } 161 162 if found, globalSanitizers = android.RemoveFromList("fuzzer", globalSanitizers); found && s.Fuzzer == nil { 163 // TODO(b/204776996): HWASan for static Rust binaries isn't supported yet, and fuzzer enables HWAsan 164 if !ctx.RustModule().StaticExecutable() { 165 s.Fuzzer = proptools.BoolPtr(true) 166 } 167 } 168 169 // Global Diag Sanitizers 170 if found, globalSanitizersDiag = android.RemoveFromList("memtag_heap", globalSanitizersDiag); found && 171 s.Diag.Memtag_heap == nil && Bool(s.Memtag_heap) { 172 s.Diag.Memtag_heap = proptools.BoolPtr(true) 173 } 174 } 175 176 // Enable Memtag for all components in the include paths (for Aarch64 only) 177 if ctx.Arch().ArchType == android.Arm64 && ctx.Os().Bionic() { 178 if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) { 179 if s.Memtag_heap == nil { 180 s.Memtag_heap = proptools.BoolPtr(true) 181 } 182 if s.Diag.Memtag_heap == nil { 183 s.Diag.Memtag_heap = proptools.BoolPtr(true) 184 } 185 } else if ctx.Config().MemtagHeapAsyncEnabledForPath(ctx.ModuleDir()) { 186 if s.Memtag_heap == nil { 187 s.Memtag_heap = proptools.BoolPtr(true) 188 } 189 } 190 } 191 192 // HWASan requires AArch64 hardware feature (top-byte-ignore). 193 if ctx.Arch().ArchType != android.Arm64 || !ctx.Os().Bionic() { 194 s.Hwaddress = nil 195 } 196 197 // HWASan ramdisk (which is built from recovery) goes over some bootloader limit. 198 // Keep libc instrumented so that ramdisk / vendor_ramdisk / recovery can run hwasan-instrumented code if necessary. 199 if (ctx.RustModule().InRamdisk() || ctx.RustModule().InVendorRamdisk() || ctx.RustModule().InRecovery()) && !strings.HasPrefix(ctx.ModuleDir(), "bionic/libc") { 200 s.Hwaddress = nil 201 } 202 203 if Bool(s.Hwaddress) { 204 s.Address = nil 205 } 206 207 // Memtag_heap is only implemented on AArch64. 208 if ctx.Arch().ArchType != android.Arm64 || !ctx.Os().Bionic() { 209 s.Memtag_heap = nil 210 } 211 212 // TODO:(b/178369775) 213 // For now sanitizing is only supported on devices 214 if ctx.Os() == android.Android && (Bool(s.Hwaddress) || Bool(s.Address) || Bool(s.Memtag_heap) || Bool(s.Fuzzer)) { 215 sanitize.Properties.SanitizerEnabled = true 216 } 217} 218 219type sanitize struct { 220 Properties SanitizeProperties 221} 222 223func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) { 224 if !sanitize.Properties.SanitizerEnabled { 225 return flags, deps 226 } 227 if Bool(sanitize.Properties.Sanitize.Fuzzer) { 228 flags.RustFlags = append(flags.RustFlags, fuzzerFlags...) 229 if ctx.Arch().ArchType == android.Arm64 && ctx.Os().Bionic() { 230 flags.RustFlags = append(flags.RustFlags, hwasanFlags...) 231 } else { 232 flags.RustFlags = append(flags.RustFlags, asanFlags...) 233 } 234 } else if Bool(sanitize.Properties.Sanitize.Hwaddress) { 235 flags.RustFlags = append(flags.RustFlags, hwasanFlags...) 236 } else if Bool(sanitize.Properties.Sanitize.Address) { 237 flags.RustFlags = append(flags.RustFlags, asanFlags...) 238 } 239 return flags, deps 240} 241 242func (sanitize *sanitize) deps(ctx BaseModuleContext, deps Deps) Deps { 243 return deps 244} 245 246func rustSanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) { 247 if mod, ok := mctx.Module().(*Module); ok && mod.sanitize != nil { 248 if !mod.Enabled() { 249 return 250 } 251 252 if Bool(mod.sanitize.Properties.Sanitize.Memtag_heap) && mod.Binary() { 253 noteDep := "note_memtag_heap_async" 254 if Bool(mod.sanitize.Properties.Sanitize.Diag.Memtag_heap) { 255 noteDep = "note_memtag_heap_sync" 256 } 257 // If we're using snapshots, redirect to snapshot whenever possible 258 // TODO(b/178470649): clean manual snapshot redirections 259 snapshot := mctx.Provider(cc.SnapshotInfoProvider).(cc.SnapshotInfo) 260 if lib, ok := snapshot.StaticLibs[noteDep]; ok { 261 noteDep = lib 262 } 263 depTag := cc.StaticDepTag(true) 264 variations := append(mctx.Target().Variations(), 265 blueprint.Variation{Mutator: "link", Variation: "static"}) 266 if mod.Device() { 267 variations = append(variations, mod.ImageVariation()) 268 } 269 mctx.AddFarVariationDependencies(variations, depTag, noteDep) 270 } 271 272 variations := mctx.Target().Variations() 273 var depTag blueprint.DependencyTag 274 var deps []string 275 276 if mod.IsSanitizerEnabled(cc.Asan) || 277 (mod.IsSanitizerEnabled(cc.Fuzzer) && (mctx.Arch().ArchType != android.Arm64 || !mctx.Os().Bionic())) { 278 variations = append(variations, 279 blueprint.Variation{Mutator: "link", Variation: "shared"}) 280 depTag = cc.SharedDepTag() 281 deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan")} 282 } else if mod.IsSanitizerEnabled(cc.Hwasan) || 283 (mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType == android.Arm64 && mctx.Os().Bionic()) { 284 // TODO(b/204776996): HWASan for static Rust binaries isn't supported yet. 285 if binary, ok := mod.compiler.(binaryInterface); ok { 286 if binary.staticallyLinked() { 287 mctx.ModuleErrorf("HWASan is not supported for static Rust executables yet.") 288 } 289 } 290 291 // Always link against the shared library -- static binaries will pull in the static 292 // library during final link if necessary 293 variations = append(variations, 294 blueprint.Variation{Mutator: "link", Variation: "shared"}) 295 depTag = cc.SharedDepTag() 296 deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "hwasan")} 297 } 298 299 if len(deps) > 0 { 300 mctx.AddFarVariationDependencies(variations, depTag, deps...) 301 } 302 } 303} 304 305func (sanitize *sanitize) SetSanitizer(t cc.SanitizerType, b bool) { 306 sanitizerSet := false 307 switch t { 308 case cc.Fuzzer: 309 sanitize.Properties.Sanitize.Fuzzer = boolPtr(b) 310 sanitizerSet = true 311 case cc.Asan: 312 sanitize.Properties.Sanitize.Address = boolPtr(b) 313 sanitizerSet = true 314 case cc.Hwasan: 315 sanitize.Properties.Sanitize.Hwaddress = boolPtr(b) 316 sanitizerSet = true 317 case cc.Memtag_heap: 318 sanitize.Properties.Sanitize.Memtag_heap = boolPtr(b) 319 sanitizerSet = true 320 default: 321 panic(fmt.Errorf("setting unsupported sanitizerType %d", t)) 322 } 323 if b && sanitizerSet { 324 sanitize.Properties.SanitizerEnabled = true 325 } 326} 327 328func (m *Module) UbsanRuntimeNeeded() bool { 329 return false 330} 331 332func (m *Module) MinimalRuntimeNeeded() bool { 333 return false 334} 335 336func (m *Module) UbsanRuntimeDep() bool { 337 return false 338} 339 340func (m *Module) MinimalRuntimeDep() bool { 341 return false 342} 343 344// Check if the sanitizer is explicitly disabled (as opposed to nil by 345// virtue of not being set). 346func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t cc.SanitizerType) bool { 347 if sanitize == nil { 348 return false 349 } 350 if Bool(sanitize.Properties.Sanitize.Never) { 351 return true 352 } 353 sanitizerVal := sanitize.getSanitizerBoolPtr(t) 354 return sanitizerVal != nil && *sanitizerVal == false 355} 356 357// There isn't an analog of the method above (ie:isSanitizerExplicitlyEnabled) 358// because enabling a sanitizer either directly (via the blueprint) or 359// indirectly (via a mutator) sets the bool ptr to true, and you can't 360// distinguish between the cases. It isn't needed though - both cases can be 361// treated identically. 362func (sanitize *sanitize) isSanitizerEnabled(t cc.SanitizerType) bool { 363 if sanitize == nil || !sanitize.Properties.SanitizerEnabled { 364 return false 365 } 366 367 sanitizerVal := sanitize.getSanitizerBoolPtr(t) 368 return sanitizerVal != nil && *sanitizerVal == true 369} 370 371func (sanitize *sanitize) getSanitizerBoolPtr(t cc.SanitizerType) *bool { 372 switch t { 373 case cc.Fuzzer: 374 return sanitize.Properties.Sanitize.Fuzzer 375 case cc.Asan: 376 return sanitize.Properties.Sanitize.Address 377 case cc.Hwasan: 378 return sanitize.Properties.Sanitize.Hwaddress 379 case cc.Memtag_heap: 380 return sanitize.Properties.Sanitize.Memtag_heap 381 default: 382 return nil 383 } 384} 385 386func (sanitize *sanitize) AndroidMk(ctx AndroidMkContext, entries *android.AndroidMkEntries) { 387 // Add a suffix for hwasan rlib libraries to allow surfacing both the sanitized and 388 // non-sanitized variants to make without a name conflict. 389 if entries.Class == "RLIB_LIBRARIES" || entries.Class == "STATIC_LIBRARIES" { 390 if sanitize.isSanitizerEnabled(cc.Hwasan) { 391 entries.SubName += ".hwasan" 392 } 393 } 394} 395 396func (mod *Module) SanitizerSupported(t cc.SanitizerType) bool { 397 if mod.Host() { 398 return false 399 } 400 switch t { 401 case cc.Fuzzer: 402 return true 403 case cc.Asan: 404 return true 405 case cc.Hwasan: 406 // TODO(b/180495975): HWASan for static Rust binaries isn't supported yet. 407 if mod.StaticExecutable() { 408 return false 409 } 410 return true 411 case cc.Memtag_heap: 412 return true 413 default: 414 return false 415 } 416} 417 418func (mod *Module) IsSanitizerEnabled(t cc.SanitizerType) bool { 419 return mod.sanitize.isSanitizerEnabled(t) 420} 421 422func (mod *Module) IsSanitizerExplicitlyDisabled(t cc.SanitizerType) bool { 423 if mod.Host() { 424 return true 425 } 426 427 // TODO(b/178365482): Rust/CC interop doesn't work just yet; don't sanitize rust_ffi modules until 428 // linkage issues are resolved. 429 if lib, ok := mod.compiler.(libraryInterface); ok { 430 if lib.shared() || lib.static() { 431 return true 432 } 433 } 434 435 return mod.sanitize.isSanitizerExplicitlyDisabled(t) 436} 437 438func (mod *Module) SetSanitizer(t cc.SanitizerType, b bool) { 439 if !Bool(mod.sanitize.Properties.Sanitize.Never) { 440 mod.sanitize.SetSanitizer(t, b) 441 } 442} 443 444func (mod *Module) StaticallyLinked() bool { 445 if lib, ok := mod.compiler.(libraryInterface); ok { 446 return lib.rlib() || lib.static() 447 } else if binary, ok := mod.compiler.(binaryInterface); ok { 448 return binary.staticallyLinked() 449 } 450 return false 451} 452 453func (mod *Module) SetInSanitizerDir() { 454 mod.sanitize.Properties.InSanitizerDir = true 455} 456 457func (mod *Module) SanitizeNever() bool { 458 return Bool(mod.sanitize.Properties.Sanitize.Never) 459} 460 461var _ cc.PlatformSanitizeable = (*Module)(nil) 462 463func IsSanitizableDependencyTag(tag blueprint.DependencyTag) bool { 464 switch t := tag.(type) { 465 case dependencyTag: 466 return t.library 467 default: 468 return cc.IsSanitizableDependencyTag(tag) 469 } 470} 471 472func (m *Module) SanitizableDepTagChecker() cc.SantizableDependencyTagChecker { 473 return IsSanitizableDependencyTag 474} 475