blob: b8a01972e886a7b468b1bdf2d227a2068216e6a5 [file] [log] [blame]
Steven Moreland65b3fd92017-12-06 14:18:35 -08001// Copyright 2017 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 android
16
17import (
18 "path/filepath"
19 "reflect"
20 "strconv"
21 "strings"
22
23 "github.com/google/blueprint/proptools"
24)
25
26// "neverallow" rules for the build system.
27//
28// This allows things which aren't related to the build system and are enforced
29// for sanity, in progress code refactors, or policy to be expressed in a
30// straightforward away disjoint from implementations and tests which should
31// work regardless of these restrictions.
32//
33// A module is disallowed if all of the following are true:
Paul Duffinaebc02a2019-06-27 14:08:51 +010034// - it is in one of the "In" paths
35// - it is not in one of the "NotIn" paths
36// - it has all "With" properties matched
Steven Moreland65b3fd92017-12-06 14:18:35 -080037// - - values are matched in their entirety
38// - - nil is interpreted as an empty string
39// - - nested properties are separated with a '.'
40// - - if the property is a list, any of the values in the list being matches
41// counts as a match
Paul Duffinaebc02a2019-06-27 14:08:51 +010042// - it has none of the "Without" properties matched (same rules as above)
Steven Moreland65b3fd92017-12-06 14:18:35 -080043
44func registerNeverallowMutator(ctx RegisterMutatorsContext) {
45 ctx.BottomUp("neverallow", neverallowMutator).Parallel()
46}
47
Paul Duffinaebc02a2019-06-27 14:08:51 +010048var neverallows = []Rule{}
Steven Moreland65b3fd92017-12-06 14:18:35 -080049
Paul Duffinaebc02a2019-06-27 14:08:51 +010050func init() {
51 AddNeverAllowRules(createTrebleRules()...)
52 AddNeverAllowRules(createLibcoreRules()...)
53 AddNeverAllowRules(createJavaDeviceForHostRules()...)
Neil Fullerdf5f3562018-10-21 17:19:10 +010054}
Steven Moreland65b3fd92017-12-06 14:18:35 -080055
Paul Duffinaebc02a2019-06-27 14:08:51 +010056// Add a NeverAllow rule to the set of rules to apply.
57func AddNeverAllowRules(rules ...Rule) {
58 neverallows = append(neverallows, rules...)
59}
60
61func createTrebleRules() []Rule {
62 return []Rule{
63 NeverAllow().
64 In("vendor", "device").
65 With("vndk.enabled", "true").
66 Without("vendor", "true").
67 Because("the VNDK can never contain a library that is device dependent."),
68 NeverAllow().
69 With("vndk.enabled", "true").
70 Without("vendor", "true").
71 Without("owner", "").
72 Because("a VNDK module can never have an owner."),
Steven Moreland65b3fd92017-12-06 14:18:35 -080073
Neil Fullerdf5f3562018-10-21 17:19:10 +010074 // TODO(b/67974785): always enforce the manifest
Paul Duffinaebc02a2019-06-27 14:08:51 +010075 NeverAllow().
76 Without("name", "libhidltransport").
77 With("product_variables.enforce_vintf_manifest.cflags", "*").
78 Because("manifest enforcement should be independent of ."),
Neil Fullerdf5f3562018-10-21 17:19:10 +010079
80 // TODO(b/67975799): vendor code should always use /vendor/bin/sh
Paul Duffinaebc02a2019-06-27 14:08:51 +010081 NeverAllow().
82 Without("name", "libc_bionic_ndk").
83 With("product_variables.treble_linker_namespaces.cflags", "*").
84 Because("nothing should care if linker namespaces are enabled or not"),
Neil Fullerdf5f3562018-10-21 17:19:10 +010085
86 // Example:
Paul Duffinaebc02a2019-06-27 14:08:51 +010087 // *NeverAllow().with("Srcs", "main.cpp"))
Neil Fullerdf5f3562018-10-21 17:19:10 +010088 }
89}
90
Paul Duffinaebc02a2019-06-27 14:08:51 +010091func createLibcoreRules() []Rule {
Neil Fullerdf5f3562018-10-21 17:19:10 +010092 var coreLibraryProjects = []string{
93 "libcore",
94 "external/apache-harmony",
95 "external/apache-xml",
96 "external/bouncycastle",
97 "external/conscrypt",
98 "external/icu",
99 "external/okhttp",
100 "external/wycheproof",
Paul Duffinb6c6bdd2019-06-07 11:43:55 +0100101
102 // Not really a core library but still needs access to same capabilities.
103 "development",
Neil Fullerdf5f3562018-10-21 17:19:10 +0100104 }
105
Paul Duffina3d09862019-06-11 13:40:47 +0100106 // Core library constraints. The sdk_version: "none" can only be used in core library projects.
107 // Access to core library targets is restricted using visibility rules.
Paul Duffinaebc02a2019-06-27 14:08:51 +0100108 rules := []Rule{
109 NeverAllow().
110 NotIn(coreLibraryProjects...).
111 With("sdk_version", "none"),
Neil Fullerdf5f3562018-10-21 17:19:10 +0100112 }
113
Neil Fullerdf5f3562018-10-21 17:19:10 +0100114 return rules
Steven Moreland65b3fd92017-12-06 14:18:35 -0800115}
116
Paul Duffinaebc02a2019-06-27 14:08:51 +0100117func createJavaDeviceForHostRules() []Rule {
Colin Crossc35c5f92019-03-05 15:06:16 -0800118 javaDeviceForHostProjectsWhitelist := []string{
Colin Cross97add502019-04-11 14:07:38 -0700119 "external/guava",
Colin Crossc35c5f92019-03-05 15:06:16 -0800120 "external/robolectric-shadows",
121 "framework/layoutlib",
122 }
123
Paul Duffinaebc02a2019-06-27 14:08:51 +0100124 return []Rule{
125 NeverAllow().
126 NotIn(javaDeviceForHostProjectsWhitelist...).
127 ModuleType("java_device_for_host", "java_host_for_device").
128 Because("java_device_for_host can only be used in whitelisted projects"),
Colin Crossc35c5f92019-03-05 15:06:16 -0800129 }
130}
131
Steven Moreland65b3fd92017-12-06 14:18:35 -0800132func neverallowMutator(ctx BottomUpMutatorContext) {
133 m, ok := ctx.Module().(Module)
134 if !ok {
135 return
136 }
137
138 dir := ctx.ModuleDir() + "/"
139 properties := m.GetProperties()
140
Paul Duffinaebc02a2019-06-27 14:08:51 +0100141 for _, r := range neverallows {
142 n := r.(*rule)
Steven Moreland65b3fd92017-12-06 14:18:35 -0800143 if !n.appliesToPath(dir) {
144 continue
145 }
146
Colin Crossc35c5f92019-03-05 15:06:16 -0800147 if !n.appliesToModuleType(ctx.ModuleType()) {
148 continue
149 }
150
Steven Moreland65b3fd92017-12-06 14:18:35 -0800151 if !n.appliesToProperties(properties) {
152 continue
153 }
154
155 ctx.ModuleErrorf("violates " + n.String())
156 }
157}
158
Paul Duffin73bf0542019-07-12 14:12:49 +0100159type ValueMatcher interface {
160 test(string) bool
161 String() string
162}
163
164type equalMatcher struct {
165 expected string
166}
167
168func (m *equalMatcher) test(value string) bool {
169 return m.expected == value
170}
171
172func (m *equalMatcher) String() string {
173 return "=" + m.expected
174}
175
176type anyMatcher struct {
177}
178
179func (m *anyMatcher) test(value string) bool {
180 return true
181}
182
183func (m *anyMatcher) String() string {
184 return "=*"
185}
186
187var anyMatcherInstance = &anyMatcher{}
188
Steven Moreland65b3fd92017-12-06 14:18:35 -0800189type ruleProperty struct {
Paul Duffin73bf0542019-07-12 14:12:49 +0100190 fields []string // e.x.: Vndk.Enabled
191 matcher ValueMatcher
Steven Moreland65b3fd92017-12-06 14:18:35 -0800192}
193
Paul Duffinaebc02a2019-06-27 14:08:51 +0100194// A NeverAllow rule.
195type Rule interface {
196 In(path ...string) Rule
197
198 NotIn(path ...string) Rule
199
200 ModuleType(types ...string) Rule
201
202 NotModuleType(types ...string) Rule
203
204 With(properties, value string) Rule
205
206 Without(properties, value string) Rule
207
208 Because(reason string) Rule
209}
210
Steven Moreland65b3fd92017-12-06 14:18:35 -0800211type rule struct {
212 // User string for why this is a thing.
213 reason string
214
215 paths []string
216 unlessPaths []string
217
Colin Crossc35c5f92019-03-05 15:06:16 -0800218 moduleTypes []string
219 unlessModuleTypes []string
220
Steven Moreland65b3fd92017-12-06 14:18:35 -0800221 props []ruleProperty
222 unlessProps []ruleProperty
223}
224
Paul Duffinaebc02a2019-06-27 14:08:51 +0100225// Create a new NeverAllow rule.
226func NeverAllow() Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800227 return &rule{}
228}
Colin Crossc35c5f92019-03-05 15:06:16 -0800229
Paul Duffinaebc02a2019-06-27 14:08:51 +0100230func (r *rule) In(path ...string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800231 r.paths = append(r.paths, cleanPaths(path)...)
232 return r
233}
Colin Crossc35c5f92019-03-05 15:06:16 -0800234
Paul Duffinaebc02a2019-06-27 14:08:51 +0100235func (r *rule) NotIn(path ...string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800236 r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
237 return r
238}
Colin Crossc35c5f92019-03-05 15:06:16 -0800239
Paul Duffinaebc02a2019-06-27 14:08:51 +0100240func (r *rule) ModuleType(types ...string) Rule {
Colin Crossc35c5f92019-03-05 15:06:16 -0800241 r.moduleTypes = append(r.moduleTypes, types...)
242 return r
243}
244
Paul Duffinaebc02a2019-06-27 14:08:51 +0100245func (r *rule) NotModuleType(types ...string) Rule {
Colin Crossc35c5f92019-03-05 15:06:16 -0800246 r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
247 return r
248}
249
Paul Duffinaebc02a2019-06-27 14:08:51 +0100250func (r *rule) With(properties, value string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800251 r.props = append(r.props, ruleProperty{
Paul Duffin73bf0542019-07-12 14:12:49 +0100252 fields: fieldNamesForProperties(properties),
253 matcher: selectMatcher(value),
Steven Moreland65b3fd92017-12-06 14:18:35 -0800254 })
255 return r
256}
Colin Crossc35c5f92019-03-05 15:06:16 -0800257
Paul Duffinaebc02a2019-06-27 14:08:51 +0100258func (r *rule) Without(properties, value string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800259 r.unlessProps = append(r.unlessProps, ruleProperty{
Paul Duffin73bf0542019-07-12 14:12:49 +0100260 fields: fieldNamesForProperties(properties),
261 matcher: selectMatcher(value),
Steven Moreland65b3fd92017-12-06 14:18:35 -0800262 })
263 return r
264}
Colin Crossc35c5f92019-03-05 15:06:16 -0800265
Paul Duffin73bf0542019-07-12 14:12:49 +0100266func selectMatcher(expected string) ValueMatcher {
267 if expected == "*" {
268 return anyMatcherInstance
269 }
270 return &equalMatcher{expected: expected}
271}
272
Paul Duffinaebc02a2019-06-27 14:08:51 +0100273func (r *rule) Because(reason string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800274 r.reason = reason
275 return r
276}
277
278func (r *rule) String() string {
279 s := "neverallow"
280 for _, v := range r.paths {
281 s += " dir:" + v + "*"
282 }
283 for _, v := range r.unlessPaths {
284 s += " -dir:" + v + "*"
285 }
Colin Crossc35c5f92019-03-05 15:06:16 -0800286 for _, v := range r.moduleTypes {
287 s += " type:" + v
288 }
289 for _, v := range r.unlessModuleTypes {
290 s += " -type:" + v
291 }
Steven Moreland65b3fd92017-12-06 14:18:35 -0800292 for _, v := range r.props {
Paul Duffin73bf0542019-07-12 14:12:49 +0100293 s += " " + strings.Join(v.fields, ".") + v.matcher.String()
Steven Moreland65b3fd92017-12-06 14:18:35 -0800294 }
295 for _, v := range r.unlessProps {
Paul Duffin73bf0542019-07-12 14:12:49 +0100296 s += " -" + strings.Join(v.fields, ".") + v.matcher.String()
Steven Moreland65b3fd92017-12-06 14:18:35 -0800297 }
298 if len(r.reason) != 0 {
299 s += " which is restricted because " + r.reason
300 }
301 return s
302}
303
304func (r *rule) appliesToPath(dir string) bool {
305 includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
306 excludePath := hasAnyPrefix(dir, r.unlessPaths)
307 return includePath && !excludePath
308}
309
Colin Crossc35c5f92019-03-05 15:06:16 -0800310func (r *rule) appliesToModuleType(moduleType string) bool {
311 return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
312}
313
Steven Moreland65b3fd92017-12-06 14:18:35 -0800314func (r *rule) appliesToProperties(properties []interface{}) bool {
315 includeProps := hasAllProperties(properties, r.props)
316 excludeProps := hasAnyProperty(properties, r.unlessProps)
317 return includeProps && !excludeProps
318}
319
320// assorted utils
321
322func cleanPaths(paths []string) []string {
323 res := make([]string, len(paths))
324 for i, v := range paths {
325 res[i] = filepath.Clean(v) + "/"
326 }
327 return res
328}
329
330func fieldNamesForProperties(propertyNames string) []string {
331 names := strings.Split(propertyNames, ".")
332 for i, v := range names {
333 names[i] = proptools.FieldNameForProperty(v)
334 }
335 return names
336}
337
338func hasAnyPrefix(s string, prefixes []string) bool {
339 for _, prefix := range prefixes {
340 if strings.HasPrefix(s, prefix) {
341 return true
342 }
343 }
344 return false
345}
346
347func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
348 for _, v := range props {
349 if hasProperty(properties, v) {
350 return true
351 }
352 }
353 return false
354}
355
356func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
357 for _, v := range props {
358 if !hasProperty(properties, v) {
359 return false
360 }
361 }
362 return true
363}
364
365func hasProperty(properties []interface{}, prop ruleProperty) bool {
366 for _, propertyStruct := range properties {
367 propertiesValue := reflect.ValueOf(propertyStruct).Elem()
368 for _, v := range prop.fields {
369 if !propertiesValue.IsValid() {
370 break
371 }
372 propertiesValue = propertiesValue.FieldByName(v)
373 }
374 if !propertiesValue.IsValid() {
375 continue
376 }
377
Paul Duffin73bf0542019-07-12 14:12:49 +0100378 check := func(value string) bool {
379 return prop.matcher.test(value)
Steven Moreland65b3fd92017-12-06 14:18:35 -0800380 }
381
382 if matchValue(propertiesValue, check) {
383 return true
384 }
385 }
386 return false
387}
388
389func matchValue(value reflect.Value, check func(string) bool) bool {
390 if !value.IsValid() {
391 return false
392 }
393
394 if value.Kind() == reflect.Ptr {
395 if value.IsNil() {
396 return check("")
397 }
398 value = value.Elem()
399 }
400
401 switch value.Kind() {
402 case reflect.String:
403 return check(value.String())
404 case reflect.Bool:
405 return check(strconv.FormatBool(value.Bool()))
406 case reflect.Int:
407 return check(strconv.FormatInt(value.Int(), 10))
408 case reflect.Slice:
409 slice, ok := value.Interface().([]string)
410 if !ok {
411 panic("Can only handle slice of string")
412 }
413 for _, v := range slice {
414 if check(v) {
415 return true
416 }
417 }
418 return false
419 }
420
421 panic("Can't handle type: " + value.Kind().String())
422}