blob: 670443bff5959951c934bb42cb16636b9cce4432 [file] [log] [blame]
Colin Cross16b23492016-01-06 14:41:07 -08001// 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
15package cc
16
17import (
18 "fmt"
19 "strings"
20
21 "github.com/google/blueprint"
22
23 "android/soong/common"
24)
25
26type sanitizerType int
27
28func init() {
29 pctx.StaticVariable("clangAsanLibDir", "${clangPath}/lib64/clang/3.8/lib/linux")
30}
31
32const (
33 asan sanitizerType = iota + 1
34 tsan
35)
36
37func (t sanitizerType) String() string {
38 switch t {
39 case asan:
40 return "asan"
41 case tsan:
42 return "tsan"
43 default:
44 panic(fmt.Errorf("unknown sanitizerType %d", t))
45 }
46}
47
48type SanitizeProperties struct {
49 // enable AddressSanitizer, ThreadSanitizer, or UndefinedBehaviorSanitizer
50 Sanitize struct {
51 Never bool `android:"arch_variant"`
52
53 // main sanitizers
54 Address bool `android:"arch_variant"`
55 Thread bool `android:"arch_variant"`
56
57 // local sanitizers
58 Undefined bool `android:"arch_variant"`
59 All_undefined bool `android:"arch_variant"`
60 Misc_undefined []string `android:"arch_variant"`
61 Coverage bool `android:"arch_variant"`
62
63 // value to pass to -fsantitize-recover=
64 Recover []string
65
66 // value to pass to -fsanitize-blacklist
67 Blacklist *string
68 } `android:"arch_variant"`
69
70 SanitizerEnabled bool `blueprint:"mutated"`
71 SanitizeDep bool `blueprint:"mutated"`
Colin Cross30d5f512016-05-03 18:02:42 -070072 InData bool `blueprint:"mutated"`
Colin Cross16b23492016-01-06 14:41:07 -080073}
74
75type sanitize struct {
76 Properties SanitizeProperties
77}
78
79func (sanitize *sanitize) props() []interface{} {
80 return []interface{}{&sanitize.Properties}
81}
82
83func (sanitize *sanitize) begin(ctx BaseModuleContext) {
84 // Don't apply sanitizers to NDK code.
85 if ctx.sdk() {
86 sanitize.Properties.Sanitize.Never = true
87 }
88
89 // Never always wins.
90 if sanitize.Properties.Sanitize.Never {
91 return
92 }
93
94 if ctx.ContainsProperty("sanitize") {
95 sanitize.Properties.SanitizerEnabled = true
96 }
97
98 var globalSanitizers []string
99 if ctx.clang() {
100 if ctx.Host() {
101 globalSanitizers = ctx.AConfig().SanitizeHost()
102 } else {
103 globalSanitizers = ctx.AConfig().SanitizeDevice()
104 }
105 }
106
107 // The sanitizer specified by the environment wins over the module.
108 if len(globalSanitizers) > 0 {
109 // wipe the enabled sanitizers
110 sanitize.Properties = SanitizeProperties{}
111 var found bool
112 if found, globalSanitizers = removeFromList("undefined", globalSanitizers); found {
113 sanitize.Properties.Sanitize.All_undefined = true
114 } else if found, globalSanitizers = removeFromList("default-ub", globalSanitizers); found {
115 sanitize.Properties.Sanitize.Undefined = true
116 }
117
118 if found, globalSanitizers = removeFromList("address", globalSanitizers); found {
119 sanitize.Properties.Sanitize.Address = true
120 }
121
122 if found, globalSanitizers = removeFromList("thread", globalSanitizers); found {
123 sanitize.Properties.Sanitize.Thread = true
124 }
125
126 if found, globalSanitizers = removeFromList("coverage", globalSanitizers); found {
127 sanitize.Properties.Sanitize.Coverage = true
128 }
129
130 if len(globalSanitizers) > 0 {
131 ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
132 }
133 sanitize.Properties.SanitizerEnabled = true
134 }
135
136 if !ctx.toolchain().Is64Bit() && sanitize.Properties.Sanitize.Thread {
137 // TSAN is not supported on 32-bit architectures
138 sanitize.Properties.Sanitize.Thread = false
139 // TODO(ccross): error for compile_multilib = "32"?
140 }
141
142 if sanitize.Properties.Sanitize.Coverage {
143 if !sanitize.Properties.Sanitize.Address {
144 ctx.ModuleErrorf(`Use of "coverage" also requires "address"`)
145 }
146 }
147}
148
149func (sanitize *sanitize) deps(ctx BaseModuleContext, deps Deps) Deps {
150 if !sanitize.Properties.SanitizerEnabled { // || c.static() {
151 return deps
152 }
153
154 if ctx.Device() {
155 deps.SharedLibs = append(deps.SharedLibs, "libdl")
156 if sanitize.Properties.Sanitize.Address {
157 deps.StaticLibs = append(deps.StaticLibs, "libasan")
158 }
159 }
160
161 return deps
162}
163
164func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
165 if !sanitize.Properties.SanitizerEnabled {
166 return flags
167 }
168
169 if !ctx.clang() {
170 ctx.ModuleErrorf("Use of sanitizers requires clang")
171 }
172
173 var sanitizers []string
174
175 if sanitize.Properties.Sanitize.All_undefined {
176 sanitizers = append(sanitizers, "undefined")
177 if ctx.Device() {
178 ctx.ModuleErrorf("ubsan is not yet supported on the device")
179 }
180 } else {
181 if sanitize.Properties.Sanitize.Undefined {
182 sanitizers = append(sanitizers,
183 "bool",
184 "integer-divide-by-zero",
185 "return",
186 "returns-nonnull-attribute",
187 "shift-exponent",
188 "unreachable",
189 "vla-bound",
190 // TODO(danalbert): The following checks currently have compiler performance issues.
191 //"alignment",
192 //"bounds",
193 //"enum",
194 //"float-cast-overflow",
195 //"float-divide-by-zero",
196 //"nonnull-attribute",
197 //"null",
198 //"shift-base",
199 //"signed-integer-overflow",
200 // TODO(danalbert): Fix UB in libc++'s __tree so we can turn this on.
201 // https://llvm.org/PR19302
202 // http://reviews.llvm.org/D6974
203 // "object-size",
204 )
205 }
206 sanitizers = append(sanitizers, sanitize.Properties.Sanitize.Misc_undefined...)
207 }
208
209 if sanitize.Properties.Sanitize.Address {
210 if ctx.Arch().ArchType == common.Arm {
211 // Frame pointer based unwinder in ASan requires ARM frame setup.
212 // TODO: put in flags?
213 flags.RequiredInstructionSet = "arm"
214 }
215 flags.CFlags = append(flags.CFlags, "-fno-omit-frame-pointer")
216 flags.LdFlags = append(flags.LdFlags, "-Wl,-u,__asan_preinit")
217
218 // ASan runtime library must be the first in the link order.
219 runtimeLibrary := ctx.toolchain().AddressSanitizerRuntimeLibrary()
220 if runtimeLibrary != "" {
221 flags.libFlags = append([]string{"${clangAsanLibDir}/" + runtimeLibrary}, flags.libFlags...)
222 }
223 if ctx.Host() {
224 // -nodefaultlibs (provided with libc++) prevents the driver from linking
225 // libraries needed with -fsanitize=address. http://b/18650275 (WAI)
226 flags.LdFlags = append(flags.LdFlags, "-lm", "-lpthread")
227 flags.LdFlags = append(flags.LdFlags, "-Wl,--no-as-needed")
228 } else {
229 flags.CFlags = append(flags.CFlags, "-mllvm", "-asan-globals=0")
230 flags.DynamicLinker = "/system/bin/linker_asan"
231 if flags.Toolchain.Is64Bit() {
232 flags.DynamicLinker += "64"
233 }
234 }
235 sanitizers = append(sanitizers, "address")
236 }
237
238 if sanitize.Properties.Sanitize.Coverage {
239 flags.CFlags = append(flags.CFlags, "-fsanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp")
240 }
241
242 if sanitize.Properties.Sanitize.Recover != nil {
243 flags.CFlags = append(flags.CFlags, "-fsanitize-recover="+
244 strings.Join(sanitize.Properties.Sanitize.Recover, ","))
245 }
246
247 if len(sanitizers) > 0 {
248 sanitizeArg := "-fsanitize=" + strings.Join(sanitizers, ",")
249 flags.CFlags = append(flags.CFlags, sanitizeArg)
250 if ctx.Host() {
251 flags.CFlags = append(flags.CFlags, "-fno-sanitize-recover=all")
252 flags.LdFlags = append(flags.LdFlags, sanitizeArg)
253 flags.LdFlags = append(flags.LdFlags, "-lrt", "-ldl")
254 } else {
255 if !sanitize.Properties.Sanitize.Address {
256 flags.CFlags = append(flags.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
257 }
258 }
259 }
260
261 blacklist := common.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blacklist)
262 if blacklist.Valid() {
263 flags.CFlags = append(flags.CFlags, "-fsanitize-blacklist="+blacklist.String())
264 flags.CFlagsDeps = append(flags.CFlagsDeps, blacklist.Path())
265 }
266
267 return flags
268}
269
Colin Cross30d5f512016-05-03 18:02:42 -0700270func (sanitize *sanitize) inData() bool {
271 return sanitize.Properties.InData
272}
273
Colin Cross16b23492016-01-06 14:41:07 -0800274func (sanitize *sanitize) Sanitizer(t sanitizerType) bool {
275 if sanitize == nil {
276 return false
277 }
278
279 switch t {
280 case asan:
281 return sanitize.Properties.Sanitize.Address
282 case tsan:
283 return sanitize.Properties.Sanitize.Thread
284 default:
285 panic(fmt.Errorf("unknown sanitizerType %d", t))
286 }
287}
288
289func (sanitize *sanitize) SetSanitizer(t sanitizerType, b bool) {
290 switch t {
291 case asan:
292 sanitize.Properties.Sanitize.Address = b
293 case tsan:
294 sanitize.Properties.Sanitize.Thread = b
295 default:
296 panic(fmt.Errorf("unknown sanitizerType %d", t))
297 }
298 if b {
299 sanitize.Properties.SanitizerEnabled = true
300 }
301}
302
303// Propagate asan requirements down from binaries
304func sanitizerDepsMutator(t sanitizerType) func(common.AndroidTopDownMutatorContext) {
305 return func(mctx common.AndroidTopDownMutatorContext) {
306 if c, ok := mctx.Module().(*Module); ok && c.sanitize.Sanitizer(t) {
307 mctx.VisitDepsDepthFirst(func(module blueprint.Module) {
308 if d, ok := mctx.Module().(*Module); ok && c.sanitize != nil &&
309 !c.sanitize.Properties.Sanitize.Never {
310 d.sanitize.Properties.SanitizeDep = true
311 }
312 })
313 }
314 }
315}
316
317// Create asan variants for modules that need them
318func sanitizerMutator(t sanitizerType) func(common.AndroidBottomUpMutatorContext) {
319 return func(mctx common.AndroidBottomUpMutatorContext) {
320 if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
321 if d, ok := c.linker.(baseLinkerInterface); ok && d.isDependencyRoot() && c.sanitize.Sanitizer(t) {
Colin Cross30d5f512016-05-03 18:02:42 -0700322 modules := mctx.CreateVariations(t.String())
323 modules[0].(*Module).sanitize.SetSanitizer(t, true)
324 if mctx.AConfig().EmbeddedInMake() {
325 modules[0].(*Module).sanitize.Properties.InData = true
326 }
Colin Cross16b23492016-01-06 14:41:07 -0800327 } else if c.sanitize.Properties.SanitizeDep {
Colin Cross30d5f512016-05-03 18:02:42 -0700328 if mctx.AConfig().EmbeddedInMake() {
329 modules := mctx.CreateVariations(t.String())
330 modules[0].(*Module).sanitize.SetSanitizer(t, true)
331 modules[0].(*Module).sanitize.Properties.InData = true
332 } else {
333 modules := mctx.CreateVariations("", t.String())
334 modules[0].(*Module).sanitize.SetSanitizer(t, false)
335 modules[1].(*Module).sanitize.SetSanitizer(t, true)
336 modules[1].(*Module).appendVariantName("_" + t.String())
337 modules[0].(*Module).sanitize.Properties.SanitizeDep = false
338 modules[1].(*Module).sanitize.Properties.SanitizeDep = false
339 }
Colin Cross16b23492016-01-06 14:41:07 -0800340 }
341 c.sanitize.Properties.SanitizeDep = false
342 }
343 }
344}