Home | History | Annotate | Download | only in cc
      1 // Copyright 2016 Google Inc. All rights reserved.
      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 
     15 package cc
     16 
     17 import (
     18 	"fmt"
     19 	"io"
     20 	"strings"
     21 
     22 	"github.com/google/blueprint"
     23 
     24 	"android/soong/android"
     25 	"android/soong/cc/config"
     26 )
     27 
     28 var (
     29 	// Any C flags added by sanitizer which libTooling tools may not
     30 	// understand also need to be added to ClangLibToolingUnknownCflags in
     31 	// cc/config/clang.go
     32 
     33 	asanCflags  = []string{"-fno-omit-frame-pointer"}
     34 	asanLdflags = []string{"-Wl,-u,__asan_preinit"}
     35 	asanLibs    = []string{"libasan"}
     36 
     37 	cfiCflags = []string{"-flto", "-fsanitize-cfi-cross-dso", "-fvisibility=default",
     38 		"-fsanitize-blacklist=external/compiler-rt/lib/cfi/cfi_blacklist.txt"}
     39 	// FIXME: revert the __cfi_check flag when clang is updated to r280031.
     40 	cfiLdflags = []string{"-flto", "-fsanitize-cfi-cross-dso", "-fsanitize=cfi",
     41 		"-Wl,-plugin-opt,O1 -Wl,-export-dynamic-symbol=__cfi_check"}
     42 	cfiArflags = []string{"--plugin ${config.ClangBin}/../lib64/LLVMgold.so"}
     43 
     44 	intOverflowCflags = []string{"-fsanitize-blacklist=build/soong/cc/config/integer_overflow_blacklist.txt"}
     45 )
     46 
     47 type sanitizerType int
     48 
     49 func boolPtr(v bool) *bool {
     50 	if v {
     51 		return &v
     52 	} else {
     53 		return nil
     54 	}
     55 }
     56 
     57 const (
     58 	asan sanitizerType = iota + 1
     59 	tsan
     60 	intOverflow
     61 )
     62 
     63 func (t sanitizerType) String() string {
     64 	switch t {
     65 	case asan:
     66 		return "asan"
     67 	case tsan:
     68 		return "tsan"
     69 	case intOverflow:
     70 		return "intOverflow"
     71 	default:
     72 		panic(fmt.Errorf("unknown sanitizerType %d", t))
     73 	}
     74 }
     75 
     76 type SanitizeProperties struct {
     77 	// enable AddressSanitizer, ThreadSanitizer, or UndefinedBehaviorSanitizer
     78 	Sanitize struct {
     79 		Never bool `android:"arch_variant"`
     80 
     81 		// main sanitizers
     82 		Address *bool `android:"arch_variant"`
     83 		Thread  *bool `android:"arch_variant"`
     84 
     85 		// local sanitizers
     86 		Undefined        *bool    `android:"arch_variant"`
     87 		All_undefined    *bool    `android:"arch_variant"`
     88 		Misc_undefined   []string `android:"arch_variant"`
     89 		Coverage         *bool    `android:"arch_variant"`
     90 		Safestack        *bool    `android:"arch_variant"`
     91 		Cfi              *bool    `android:"arch_variant"`
     92 		Integer_overflow *bool    `android:"arch_variant"`
     93 
     94 		// Sanitizers to run in the diagnostic mode (as opposed to the release mode).
     95 		// Replaces abort() on error with a human-readable error message.
     96 		// Address and Thread sanitizers always run in diagnostic mode.
     97 		Diag struct {
     98 			Undefined        *bool    `android:"arch_variant"`
     99 			Cfi              *bool    `android:"arch_variant"`
    100 			Integer_overflow *bool    `android:"arch_variant"`
    101 			Misc_undefined   []string `android:"arch_variant"`
    102 		}
    103 
    104 		// value to pass to -fsanitize-recover=
    105 		Recover []string
    106 
    107 		// value to pass to -fsanitize-blacklist
    108 		Blacklist *string
    109 	} `android:"arch_variant"`
    110 
    111 	SanitizerEnabled bool `blueprint:"mutated"`
    112 	SanitizeDep      bool `blueprint:"mutated"`
    113 	InSanitizerDir   bool `blueprint:"mutated"`
    114 }
    115 
    116 type sanitize struct {
    117 	Properties SanitizeProperties
    118 
    119 	runtimeLibrary          string
    120 	androidMkRuntimeLibrary string
    121 }
    122 
    123 func (sanitize *sanitize) props() []interface{} {
    124 	return []interface{}{&sanitize.Properties}
    125 }
    126 
    127 func (sanitize *sanitize) begin(ctx BaseModuleContext) {
    128 	s := &sanitize.Properties.Sanitize
    129 
    130 	// Don't apply sanitizers to NDK code.
    131 	if ctx.sdk() {
    132 		s.Never = true
    133 	}
    134 
    135 	// Never always wins.
    136 	if s.Never {
    137 		return
    138 	}
    139 
    140 	var globalSanitizers []string
    141 	var globalSanitizersDiag []string
    142 
    143 	if ctx.clang() {
    144 		if ctx.Host() {
    145 			globalSanitizers = ctx.AConfig().SanitizeHost()
    146 		} else {
    147 			arches := ctx.AConfig().SanitizeDeviceArch()
    148 			if len(arches) == 0 || inList(ctx.Arch().ArchType.Name, arches) {
    149 				globalSanitizers = ctx.AConfig().SanitizeDevice()
    150 				globalSanitizersDiag = ctx.AConfig().SanitizeDeviceDiag()
    151 			}
    152 		}
    153 	}
    154 
    155 	if len(globalSanitizers) > 0 {
    156 		var found bool
    157 		if found, globalSanitizers = removeFromList("undefined", globalSanitizers); found && s.All_undefined == nil {
    158 			s.All_undefined = boolPtr(true)
    159 		}
    160 
    161 		if found, globalSanitizers = removeFromList("default-ub", globalSanitizers); found && s.Undefined == nil {
    162 			s.Undefined = boolPtr(true)
    163 		}
    164 
    165 		if found, globalSanitizers = removeFromList("address", globalSanitizers); found {
    166 			if s.Address == nil {
    167 				s.Address = boolPtr(true)
    168 			} else if *s.Address == false {
    169 				// Coverage w/o address is an error. If globalSanitizers includes both, and the module
    170 				// disables address, then disable coverage as well.
    171 				_, globalSanitizers = removeFromList("coverage", globalSanitizers)
    172 			}
    173 		}
    174 
    175 		if found, globalSanitizers = removeFromList("thread", globalSanitizers); found && s.Thread == nil {
    176 			s.Thread = boolPtr(true)
    177 		}
    178 
    179 		if found, globalSanitizers = removeFromList("coverage", globalSanitizers); found && s.Coverage == nil {
    180 			s.Coverage = boolPtr(true)
    181 		}
    182 
    183 		if found, globalSanitizers = removeFromList("safe-stack", globalSanitizers); found && s.Safestack == nil {
    184 			s.Safestack = boolPtr(true)
    185 		}
    186 
    187 		if found, globalSanitizers = removeFromList("cfi", globalSanitizers); found && s.Cfi == nil {
    188 			s.Cfi = boolPtr(true)
    189 		}
    190 
    191 		if found, globalSanitizers = removeFromList("integer_overflow", globalSanitizers); found && s.Integer_overflow == nil {
    192 			if !ctx.AConfig().IntegerOverflowDisabledForPath(ctx.ModuleDir()) {
    193 				s.Integer_overflow = boolPtr(true)
    194 			}
    195 		}
    196 
    197 		if len(globalSanitizers) > 0 {
    198 			ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
    199 		}
    200 
    201 		if found, globalSanitizersDiag = removeFromList("integer_overflow", globalSanitizersDiag); found &&
    202 			s.Diag.Integer_overflow == nil && Bool(s.Integer_overflow) {
    203 			s.Diag.Integer_overflow = boolPtr(true)
    204 		}
    205 
    206 		if len(globalSanitizersDiag) > 0 {
    207 			ctx.ModuleErrorf("unknown global sanitizer diagnostics option %s", globalSanitizersDiag[0])
    208 		}
    209 	}
    210 
    211 	// CFI needs gold linker, and mips toolchain does not have one.
    212 	if !ctx.AConfig().EnableCFI() || ctx.Arch().ArchType == android.Mips || ctx.Arch().ArchType == android.Mips64 {
    213 		s.Cfi = nil
    214 		s.Diag.Cfi = nil
    215 	}
    216 
    217 	// Also disable CFI for arm32 until b/35157333 is fixed.
    218 	if ctx.Arch().ArchType == android.Arm {
    219 		s.Cfi = nil
    220 		s.Diag.Cfi = nil
    221 	}
    222 
    223 	// Also disable CFI if ASAN is enabled.
    224 	if Bool(s.Address) {
    225 		s.Cfi = nil
    226 		s.Diag.Cfi = nil
    227 	}
    228 
    229 	if ctx.staticBinary() {
    230 		s.Address = nil
    231 		s.Coverage = nil
    232 		s.Thread = nil
    233 	}
    234 
    235 	if Bool(s.All_undefined) {
    236 		s.Undefined = nil
    237 	}
    238 
    239 	if !ctx.toolchain().Is64Bit() {
    240 		// TSAN and SafeStack are not supported on 32-bit architectures
    241 		s.Thread = nil
    242 		s.Safestack = nil
    243 		// TODO(ccross): error for compile_multilib = "32"?
    244 	}
    245 
    246 	if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
    247 		Bool(s.Coverage) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0) {
    248 		sanitize.Properties.SanitizerEnabled = true
    249 	}
    250 
    251 	if Bool(s.Coverage) {
    252 		if !Bool(s.Address) {
    253 			ctx.ModuleErrorf(`Use of "coverage" also requires "address"`)
    254 		}
    255 	}
    256 }
    257 
    258 func (sanitize *sanitize) deps(ctx BaseModuleContext, deps Deps) Deps {
    259 	if !sanitize.Properties.SanitizerEnabled { // || c.static() {
    260 		return deps
    261 	}
    262 
    263 	if ctx.Device() {
    264 		if Bool(sanitize.Properties.Sanitize.Address) {
    265 			deps.StaticLibs = append(deps.StaticLibs, asanLibs...)
    266 		}
    267 	}
    268 
    269 	return deps
    270 }
    271 
    272 func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
    273 	if !sanitize.Properties.SanitizerEnabled {
    274 		return flags
    275 	}
    276 
    277 	if !ctx.clang() {
    278 		ctx.ModuleErrorf("Use of sanitizers requires clang")
    279 	}
    280 
    281 	var sanitizers []string
    282 	var diagSanitizers []string
    283 
    284 	if Bool(sanitize.Properties.Sanitize.All_undefined) {
    285 		sanitizers = append(sanitizers, "undefined")
    286 		if ctx.Device() {
    287 			ctx.ModuleErrorf("ubsan is not yet supported on the device")
    288 		}
    289 	} else {
    290 		if Bool(sanitize.Properties.Sanitize.Undefined) {
    291 			sanitizers = append(sanitizers,
    292 				"bool",
    293 				"integer-divide-by-zero",
    294 				"return",
    295 				"returns-nonnull-attribute",
    296 				"shift-exponent",
    297 				"unreachable",
    298 				"vla-bound",
    299 				// TODO(danalbert): The following checks currently have compiler performance issues.
    300 				//"alignment",
    301 				//"bounds",
    302 				//"enum",
    303 				//"float-cast-overflow",
    304 				//"float-divide-by-zero",
    305 				//"nonnull-attribute",
    306 				//"null",
    307 				//"shift-base",
    308 				//"signed-integer-overflow",
    309 				// TODO(danalbert): Fix UB in libc++'s __tree so we can turn this on.
    310 				// https://llvm.org/PR19302
    311 				// http://reviews.llvm.org/D6974
    312 				// "object-size",
    313 			)
    314 		}
    315 		sanitizers = append(sanitizers, sanitize.Properties.Sanitize.Misc_undefined...)
    316 	}
    317 
    318 	if Bool(sanitize.Properties.Sanitize.Diag.Undefined) {
    319 		diagSanitizers = append(diagSanitizers, "undefined")
    320 	}
    321 
    322 	diagSanitizers = append(diagSanitizers, sanitize.Properties.Sanitize.Diag.Misc_undefined...)
    323 
    324 	if Bool(sanitize.Properties.Sanitize.Address) {
    325 		if ctx.Arch().ArchType == android.Arm {
    326 			// Frame pointer based unwinder in ASan requires ARM frame setup.
    327 			// TODO: put in flags?
    328 			flags.RequiredInstructionSet = "arm"
    329 		}
    330 		flags.CFlags = append(flags.CFlags, asanCflags...)
    331 		flags.LdFlags = append(flags.LdFlags, asanLdflags...)
    332 
    333 		if ctx.Host() {
    334 			// -nodefaultlibs (provided with libc++) prevents the driver from linking
    335 			// libraries needed with -fsanitize=address. http://b/18650275 (WAI)
    336 			flags.LdFlags = append(flags.LdFlags, "-lm", "-lpthread")
    337 			flags.LdFlags = append(flags.LdFlags, "-Wl,--no-as-needed")
    338 		} else {
    339 			flags.CFlags = append(flags.CFlags, "-mllvm", "-asan-globals=0")
    340 			flags.DynamicLinker = "/system/bin/linker_asan"
    341 			if flags.Toolchain.Is64Bit() {
    342 				flags.DynamicLinker += "64"
    343 			}
    344 		}
    345 		sanitizers = append(sanitizers, "address")
    346 		diagSanitizers = append(diagSanitizers, "address")
    347 	}
    348 
    349 	if Bool(sanitize.Properties.Sanitize.Coverage) {
    350 		flags.CFlags = append(flags.CFlags, "-fsanitize-coverage=trace-pc-guard")
    351 	}
    352 
    353 	if Bool(sanitize.Properties.Sanitize.Safestack) {
    354 		sanitizers = append(sanitizers, "safe-stack")
    355 	}
    356 
    357 	if Bool(sanitize.Properties.Sanitize.Cfi) {
    358 		if ctx.Arch().ArchType == android.Arm {
    359 			// __cfi_check needs to be built as Thumb (see the code in linker_cfi.cpp). LLVM is not set up
    360 			// to do this on a function basis, so force Thumb on the entire module.
    361 			flags.RequiredInstructionSet = "thumb"
    362 			// Workaround for b/33678192. CFI jumptables need Thumb2 codegen.  Revert when
    363 			// Clang is updated past r290384.
    364 			flags.LdFlags = append(flags.LdFlags, "-march=armv7-a")
    365 		}
    366 		sanitizers = append(sanitizers, "cfi")
    367 		flags.CFlags = append(flags.CFlags, cfiCflags...)
    368 		flags.LdFlags = append(flags.LdFlags, cfiLdflags...)
    369 		flags.ArFlags = append(flags.ArFlags, cfiArflags...)
    370 		if Bool(sanitize.Properties.Sanitize.Diag.Cfi) {
    371 			diagSanitizers = append(diagSanitizers, "cfi")
    372 		}
    373 	}
    374 
    375 	if Bool(sanitize.Properties.Sanitize.Integer_overflow) {
    376 		if !ctx.static() {
    377 			sanitizers = append(sanitizers, "unsigned-integer-overflow")
    378 			sanitizers = append(sanitizers, "signed-integer-overflow")
    379 			flags.CFlags = append(flags.CFlags, intOverflowCflags...)
    380 			if Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) {
    381 				diagSanitizers = append(diagSanitizers, "unsigned-integer-overflow")
    382 				diagSanitizers = append(diagSanitizers, "signed-integer-overflow")
    383 			}
    384 		}
    385 	}
    386 
    387 	if len(sanitizers) > 0 {
    388 		sanitizeArg := "-fsanitize=" + strings.Join(sanitizers, ",")
    389 		flags.CFlags = append(flags.CFlags, sanitizeArg)
    390 		if ctx.Host() {
    391 			flags.CFlags = append(flags.CFlags, "-fno-sanitize-recover=all")
    392 			flags.LdFlags = append(flags.LdFlags, sanitizeArg)
    393 			if ctx.Os() == android.Linux {
    394 				flags.LdFlags = append(flags.LdFlags, "-lrt")
    395 			}
    396 			flags.LdFlags = append(flags.LdFlags, "-ldl")
    397 			// Host sanitizers only link symbols in the final executable, so
    398 			// there will always be undefined symbols in intermediate libraries.
    399 			_, flags.LdFlags = removeFromList("-Wl,--no-undefined", flags.LdFlags)
    400 		} else {
    401 			flags.CFlags = append(flags.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
    402 		}
    403 	}
    404 
    405 	if len(diagSanitizers) > 0 {
    406 		flags.CFlags = append(flags.CFlags, "-fno-sanitize-trap="+strings.Join(diagSanitizers, ","))
    407 	}
    408 	// FIXME: enable RTTI if diag + (cfi or vptr)
    409 
    410 	if sanitize.Properties.Sanitize.Recover != nil {
    411 		flags.CFlags = append(flags.CFlags, "-fsanitize-recover="+
    412 			strings.Join(sanitize.Properties.Sanitize.Recover, ","))
    413 	}
    414 
    415 	// Link a runtime library if needed.
    416 	runtimeLibrary := ""
    417 	if Bool(sanitize.Properties.Sanitize.Address) {
    418 		runtimeLibrary = config.AddressSanitizerRuntimeLibrary(ctx.toolchain())
    419 	} else if len(diagSanitizers) > 0 {
    420 		runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(ctx.toolchain())
    421 	}
    422 
    423 	if runtimeLibrary != "" {
    424 		// ASan runtime library must be the first in the link order.
    425 		flags.libFlags = append([]string{
    426 			"${config.ClangAsanLibDir}/" + runtimeLibrary + ctx.toolchain().ShlibSuffix(),
    427 		}, flags.libFlags...)
    428 		sanitize.runtimeLibrary = runtimeLibrary
    429 
    430 		// When linking against VNDK, use the vendor variant of the runtime lib
    431 		sanitize.androidMkRuntimeLibrary = sanitize.runtimeLibrary
    432 		if ctx.vndk() {
    433 			sanitize.androidMkRuntimeLibrary = sanitize.runtimeLibrary + vendorSuffix
    434 		}
    435 	}
    436 
    437 	blacklist := android.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blacklist)
    438 	if blacklist.Valid() {
    439 		flags.CFlags = append(flags.CFlags, "-fsanitize-blacklist="+blacklist.String())
    440 		flags.CFlagsDeps = append(flags.CFlagsDeps, blacklist.Path())
    441 	}
    442 
    443 	return flags
    444 }
    445 
    446 func (sanitize *sanitize) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
    447 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
    448 		if sanitize.androidMkRuntimeLibrary != "" {
    449 			fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES += "+sanitize.androidMkRuntimeLibrary)
    450 		}
    451 
    452 		return nil
    453 	})
    454 }
    455 
    456 func (sanitize *sanitize) inSanitizerDir() bool {
    457 	return sanitize.Properties.InSanitizerDir
    458 }
    459 
    460 func (sanitize *sanitize) Sanitizer(t sanitizerType) bool {
    461 	if sanitize == nil {
    462 		return false
    463 	}
    464 
    465 	switch t {
    466 	case asan:
    467 		return Bool(sanitize.Properties.Sanitize.Address)
    468 	case tsan:
    469 		return Bool(sanitize.Properties.Sanitize.Thread)
    470 	case intOverflow:
    471 		return Bool(sanitize.Properties.Sanitize.Integer_overflow)
    472 	default:
    473 		panic(fmt.Errorf("unknown sanitizerType %d", t))
    474 	}
    475 }
    476 
    477 func (sanitize *sanitize) SetSanitizer(t sanitizerType, b bool) {
    478 	switch t {
    479 	case asan:
    480 		sanitize.Properties.Sanitize.Address = boolPtr(b)
    481 		if !b {
    482 			sanitize.Properties.Sanitize.Coverage = nil
    483 		}
    484 	case tsan:
    485 		sanitize.Properties.Sanitize.Thread = boolPtr(b)
    486 	case intOverflow:
    487 		sanitize.Properties.Sanitize.Integer_overflow = boolPtr(b)
    488 	default:
    489 		panic(fmt.Errorf("unknown sanitizerType %d", t))
    490 	}
    491 	if b {
    492 		sanitize.Properties.SanitizerEnabled = true
    493 	}
    494 }
    495 
    496 // Propagate asan requirements down from binaries
    497 func sanitizerDepsMutator(t sanitizerType) func(android.TopDownMutatorContext) {
    498 	return func(mctx android.TopDownMutatorContext) {
    499 		if c, ok := mctx.Module().(*Module); ok && c.sanitize.Sanitizer(t) {
    500 			mctx.VisitDepsDepthFirst(func(module blueprint.Module) {
    501 				if d, ok := mctx.Module().(*Module); ok && c.sanitize != nil &&
    502 					!c.sanitize.Properties.Sanitize.Never {
    503 					d.sanitize.Properties.SanitizeDep = true
    504 				}
    505 			})
    506 		}
    507 	}
    508 }
    509 
    510 // Create asan variants for modules that need them
    511 func sanitizerMutator(t sanitizerType) func(android.BottomUpMutatorContext) {
    512 	return func(mctx android.BottomUpMutatorContext) {
    513 		if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
    514 			if c.isDependencyRoot() && c.sanitize.Sanitizer(t) {
    515 				modules := mctx.CreateVariations(t.String())
    516 				modules[0].(*Module).sanitize.SetSanitizer(t, true)
    517 			} else if c.sanitize.Properties.SanitizeDep {
    518 				modules := mctx.CreateVariations("", t.String())
    519 				modules[0].(*Module).sanitize.SetSanitizer(t, false)
    520 				modules[1].(*Module).sanitize.SetSanitizer(t, true)
    521 				modules[0].(*Module).sanitize.Properties.SanitizeDep = false
    522 				modules[1].(*Module).sanitize.Properties.SanitizeDep = false
    523 				if mctx.Device() {
    524 					modules[1].(*Module).sanitize.Properties.InSanitizerDir = true
    525 				} else {
    526 					modules[0].(*Module).Properties.PreventInstall = true
    527 				}
    528 				if mctx.AConfig().EmbeddedInMake() {
    529 					modules[0].(*Module).Properties.HideFromMake = true
    530 				}
    531 			}
    532 			c.sanitize.Properties.SanitizeDep = false
    533 		}
    534 	}
    535 }
    536