blob: ee3bf4a5eaa22d405b9be27448d9f8709b0f55ab [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:
34// - 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
37// - - 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
42// - it has none of the "without" properties matched (same rules as above)
43
44func registerNeverallowMutator(ctx RegisterMutatorsContext) {
45 ctx.BottomUp("neverallow", neverallowMutator).Parallel()
46}
47
Neil Fullerdf5f3562018-10-21 17:19:10 +010048var neverallows = createNeverAllows()
Steven Moreland65b3fd92017-12-06 14:18:35 -080049
Neil Fullerdf5f3562018-10-21 17:19:10 +010050func createNeverAllows() []*rule {
51 rules := []*rule{}
52 rules = append(rules, createTrebleRules()...)
53 rules = append(rules, createLibcoreRules()...)
Colin Crossc35c5f92019-03-05 15:06:16 -080054 rules = append(rules, createJavaDeviceForHostRules()...)
Neil Fullerdf5f3562018-10-21 17:19:10 +010055 return rules
56}
Steven Moreland65b3fd92017-12-06 14:18:35 -080057
Neil Fullerdf5f3562018-10-21 17:19:10 +010058func createTrebleRules() []*rule {
59 return []*rule{
60 neverallow().
61 in("vendor", "device").
62 with("vndk.enabled", "true").
63 without("vendor", "true").
64 because("the VNDK can never contain a library that is device dependent."),
65 neverallow().
66 with("vndk.enabled", "true").
67 without("vendor", "true").
68 without("owner", "").
69 because("a VNDK module can never have an owner."),
Steven Moreland65b3fd92017-12-06 14:18:35 -080070
Neil Fullerdf5f3562018-10-21 17:19:10 +010071 // TODO(b/67974785): always enforce the manifest
72 neverallow().
73 without("name", "libhidltransport").
74 with("product_variables.enforce_vintf_manifest.cflags", "*").
75 because("manifest enforcement should be independent of ."),
76
77 // TODO(b/67975799): vendor code should always use /vendor/bin/sh
78 neverallow().
79 without("name", "libc_bionic_ndk").
80 with("product_variables.treble_linker_namespaces.cflags", "*").
81 because("nothing should care if linker namespaces are enabled or not"),
82
83 // Example:
84 // *neverallow().with("Srcs", "main.cpp"))
85 }
86}
87
88func createLibcoreRules() []*rule {
89 var coreLibraryProjects = []string{
90 "libcore",
91 "external/apache-harmony",
92 "external/apache-xml",
93 "external/bouncycastle",
94 "external/conscrypt",
95 "external/icu",
96 "external/okhttp",
97 "external/wycheproof",
Paul Duffinb6c6bdd2019-06-07 11:43:55 +010098
99 // Not really a core library but still needs access to same capabilities.
100 "development",
Neil Fullerdf5f3562018-10-21 17:19:10 +0100101 }
102
Paul Duffinff5a1772019-04-30 13:13:39 +0100103 // Core library constraints. The no_standard_libs can only be used in core
104 // library projects. Access to core library targets is restricted using
105 // visibility rules.
Neil Fullerdf5f3562018-10-21 17:19:10 +0100106 rules := []*rule{
107 neverallow().
Paul Duffinb6c6bdd2019-06-07 11:43:55 +0100108 notIn(coreLibraryProjects...).
Neil Fullerdf5f3562018-10-21 17:19:10 +0100109 with("no_standard_libs", "true"),
110 }
111
Neil Fullerdf5f3562018-10-21 17:19:10 +0100112 return rules
Steven Moreland65b3fd92017-12-06 14:18:35 -0800113}
114
Colin Crossc35c5f92019-03-05 15:06:16 -0800115func createJavaDeviceForHostRules() []*rule {
116 javaDeviceForHostProjectsWhitelist := []string{
Colin Cross97add502019-04-11 14:07:38 -0700117 "external/guava",
Colin Crossc35c5f92019-03-05 15:06:16 -0800118 "external/robolectric-shadows",
119 "framework/layoutlib",
120 }
121
122 return []*rule{
123 neverallow().
124 notIn(javaDeviceForHostProjectsWhitelist...).
125 moduleType("java_device_for_host", "java_host_for_device").
126 because("java_device_for_host can only be used in whitelisted projects"),
127 }
128}
129
Steven Moreland65b3fd92017-12-06 14:18:35 -0800130func neverallowMutator(ctx BottomUpMutatorContext) {
131 m, ok := ctx.Module().(Module)
132 if !ok {
133 return
134 }
135
136 dir := ctx.ModuleDir() + "/"
137 properties := m.GetProperties()
138
139 for _, n := range neverallows {
140 if !n.appliesToPath(dir) {
141 continue
142 }
143
Colin Crossc35c5f92019-03-05 15:06:16 -0800144 if !n.appliesToModuleType(ctx.ModuleType()) {
145 continue
146 }
147
Steven Moreland65b3fd92017-12-06 14:18:35 -0800148 if !n.appliesToProperties(properties) {
149 continue
150 }
151
152 ctx.ModuleErrorf("violates " + n.String())
153 }
154}
155
156type ruleProperty struct {
157 fields []string // e.x.: Vndk.Enabled
158 value string // e.x.: true
159}
160
161type rule struct {
162 // User string for why this is a thing.
163 reason string
164
165 paths []string
166 unlessPaths []string
167
Colin Crossc35c5f92019-03-05 15:06:16 -0800168 moduleTypes []string
169 unlessModuleTypes []string
170
Steven Moreland65b3fd92017-12-06 14:18:35 -0800171 props []ruleProperty
172 unlessProps []ruleProperty
173}
174
175func neverallow() *rule {
176 return &rule{}
177}
Colin Crossc35c5f92019-03-05 15:06:16 -0800178
Steven Moreland65b3fd92017-12-06 14:18:35 -0800179func (r *rule) in(path ...string) *rule {
180 r.paths = append(r.paths, cleanPaths(path)...)
181 return r
182}
Colin Crossc35c5f92019-03-05 15:06:16 -0800183
Steven Moreland65b3fd92017-12-06 14:18:35 -0800184func (r *rule) notIn(path ...string) *rule {
185 r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
186 return r
187}
Colin Crossc35c5f92019-03-05 15:06:16 -0800188
189func (r *rule) moduleType(types ...string) *rule {
190 r.moduleTypes = append(r.moduleTypes, types...)
191 return r
192}
193
194func (r *rule) notModuleType(types ...string) *rule {
195 r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
196 return r
197}
198
Steven Moreland65b3fd92017-12-06 14:18:35 -0800199func (r *rule) with(properties, value string) *rule {
200 r.props = append(r.props, ruleProperty{
201 fields: fieldNamesForProperties(properties),
202 value: value,
203 })
204 return r
205}
Colin Crossc35c5f92019-03-05 15:06:16 -0800206
Steven Moreland65b3fd92017-12-06 14:18:35 -0800207func (r *rule) without(properties, value string) *rule {
208 r.unlessProps = append(r.unlessProps, ruleProperty{
209 fields: fieldNamesForProperties(properties),
210 value: value,
211 })
212 return r
213}
Colin Crossc35c5f92019-03-05 15:06:16 -0800214
Steven Moreland65b3fd92017-12-06 14:18:35 -0800215func (r *rule) because(reason string) *rule {
216 r.reason = reason
217 return r
218}
219
220func (r *rule) String() string {
221 s := "neverallow"
222 for _, v := range r.paths {
223 s += " dir:" + v + "*"
224 }
225 for _, v := range r.unlessPaths {
226 s += " -dir:" + v + "*"
227 }
Colin Crossc35c5f92019-03-05 15:06:16 -0800228 for _, v := range r.moduleTypes {
229 s += " type:" + v
230 }
231 for _, v := range r.unlessModuleTypes {
232 s += " -type:" + v
233 }
Steven Moreland65b3fd92017-12-06 14:18:35 -0800234 for _, v := range r.props {
235 s += " " + strings.Join(v.fields, ".") + "=" + v.value
236 }
237 for _, v := range r.unlessProps {
238 s += " -" + strings.Join(v.fields, ".") + "=" + v.value
239 }
240 if len(r.reason) != 0 {
241 s += " which is restricted because " + r.reason
242 }
243 return s
244}
245
246func (r *rule) appliesToPath(dir string) bool {
247 includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
248 excludePath := hasAnyPrefix(dir, r.unlessPaths)
249 return includePath && !excludePath
250}
251
Colin Crossc35c5f92019-03-05 15:06:16 -0800252func (r *rule) appliesToModuleType(moduleType string) bool {
253 return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
254}
255
Steven Moreland65b3fd92017-12-06 14:18:35 -0800256func (r *rule) appliesToProperties(properties []interface{}) bool {
257 includeProps := hasAllProperties(properties, r.props)
258 excludeProps := hasAnyProperty(properties, r.unlessProps)
259 return includeProps && !excludeProps
260}
261
262// assorted utils
263
264func cleanPaths(paths []string) []string {
265 res := make([]string, len(paths))
266 for i, v := range paths {
267 res[i] = filepath.Clean(v) + "/"
268 }
269 return res
270}
271
272func fieldNamesForProperties(propertyNames string) []string {
273 names := strings.Split(propertyNames, ".")
274 for i, v := range names {
275 names[i] = proptools.FieldNameForProperty(v)
276 }
277 return names
278}
279
280func hasAnyPrefix(s string, prefixes []string) bool {
281 for _, prefix := range prefixes {
282 if strings.HasPrefix(s, prefix) {
283 return true
284 }
285 }
286 return false
287}
288
289func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
290 for _, v := range props {
291 if hasProperty(properties, v) {
292 return true
293 }
294 }
295 return false
296}
297
298func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
299 for _, v := range props {
300 if !hasProperty(properties, v) {
301 return false
302 }
303 }
304 return true
305}
306
307func hasProperty(properties []interface{}, prop ruleProperty) bool {
308 for _, propertyStruct := range properties {
309 propertiesValue := reflect.ValueOf(propertyStruct).Elem()
310 for _, v := range prop.fields {
311 if !propertiesValue.IsValid() {
312 break
313 }
314 propertiesValue = propertiesValue.FieldByName(v)
315 }
316 if !propertiesValue.IsValid() {
317 continue
318 }
319
320 check := func(v string) bool {
321 return prop.value == "*" || prop.value == v
322 }
323
324 if matchValue(propertiesValue, check) {
325 return true
326 }
327 }
328 return false
329}
330
331func matchValue(value reflect.Value, check func(string) bool) bool {
332 if !value.IsValid() {
333 return false
334 }
335
336 if value.Kind() == reflect.Ptr {
337 if value.IsNil() {
338 return check("")
339 }
340 value = value.Elem()
341 }
342
343 switch value.Kind() {
344 case reflect.String:
345 return check(value.String())
346 case reflect.Bool:
347 return check(strconv.FormatBool(value.Bool()))
348 case reflect.Int:
349 return check(strconv.FormatInt(value.Int(), 10))
350 case reflect.Slice:
351 slice, ok := value.Interface().([]string)
352 if !ok {
353 panic("Can only handle slice of string")
354 }
355 for _, v := range slice {
356 if check(v) {
357 return true
358 }
359 }
360 return false
361 }
362
363 panic("Can't handle type: " + value.Kind().String())
364}