blob: 1893e8b7637fdc000432e502d083128cb110e56c [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
159type ruleProperty struct {
160 fields []string // e.x.: Vndk.Enabled
161 value string // e.x.: true
162}
163
Paul Duffinaebc02a2019-06-27 14:08:51 +0100164// A NeverAllow rule.
165type Rule interface {
166 In(path ...string) Rule
167
168 NotIn(path ...string) Rule
169
170 ModuleType(types ...string) Rule
171
172 NotModuleType(types ...string) Rule
173
174 With(properties, value string) Rule
175
176 Without(properties, value string) Rule
177
178 Because(reason string) Rule
179}
180
Steven Moreland65b3fd92017-12-06 14:18:35 -0800181type rule struct {
182 // User string for why this is a thing.
183 reason string
184
185 paths []string
186 unlessPaths []string
187
Colin Crossc35c5f92019-03-05 15:06:16 -0800188 moduleTypes []string
189 unlessModuleTypes []string
190
Steven Moreland65b3fd92017-12-06 14:18:35 -0800191 props []ruleProperty
192 unlessProps []ruleProperty
193}
194
Paul Duffinaebc02a2019-06-27 14:08:51 +0100195// Create a new NeverAllow rule.
196func NeverAllow() Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800197 return &rule{}
198}
Colin Crossc35c5f92019-03-05 15:06:16 -0800199
Paul Duffinaebc02a2019-06-27 14:08:51 +0100200func (r *rule) In(path ...string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800201 r.paths = append(r.paths, cleanPaths(path)...)
202 return r
203}
Colin Crossc35c5f92019-03-05 15:06:16 -0800204
Paul Duffinaebc02a2019-06-27 14:08:51 +0100205func (r *rule) NotIn(path ...string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800206 r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
207 return r
208}
Colin Crossc35c5f92019-03-05 15:06:16 -0800209
Paul Duffinaebc02a2019-06-27 14:08:51 +0100210func (r *rule) ModuleType(types ...string) Rule {
Colin Crossc35c5f92019-03-05 15:06:16 -0800211 r.moduleTypes = append(r.moduleTypes, types...)
212 return r
213}
214
Paul Duffinaebc02a2019-06-27 14:08:51 +0100215func (r *rule) NotModuleType(types ...string) Rule {
Colin Crossc35c5f92019-03-05 15:06:16 -0800216 r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
217 return r
218}
219
Paul Duffinaebc02a2019-06-27 14:08:51 +0100220func (r *rule) With(properties, value string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800221 r.props = append(r.props, ruleProperty{
222 fields: fieldNamesForProperties(properties),
223 value: value,
224 })
225 return r
226}
Colin Crossc35c5f92019-03-05 15:06:16 -0800227
Paul Duffinaebc02a2019-06-27 14:08:51 +0100228func (r *rule) Without(properties, value string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800229 r.unlessProps = append(r.unlessProps, ruleProperty{
230 fields: fieldNamesForProperties(properties),
231 value: value,
232 })
233 return r
234}
Colin Crossc35c5f92019-03-05 15:06:16 -0800235
Paul Duffinaebc02a2019-06-27 14:08:51 +0100236func (r *rule) Because(reason string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800237 r.reason = reason
238 return r
239}
240
241func (r *rule) String() string {
242 s := "neverallow"
243 for _, v := range r.paths {
244 s += " dir:" + v + "*"
245 }
246 for _, v := range r.unlessPaths {
247 s += " -dir:" + v + "*"
248 }
Colin Crossc35c5f92019-03-05 15:06:16 -0800249 for _, v := range r.moduleTypes {
250 s += " type:" + v
251 }
252 for _, v := range r.unlessModuleTypes {
253 s += " -type:" + v
254 }
Steven Moreland65b3fd92017-12-06 14:18:35 -0800255 for _, v := range r.props {
256 s += " " + strings.Join(v.fields, ".") + "=" + v.value
257 }
258 for _, v := range r.unlessProps {
259 s += " -" + strings.Join(v.fields, ".") + "=" + v.value
260 }
261 if len(r.reason) != 0 {
262 s += " which is restricted because " + r.reason
263 }
264 return s
265}
266
267func (r *rule) appliesToPath(dir string) bool {
268 includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
269 excludePath := hasAnyPrefix(dir, r.unlessPaths)
270 return includePath && !excludePath
271}
272
Colin Crossc35c5f92019-03-05 15:06:16 -0800273func (r *rule) appliesToModuleType(moduleType string) bool {
274 return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
275}
276
Steven Moreland65b3fd92017-12-06 14:18:35 -0800277func (r *rule) appliesToProperties(properties []interface{}) bool {
278 includeProps := hasAllProperties(properties, r.props)
279 excludeProps := hasAnyProperty(properties, r.unlessProps)
280 return includeProps && !excludeProps
281}
282
283// assorted utils
284
285func cleanPaths(paths []string) []string {
286 res := make([]string, len(paths))
287 for i, v := range paths {
288 res[i] = filepath.Clean(v) + "/"
289 }
290 return res
291}
292
293func fieldNamesForProperties(propertyNames string) []string {
294 names := strings.Split(propertyNames, ".")
295 for i, v := range names {
296 names[i] = proptools.FieldNameForProperty(v)
297 }
298 return names
299}
300
301func hasAnyPrefix(s string, prefixes []string) bool {
302 for _, prefix := range prefixes {
303 if strings.HasPrefix(s, prefix) {
304 return true
305 }
306 }
307 return false
308}
309
310func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
311 for _, v := range props {
312 if hasProperty(properties, v) {
313 return true
314 }
315 }
316 return false
317}
318
319func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
320 for _, v := range props {
321 if !hasProperty(properties, v) {
322 return false
323 }
324 }
325 return true
326}
327
328func hasProperty(properties []interface{}, prop ruleProperty) bool {
329 for _, propertyStruct := range properties {
330 propertiesValue := reflect.ValueOf(propertyStruct).Elem()
331 for _, v := range prop.fields {
332 if !propertiesValue.IsValid() {
333 break
334 }
335 propertiesValue = propertiesValue.FieldByName(v)
336 }
337 if !propertiesValue.IsValid() {
338 continue
339 }
340
341 check := func(v string) bool {
342 return prop.value == "*" || prop.value == v
343 }
344
345 if matchValue(propertiesValue, check) {
346 return true
347 }
348 }
349 return false
350}
351
352func matchValue(value reflect.Value, check func(string) bool) bool {
353 if !value.IsValid() {
354 return false
355 }
356
357 if value.Kind() == reflect.Ptr {
358 if value.IsNil() {
359 return check("")
360 }
361 value = value.Elem()
362 }
363
364 switch value.Kind() {
365 case reflect.String:
366 return check(value.String())
367 case reflect.Bool:
368 return check(strconv.FormatBool(value.Bool()))
369 case reflect.Int:
370 return check(strconv.FormatInt(value.Int(), 10))
371 case reflect.Slice:
372 slice, ok := value.Interface().([]string)
373 if !ok {
374 panic("Can only handle slice of string")
375 }
376 for _, v := range slice {
377 if check(v) {
378 return true
379 }
380 }
381 return false
382 }
383
384 panic("Can't handle type: " + value.Kind().String())
385}