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 "-Z external-clangrt=true", 60 61 "-C passes='sancov-module'", 62 63 "--cfg fuzzing", 64 "-C llvm-args=-sanitizer-coverage-level=3", 65 "-C llvm-args=-sanitizer-coverage-trace-compares", 66 "-C llvm-args=-sanitizer-coverage-inline-8bit-counters", 67 "-C llvm-args=-sanitizer-coverage-pc-table", 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 external-clangrt=true", 79 "-Z sanitizer=address", 80} 81 82// See cc/sanitize.go's hwasanGlobalOptions for global hwasan options. 83var hwasanFlags = []string{ 84 "-Z external-clangrt=true", 85 "-Z sanitizer=hwaddress", 86 "-C target-feature=+tagged-globals", 87 88 // Flags from cc/sanitize.go hwasanFlags 89 "-C llvm-args=--aarch64-enable-global-isel-at-O=-1", 90 "-C llvm-args=-fast-isel=false", 91 "-C llvm-args=-instcombine-lower-dbg-declare=0", 92 93 // Additional flags for HWASAN-ified Rust/C interop 94 "-C llvm-args=--hwasan-with-ifunc", 95} 96 97func init() { 98} 99func (sanitize *sanitize) props() []interface{} { 100 return []interface{}{&sanitize.Properties} 101} 102 103func (sanitize *sanitize) begin(ctx BaseModuleContext) { 104 s := &sanitize.Properties.Sanitize 105 106 // Disable sanitizers for musl x86 modules, rustc does not support any sanitizers. 107 if ctx.Os() == android.LinuxMusl && ctx.Arch().ArchType == android.X86 { 108 s.Never = proptools.BoolPtr(true) 109 } 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 non-windows targets 214 if ctx.Os() != android.Windows && (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 228 if Bool(sanitize.Properties.Sanitize.Fuzzer) { 229 flags.RustFlags = append(flags.RustFlags, fuzzerFlags...) 230 } 231 232 if Bool(sanitize.Properties.Sanitize.Hwaddress) { 233 flags.RustFlags = append(flags.RustFlags, hwasanFlags...) 234 } 235 236 if Bool(sanitize.Properties.Sanitize.Address) { 237 flags.RustFlags = append(flags.RustFlags, asanFlags...) 238 if ctx.Host() { 239 // -nodefaultlibs (provided with libc++) prevents the driver from linking 240 // libraries needed with -fsanitize=address. http://b/18650275 (WAI) 241 flags.LinkFlags = append(flags.LinkFlags, []string{"-Wl,--no-as-needed"}...) 242 } 243 } 244 return flags, deps 245} 246 247func (sanitize *sanitize) deps(ctx BaseModuleContext, deps Deps) Deps { 248 return deps 249} 250 251func rustSanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) { 252 if mod, ok := mctx.Module().(*Module); ok && mod.sanitize != nil { 253 if !mod.Enabled(mctx) { 254 return 255 } 256 257 if Bool(mod.sanitize.Properties.Sanitize.Memtag_heap) && mod.Binary() { 258 noteDep := "note_memtag_heap_async" 259 if Bool(mod.sanitize.Properties.Sanitize.Diag.Memtag_heap) { 260 noteDep = "note_memtag_heap_sync" 261 } 262 depTag := cc.StaticDepTag(true) 263 variations := append(mctx.Target().Variations(), 264 blueprint.Variation{Mutator: "link", Variation: "static"}) 265 if mod.Device() { 266 variations = append(variations, mod.ImageVariation()) 267 } 268 mctx.AddFarVariationDependencies(variations, depTag, noteDep) 269 } 270 271 variations := mctx.Target().Variations() 272 var depTag blueprint.DependencyTag 273 var deps []string 274 275 if mod.IsSanitizerEnabled(cc.Asan) { 276 if mod.Host() { 277 variations = append(variations, 278 blueprint.Variation{Mutator: "link", Variation: "static"}) 279 depTag = cc.StaticDepTag(false) 280 deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan.static")} 281 } else { 282 variations = append(variations, 283 blueprint.Variation{Mutator: "link", Variation: "shared"}) 284 depTag = cc.SharedDepTag() 285 deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan")} 286 } 287 } else if mod.IsSanitizerEnabled(cc.Hwasan) { 288 // TODO(b/204776996): HWASan for static Rust binaries isn't supported yet. 289 if binary, ok := mod.compiler.(binaryInterface); ok { 290 if binary.staticallyLinked() { 291 mctx.ModuleErrorf("HWASan is not supported for static Rust executables yet.") 292 } 293 } 294 295 // Always link against the shared library -- static binaries will pull in the static 296 // library during final link if necessary 297 variations = append(variations, 298 blueprint.Variation{Mutator: "link", Variation: "shared"}) 299 depTag = cc.SharedDepTag() 300 deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "hwasan")} 301 } 302 303 if len(deps) > 0 { 304 mctx.AddFarVariationDependencies(variations, depTag, deps...) 305 } 306 } 307} 308 309func (sanitize *sanitize) SetSanitizer(t cc.SanitizerType, b bool) { 310 sanitizerSet := false 311 switch t { 312 case cc.Fuzzer: 313 sanitize.Properties.Sanitize.Fuzzer = proptools.BoolPtr(b) 314 sanitizerSet = true 315 case cc.Asan: 316 sanitize.Properties.Sanitize.Address = proptools.BoolPtr(b) 317 sanitizerSet = true 318 case cc.Hwasan: 319 sanitize.Properties.Sanitize.Hwaddress = proptools.BoolPtr(b) 320 sanitizerSet = true 321 case cc.Memtag_heap: 322 sanitize.Properties.Sanitize.Memtag_heap = proptools.BoolPtr(b) 323 sanitizerSet = true 324 default: 325 panic(fmt.Errorf("setting unsupported sanitizerType %d", t)) 326 } 327 if b && sanitizerSet { 328 sanitize.Properties.SanitizerEnabled = true 329 } 330} 331 332func (m *Module) UbsanRuntimeNeeded() bool { 333 return false 334} 335 336func (m *Module) MinimalRuntimeNeeded() bool { 337 return false 338} 339 340func (m *Module) UbsanRuntimeDep() bool { 341 return false 342} 343 344func (m *Module) MinimalRuntimeDep() bool { 345 return false 346} 347 348// Check if the sanitizer is explicitly disabled (as opposed to nil by 349// virtue of not being set). 350func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t cc.SanitizerType) bool { 351 if sanitize == nil { 352 return false 353 } 354 if Bool(sanitize.Properties.Sanitize.Never) { 355 return true 356 } 357 sanitizerVal := sanitize.getSanitizerBoolPtr(t) 358 return sanitizerVal != nil && *sanitizerVal == false 359} 360 361// There isn't an analog of the method above (ie:isSanitizerExplicitlyEnabled) 362// because enabling a sanitizer either directly (via the blueprint) or 363// indirectly (via a mutator) sets the bool ptr to true, and you can't 364// distinguish between the cases. It isn't needed though - both cases can be 365// treated identically. 366func (sanitize *sanitize) isSanitizerEnabled(t cc.SanitizerType) bool { 367 if sanitize == nil || !sanitize.Properties.SanitizerEnabled { 368 return false 369 } 370 371 sanitizerVal := sanitize.getSanitizerBoolPtr(t) 372 return sanitizerVal != nil && *sanitizerVal == true 373} 374 375func (sanitize *sanitize) getSanitizerBoolPtr(t cc.SanitizerType) *bool { 376 switch t { 377 case cc.Fuzzer: 378 return sanitize.Properties.Sanitize.Fuzzer 379 case cc.Asan: 380 return sanitize.Properties.Sanitize.Address 381 case cc.Hwasan: 382 return sanitize.Properties.Sanitize.Hwaddress 383 case cc.Memtag_heap: 384 return sanitize.Properties.Sanitize.Memtag_heap 385 default: 386 return nil 387 } 388} 389 390func (sanitize *sanitize) AndroidMk(ctx AndroidMkContext, entries *android.AndroidMkEntries) { 391 // Add a suffix for hwasan rlib libraries to allow surfacing both the sanitized and 392 // non-sanitized variants to make without a name conflict. 393 if entries.Class == "RLIB_LIBRARIES" || entries.Class == "STATIC_LIBRARIES" { 394 if sanitize.isSanitizerEnabled(cc.Hwasan) { 395 entries.SubName += ".hwasan" 396 } 397 } 398} 399 400func (mod *Module) SanitizerSupported(t cc.SanitizerType) bool { 401 // Sanitizers are not supported on Windows targets. 402 if mod.Os() == android.Windows { 403 return false 404 } 405 switch t { 406 case cc.Fuzzer: 407 return true 408 case cc.Asan: 409 return true 410 case cc.Hwasan: 411 // TODO(b/180495975): HWASan for static Rust binaries isn't supported yet. 412 if mod.StaticExecutable() { 413 return false 414 } 415 return true 416 case cc.Memtag_heap: 417 return true 418 default: 419 return false 420 } 421} 422 423func (mod *Module) IsSanitizerEnabled(t cc.SanitizerType) bool { 424 return mod.sanitize.isSanitizerEnabled(t) 425} 426 427func (mod *Module) IsSanitizerExplicitlyDisabled(t cc.SanitizerType) bool { 428 // Sanitizers are not supported on Windows targets. 429 if mod.Os() == android.Windows { 430 return true 431 } 432 433 return mod.sanitize.isSanitizerExplicitlyDisabled(t) 434} 435 436func (mod *Module) SetSanitizer(t cc.SanitizerType, b bool) { 437 if !Bool(mod.sanitize.Properties.Sanitize.Never) { 438 mod.sanitize.SetSanitizer(t, b) 439 } 440} 441 442func (mod *Module) StaticallyLinked() bool { 443 if lib, ok := mod.compiler.(libraryInterface); ok { 444 return lib.rlib() || lib.static() 445 } else if binary, ok := mod.compiler.(binaryInterface); ok { 446 return binary.staticallyLinked() 447 } 448 return false 449} 450 451func (mod *Module) SetInSanitizerDir() { 452 mod.sanitize.Properties.InSanitizerDir = true 453} 454 455func (mod *Module) SanitizeNever() bool { 456 return Bool(mod.sanitize.Properties.Sanitize.Never) 457} 458 459var _ cc.PlatformSanitizeable = (*Module)(nil) 460 461func IsSanitizableDependencyTag(tag blueprint.DependencyTag) bool { 462 switch t := tag.(type) { 463 case dependencyTag: 464 return t.library 465 default: 466 return cc.IsSanitizableDependencyTag(tag) 467 } 468} 469 470func (m *Module) SanitizableDepTagChecker() cc.SantizableDependencyTagChecker { 471 return IsSanitizableDependencyTag 472} 473