blob: af91676674d03cbb6dddb32b59b3cdd591ad6514 [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 Duffin730f2a52019-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 Duffin730f2a52019-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 Duffin730f2a52019-06-27 14:08:51 +010048var neverallows = []Rule{}
Steven Moreland65b3fd92017-12-06 14:18:35 -080049
Paul Duffin730f2a52019-06-27 14:08:51 +010050func init() {
51 AddNeverAllowRules(createTrebleRules()...)
52 AddNeverAllowRules(createLibcoreRules()...)
53 AddNeverAllowRules(createMediaRules()...)
54 AddNeverAllowRules(createJavaDeviceForHostRules()...)
Neil Fullerdf5f3562018-10-21 17:19:10 +010055}
Steven Moreland65b3fd92017-12-06 14:18:35 -080056
Paul Duffin730f2a52019-06-27 14:08:51 +010057// Add a NeverAllow rule to the set of rules to apply.
58func AddNeverAllowRules(rules ...Rule) {
59 neverallows = append(neverallows, rules...)
60}
61
62func createTrebleRules() []Rule {
63 return []Rule{
64 NeverAllow().
65 In("vendor", "device").
66 With("vndk.enabled", "true").
67 Without("vendor", "true").
68 Because("the VNDK can never contain a library that is device dependent."),
69 NeverAllow().
70 With("vndk.enabled", "true").
71 Without("vendor", "true").
72 Without("owner", "").
73 Because("a VNDK module can never have an owner."),
Steven Moreland65b3fd92017-12-06 14:18:35 -080074
Neil Fullerdf5f3562018-10-21 17:19:10 +010075 // TODO(b/67974785): always enforce the manifest
Paul Duffin730f2a52019-06-27 14:08:51 +010076 NeverAllow().
77 Without("name", "libhidltransport-impl-internal").
78 With("product_variables.enforce_vintf_manifest.cflags", "*").
79 Because("manifest enforcement should be independent of ."),
Neil Fullerdf5f3562018-10-21 17:19:10 +010080
81 // TODO(b/67975799): vendor code should always use /vendor/bin/sh
Paul Duffin730f2a52019-06-27 14:08:51 +010082 NeverAllow().
83 Without("name", "libc_bionic_ndk").
84 With("product_variables.treble_linker_namespaces.cflags", "*").
85 Because("nothing should care if linker namespaces are enabled or not"),
Neil Fullerdf5f3562018-10-21 17:19:10 +010086
87 // Example:
Paul Duffin730f2a52019-06-27 14:08:51 +010088 // *NeverAllow().with("Srcs", "main.cpp"))
Neil Fullerdf5f3562018-10-21 17:19:10 +010089 }
90}
91
Paul Duffin730f2a52019-06-27 14:08:51 +010092func createLibcoreRules() []Rule {
Neil Fullerdf5f3562018-10-21 17:19:10 +010093 var coreLibraryProjects = []string{
94 "libcore",
95 "external/apache-harmony",
96 "external/apache-xml",
97 "external/bouncycastle",
98 "external/conscrypt",
99 "external/icu",
100 "external/okhttp",
101 "external/wycheproof",
Paul Duffinb6c6bdd2019-06-07 11:43:55 +0100102
103 // Not really a core library but still needs access to same capabilities.
104 "development",
Neil Fullerdf5f3562018-10-21 17:19:10 +0100105 }
106
Paul Duffina3d09862019-06-11 13:40:47 +0100107 // Core library constraints. The sdk_version: "none" can only be used in core library projects.
108 // Access to core library targets is restricted using visibility rules.
Paul Duffin730f2a52019-06-27 14:08:51 +0100109 rules := []Rule{
110 NeverAllow().
111 NotIn(coreLibraryProjects...).
112 With("sdk_version", "none"),
Neil Fullerdf5f3562018-10-21 17:19:10 +0100113 }
114
Neil Fullerdf5f3562018-10-21 17:19:10 +0100115 return rules
Steven Moreland65b3fd92017-12-06 14:18:35 -0800116}
117
Paul Duffin730f2a52019-06-27 14:08:51 +0100118func createMediaRules() []Rule {
119 return []Rule{
120 NeverAllow().
121 With("libs", "updatable-media").
122 Because("updatable-media includes private APIs. Use updatable_media_stubs instead."),
Dongwon Kang50a299f2019-02-04 09:00:51 -0800123 }
124}
125
Paul Duffin730f2a52019-06-27 14:08:51 +0100126func createJavaDeviceForHostRules() []Rule {
Colin Crossfd4f7432019-03-05 15:06:16 -0800127 javaDeviceForHostProjectsWhitelist := []string{
Colin Crossb5191a52019-04-11 14:07:38 -0700128 "external/guava",
Colin Crossfd4f7432019-03-05 15:06:16 -0800129 "external/robolectric-shadows",
130 "framework/layoutlib",
131 }
132
Paul Duffin730f2a52019-06-27 14:08:51 +0100133 return []Rule{
134 NeverAllow().
135 NotIn(javaDeviceForHostProjectsWhitelist...).
136 ModuleType("java_device_for_host", "java_host_for_device").
137 Because("java_device_for_host can only be used in whitelisted projects"),
Colin Crossfd4f7432019-03-05 15:06:16 -0800138 }
139}
140
Steven Moreland65b3fd92017-12-06 14:18:35 -0800141func neverallowMutator(ctx BottomUpMutatorContext) {
142 m, ok := ctx.Module().(Module)
143 if !ok {
144 return
145 }
146
147 dir := ctx.ModuleDir() + "/"
148 properties := m.GetProperties()
149
Paul Duffin730f2a52019-06-27 14:08:51 +0100150 for _, r := range neverallows {
151 n := r.(*rule)
Steven Moreland65b3fd92017-12-06 14:18:35 -0800152 if !n.appliesToPath(dir) {
153 continue
154 }
155
Colin Crossfd4f7432019-03-05 15:06:16 -0800156 if !n.appliesToModuleType(ctx.ModuleType()) {
157 continue
158 }
159
Steven Moreland65b3fd92017-12-06 14:18:35 -0800160 if !n.appliesToProperties(properties) {
161 continue
162 }
163
164 ctx.ModuleErrorf("violates " + n.String())
165 }
166}
167
168type ruleProperty struct {
169 fields []string // e.x.: Vndk.Enabled
170 value string // e.x.: true
171}
172
Paul Duffin730f2a52019-06-27 14:08:51 +0100173// A NeverAllow rule.
174type Rule interface {
175 In(path ...string) Rule
176
177 NotIn(path ...string) Rule
178
179 ModuleType(types ...string) Rule
180
181 NotModuleType(types ...string) Rule
182
183 With(properties, value string) Rule
184
185 Without(properties, value string) Rule
186
187 Because(reason string) Rule
188}
189
Steven Moreland65b3fd92017-12-06 14:18:35 -0800190type rule struct {
191 // User string for why this is a thing.
192 reason string
193
194 paths []string
195 unlessPaths []string
196
Colin Crossfd4f7432019-03-05 15:06:16 -0800197 moduleTypes []string
198 unlessModuleTypes []string
199
Steven Moreland65b3fd92017-12-06 14:18:35 -0800200 props []ruleProperty
201 unlessProps []ruleProperty
202}
203
Paul Duffin730f2a52019-06-27 14:08:51 +0100204// Create a new NeverAllow rule.
205func NeverAllow() Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800206 return &rule{}
207}
Colin Crossfd4f7432019-03-05 15:06:16 -0800208
Paul Duffin730f2a52019-06-27 14:08:51 +0100209func (r *rule) In(path ...string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800210 r.paths = append(r.paths, cleanPaths(path)...)
211 return r
212}
Colin Crossfd4f7432019-03-05 15:06:16 -0800213
Paul Duffin730f2a52019-06-27 14:08:51 +0100214func (r *rule) NotIn(path ...string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800215 r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
216 return r
217}
Colin Crossfd4f7432019-03-05 15:06:16 -0800218
Paul Duffin730f2a52019-06-27 14:08:51 +0100219func (r *rule) ModuleType(types ...string) Rule {
Colin Crossfd4f7432019-03-05 15:06:16 -0800220 r.moduleTypes = append(r.moduleTypes, types...)
221 return r
222}
223
Paul Duffin730f2a52019-06-27 14:08:51 +0100224func (r *rule) NotModuleType(types ...string) Rule {
Colin Crossfd4f7432019-03-05 15:06:16 -0800225 r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
226 return r
227}
228
Paul Duffin730f2a52019-06-27 14:08:51 +0100229func (r *rule) With(properties, value string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800230 r.props = append(r.props, ruleProperty{
231 fields: fieldNamesForProperties(properties),
232 value: value,
233 })
234 return r
235}
Colin Crossfd4f7432019-03-05 15:06:16 -0800236
Paul Duffin730f2a52019-06-27 14:08:51 +0100237func (r *rule) Without(properties, value string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800238 r.unlessProps = append(r.unlessProps, ruleProperty{
239 fields: fieldNamesForProperties(properties),
240 value: value,
241 })
242 return r
243}
Colin Crossfd4f7432019-03-05 15:06:16 -0800244
Paul Duffin730f2a52019-06-27 14:08:51 +0100245func (r *rule) Because(reason string) Rule {
Steven Moreland65b3fd92017-12-06 14:18:35 -0800246 r.reason = reason
247 return r
248}
249
250func (r *rule) String() string {
251 s := "neverallow"
252 for _, v := range r.paths {
253 s += " dir:" + v + "*"
254 }
255 for _, v := range r.unlessPaths {
256 s += " -dir:" + v + "*"
257 }
Colin Crossfd4f7432019-03-05 15:06:16 -0800258 for _, v := range r.moduleTypes {
259 s += " type:" + v
260 }
261 for _, v := range r.unlessModuleTypes {
262 s += " -type:" + v
263 }
Steven Moreland65b3fd92017-12-06 14:18:35 -0800264 for _, v := range r.props {
265 s += " " + strings.Join(v.fields, ".") + "=" + v.value
266 }
267 for _, v := range r.unlessProps {
268 s += " -" + strings.Join(v.fields, ".") + "=" + v.value
269 }
270 if len(r.reason) != 0 {
271 s += " which is restricted because " + r.reason
272 }
273 return s
274}
275
276func (r *rule) appliesToPath(dir string) bool {
277 includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
278 excludePath := hasAnyPrefix(dir, r.unlessPaths)
279 return includePath && !excludePath
280}
281
Colin Crossfd4f7432019-03-05 15:06:16 -0800282func (r *rule) appliesToModuleType(moduleType string) bool {
283 return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
284}
285
Steven Moreland65b3fd92017-12-06 14:18:35 -0800286func (r *rule) appliesToProperties(properties []interface{}) bool {
287 includeProps := hasAllProperties(properties, r.props)
288 excludeProps := hasAnyProperty(properties, r.unlessProps)
289 return includeProps && !excludeProps
290}
291
292// assorted utils
293
294func cleanPaths(paths []string) []string {
295 res := make([]string, len(paths))
296 for i, v := range paths {
297 res[i] = filepath.Clean(v) + "/"
298 }
299 return res
300}
301
302func fieldNamesForProperties(propertyNames string) []string {
303 names := strings.Split(propertyNames, ".")
304 for i, v := range names {
305 names[i] = proptools.FieldNameForProperty(v)
306 }
307 return names
308}
309
310func hasAnyPrefix(s string, prefixes []string) bool {
311 for _, prefix := range prefixes {
312 if strings.HasPrefix(s, prefix) {
313 return true
314 }
315 }
316 return false
317}
318
319func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
320 for _, v := range props {
321 if hasProperty(properties, v) {
322 return true
323 }
324 }
325 return false
326}
327
328func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
329 for _, v := range props {
330 if !hasProperty(properties, v) {
331 return false
332 }
333 }
334 return true
335}
336
337func hasProperty(properties []interface{}, prop ruleProperty) bool {
338 for _, propertyStruct := range properties {
339 propertiesValue := reflect.ValueOf(propertyStruct).Elem()
340 for _, v := range prop.fields {
341 if !propertiesValue.IsValid() {
342 break
343 }
344 propertiesValue = propertiesValue.FieldByName(v)
345 }
346 if !propertiesValue.IsValid() {
347 continue
348 }
349
350 check := func(v string) bool {
351 return prop.value == "*" || prop.value == v
352 }
353
354 if matchValue(propertiesValue, check) {
355 return true
356 }
357 }
358 return false
359}
360
361func matchValue(value reflect.Value, check func(string) bool) bool {
362 if !value.IsValid() {
363 return false
364 }
365
366 if value.Kind() == reflect.Ptr {
367 if value.IsNil() {
368 return check("")
369 }
370 value = value.Elem()
371 }
372
373 switch value.Kind() {
374 case reflect.String:
375 return check(value.String())
376 case reflect.Bool:
377 return check(strconv.FormatBool(value.Bool()))
378 case reflect.Int:
379 return check(strconv.FormatInt(value.Int(), 10))
380 case reflect.Slice:
381 slice, ok := value.Interface().([]string)
382 if !ok {
383 panic("Can only handle slice of string")
384 }
385 for _, v := range slice {
386 if check(v) {
387 return true
388 }
389 }
390 return false
391 }
392
393 panic("Can't handle type: " + value.Kind().String())
394}