blob: ecff62d0507bf9be7ec259017a1e7c193cf7f4ab [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()...)
Paul Duffinb815ada2019-06-11 13:54:26 +010055 rules = append(rules, createJavaLibraryHostRules()...)
Neil Fullerdf5f3562018-10-21 17:19:10 +010056 return rules
57}
Steven Moreland65b3fd92017-12-06 14:18:35 -080058
Neil Fullerdf5f3562018-10-21 17:19:10 +010059func createTrebleRules() []*rule {
60 return []*rule{
61 neverallow().
62 in("vendor", "device").
63 with("vndk.enabled", "true").
64 without("vendor", "true").
65 because("the VNDK can never contain a library that is device dependent."),
66 neverallow().
67 with("vndk.enabled", "true").
68 without("vendor", "true").
69 without("owner", "").
70 because("a VNDK module can never have an owner."),
Steven Moreland65b3fd92017-12-06 14:18:35 -080071
Neil Fullerdf5f3562018-10-21 17:19:10 +010072 // TODO(b/67974785): always enforce the manifest
73 neverallow().
74 without("name", "libhidltransport").
75 with("product_variables.enforce_vintf_manifest.cflags", "*").
76 because("manifest enforcement should be independent of ."),
77
78 // TODO(b/67975799): vendor code should always use /vendor/bin/sh
79 neverallow().
80 without("name", "libc_bionic_ndk").
81 with("product_variables.treble_linker_namespaces.cflags", "*").
82 because("nothing should care if linker namespaces are enabled or not"),
83
84 // Example:
85 // *neverallow().with("Srcs", "main.cpp"))
86 }
87}
88
89func createLibcoreRules() []*rule {
90 var coreLibraryProjects = []string{
91 "libcore",
92 "external/apache-harmony",
93 "external/apache-xml",
94 "external/bouncycastle",
95 "external/conscrypt",
96 "external/icu",
97 "external/okhttp",
98 "external/wycheproof",
Paul Duffinb6c6bdd2019-06-07 11:43:55 +010099
100 // Not really a core library but still needs access to same capabilities.
101 "development",
Neil Fullerdf5f3562018-10-21 17:19:10 +0100102 }
103
Paul Duffinff5a1772019-04-30 13:13:39 +0100104 // Core library constraints. The no_standard_libs can only be used in core
105 // library projects. Access to core library targets is restricted using
106 // visibility rules.
Neil Fullerdf5f3562018-10-21 17:19:10 +0100107 rules := []*rule{
108 neverallow().
Paul Duffinb6c6bdd2019-06-07 11:43:55 +0100109 notIn(coreLibraryProjects...).
Neil Fullerdf5f3562018-10-21 17:19:10 +0100110 with("no_standard_libs", "true"),
111 }
112
Neil Fullerdf5f3562018-10-21 17:19:10 +0100113 return rules
Steven Moreland65b3fd92017-12-06 14:18:35 -0800114}
115
Colin Crossc35c5f92019-03-05 15:06:16 -0800116func createJavaDeviceForHostRules() []*rule {
117 javaDeviceForHostProjectsWhitelist := []string{
Colin Cross97add502019-04-11 14:07:38 -0700118 "external/guava",
Colin Crossc35c5f92019-03-05 15:06:16 -0800119 "external/robolectric-shadows",
120 "framework/layoutlib",
121 }
122
123 return []*rule{
124 neverallow().
125 notIn(javaDeviceForHostProjectsWhitelist...).
126 moduleType("java_device_for_host", "java_host_for_device").
127 because("java_device_for_host can only be used in whitelisted projects"),
128 }
129}
130
Paul Duffinb815ada2019-06-11 13:54:26 +0100131func createJavaLibraryHostRules() []*rule {
132 return []*rule{
133 neverallow().
134 moduleType("java_library_host").
135 with("no_standard_libs", "true").
136 because("no_standard_libs makes no sense with java_library_host"),
137 }
138}
139
Steven Moreland65b3fd92017-12-06 14:18:35 -0800140func neverallowMutator(ctx BottomUpMutatorContext) {
141 m, ok := ctx.Module().(Module)
142 if !ok {
143 return
144 }
145
146 dir := ctx.ModuleDir() + "/"
147 properties := m.GetProperties()
148
149 for _, n := range neverallows {
150 if !n.appliesToPath(dir) {
151 continue
152 }
153
Colin Crossc35c5f92019-03-05 15:06:16 -0800154 if !n.appliesToModuleType(ctx.ModuleType()) {
155 continue
156 }
157
Steven Moreland65b3fd92017-12-06 14:18:35 -0800158 if !n.appliesToProperties(properties) {
159 continue
160 }
161
162 ctx.ModuleErrorf("violates " + n.String())
163 }
164}
165
166type ruleProperty struct {
167 fields []string // e.x.: Vndk.Enabled
168 value string // e.x.: true
169}
170
171type rule struct {
172 // User string for why this is a thing.
173 reason string
174
175 paths []string
176 unlessPaths []string
177
Colin Crossc35c5f92019-03-05 15:06:16 -0800178 moduleTypes []string
179 unlessModuleTypes []string
180
Steven Moreland65b3fd92017-12-06 14:18:35 -0800181 props []ruleProperty
182 unlessProps []ruleProperty
183}
184
185func neverallow() *rule {
186 return &rule{}
187}
Colin Crossc35c5f92019-03-05 15:06:16 -0800188
Steven Moreland65b3fd92017-12-06 14:18:35 -0800189func (r *rule) in(path ...string) *rule {
190 r.paths = append(r.paths, cleanPaths(path)...)
191 return r
192}
Colin Crossc35c5f92019-03-05 15:06:16 -0800193
Steven Moreland65b3fd92017-12-06 14:18:35 -0800194func (r *rule) notIn(path ...string) *rule {
195 r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
196 return r
197}
Colin Crossc35c5f92019-03-05 15:06:16 -0800198
199func (r *rule) moduleType(types ...string) *rule {
200 r.moduleTypes = append(r.moduleTypes, types...)
201 return r
202}
203
204func (r *rule) notModuleType(types ...string) *rule {
205 r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
206 return r
207}
208
Steven Moreland65b3fd92017-12-06 14:18:35 -0800209func (r *rule) with(properties, value string) *rule {
210 r.props = append(r.props, ruleProperty{
211 fields: fieldNamesForProperties(properties),
212 value: value,
213 })
214 return r
215}
Colin Crossc35c5f92019-03-05 15:06:16 -0800216
Steven Moreland65b3fd92017-12-06 14:18:35 -0800217func (r *rule) without(properties, value string) *rule {
218 r.unlessProps = append(r.unlessProps, ruleProperty{
219 fields: fieldNamesForProperties(properties),
220 value: value,
221 })
222 return r
223}
Colin Crossc35c5f92019-03-05 15:06:16 -0800224
Steven Moreland65b3fd92017-12-06 14:18:35 -0800225func (r *rule) because(reason string) *rule {
226 r.reason = reason
227 return r
228}
229
230func (r *rule) String() string {
231 s := "neverallow"
232 for _, v := range r.paths {
233 s += " dir:" + v + "*"
234 }
235 for _, v := range r.unlessPaths {
236 s += " -dir:" + v + "*"
237 }
Colin Crossc35c5f92019-03-05 15:06:16 -0800238 for _, v := range r.moduleTypes {
239 s += " type:" + v
240 }
241 for _, v := range r.unlessModuleTypes {
242 s += " -type:" + v
243 }
Steven Moreland65b3fd92017-12-06 14:18:35 -0800244 for _, v := range r.props {
245 s += " " + strings.Join(v.fields, ".") + "=" + v.value
246 }
247 for _, v := range r.unlessProps {
248 s += " -" + strings.Join(v.fields, ".") + "=" + v.value
249 }
250 if len(r.reason) != 0 {
251 s += " which is restricted because " + r.reason
252 }
253 return s
254}
255
256func (r *rule) appliesToPath(dir string) bool {
257 includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
258 excludePath := hasAnyPrefix(dir, r.unlessPaths)
259 return includePath && !excludePath
260}
261
Colin Crossc35c5f92019-03-05 15:06:16 -0800262func (r *rule) appliesToModuleType(moduleType string) bool {
263 return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
264}
265
Steven Moreland65b3fd92017-12-06 14:18:35 -0800266func (r *rule) appliesToProperties(properties []interface{}) bool {
267 includeProps := hasAllProperties(properties, r.props)
268 excludeProps := hasAnyProperty(properties, r.unlessProps)
269 return includeProps && !excludeProps
270}
271
272// assorted utils
273
274func cleanPaths(paths []string) []string {
275 res := make([]string, len(paths))
276 for i, v := range paths {
277 res[i] = filepath.Clean(v) + "/"
278 }
279 return res
280}
281
282func fieldNamesForProperties(propertyNames string) []string {
283 names := strings.Split(propertyNames, ".")
284 for i, v := range names {
285 names[i] = proptools.FieldNameForProperty(v)
286 }
287 return names
288}
289
290func hasAnyPrefix(s string, prefixes []string) bool {
291 for _, prefix := range prefixes {
292 if strings.HasPrefix(s, prefix) {
293 return true
294 }
295 }
296 return false
297}
298
299func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
300 for _, v := range props {
301 if hasProperty(properties, v) {
302 return true
303 }
304 }
305 return false
306}
307
308func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
309 for _, v := range props {
310 if !hasProperty(properties, v) {
311 return false
312 }
313 }
314 return true
315}
316
317func hasProperty(properties []interface{}, prop ruleProperty) bool {
318 for _, propertyStruct := range properties {
319 propertiesValue := reflect.ValueOf(propertyStruct).Elem()
320 for _, v := range prop.fields {
321 if !propertiesValue.IsValid() {
322 break
323 }
324 propertiesValue = propertiesValue.FieldByName(v)
325 }
326 if !propertiesValue.IsValid() {
327 continue
328 }
329
330 check := func(v string) bool {
331 return prop.value == "*" || prop.value == v
332 }
333
334 if matchValue(propertiesValue, check) {
335 return true
336 }
337 }
338 return false
339}
340
341func matchValue(value reflect.Value, check func(string) bool) bool {
342 if !value.IsValid() {
343 return false
344 }
345
346 if value.Kind() == reflect.Ptr {
347 if value.IsNil() {
348 return check("")
349 }
350 value = value.Elem()
351 }
352
353 switch value.Kind() {
354 case reflect.String:
355 return check(value.String())
356 case reflect.Bool:
357 return check(strconv.FormatBool(value.Bool()))
358 case reflect.Int:
359 return check(strconv.FormatInt(value.Int(), 10))
360 case reflect.Slice:
361 slice, ok := value.Interface().([]string)
362 if !ok {
363 panic("Can only handle slice of string")
364 }
365 for _, v := range slice {
366 if check(v) {
367 return true
368 }
369 }
370 return false
371 }
372
373 panic("Can't handle type: " + value.Kind().String())
374}