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