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