blob: 208207b03d802d03a4933fcc5f0df8f929640649 [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()...)
Dongwon Kang50a299f2019-02-04 09:00:51 -080054 rules = append(rules, createMediaRules()...)
Colin Crossfd4f7432019-03-05 15:06:16 -080055 rules = append(rules, createJavaDeviceForHostRules()...)
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().
Steven Morelanda1165d62019-06-05 18:27:35 -070074 without("name", "libhidltransport-impl-internal").
Neil Fullerdf5f3562018-10-21 17:19:10 +010075 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 Duffina3d09862019-06-11 13:40:47 +0100104 // Core library constraints. The sdk_version: "none" can only be used in core library projects.
105 // Access to core library targets is restricted using visibility rules.
Neil Fullerdf5f3562018-10-21 17:19:10 +0100106 rules := []*rule{
107 neverallow().
Paul Duffinb6c6bdd2019-06-07 11:43:55 +0100108 notIn(coreLibraryProjects...).
Paul Duffin52d398a2019-06-11 12:31:14 +0100109 with("sdk_version", "none"),
Neil Fullerdf5f3562018-10-21 17:19:10 +0100110 }
111
Neil Fullerdf5f3562018-10-21 17:19:10 +0100112 return rules
Steven Moreland65b3fd92017-12-06 14:18:35 -0800113}
114
Dongwon Kang50a299f2019-02-04 09:00:51 -0800115func createMediaRules() []*rule {
116 return []*rule{
117 neverallow().
118 with("libs", "updatable-media").
119 because("updatable-media includes private APIs. Use updatable_media_stubs instead."),
120 }
121}
122
Colin Crossfd4f7432019-03-05 15:06:16 -0800123func createJavaDeviceForHostRules() []*rule {
124 javaDeviceForHostProjectsWhitelist := []string{
Colin Crossb5191a52019-04-11 14:07:38 -0700125 "external/guava",
Colin Crossfd4f7432019-03-05 15:06:16 -0800126 "external/robolectric-shadows",
127 "framework/layoutlib",
128 }
129
130 return []*rule{
131 neverallow().
132 notIn(javaDeviceForHostProjectsWhitelist...).
133 moduleType("java_device_for_host", "java_host_for_device").
134 because("java_device_for_host can only be used in whitelisted projects"),
135 }
136}
137
Steven Moreland65b3fd92017-12-06 14:18:35 -0800138func neverallowMutator(ctx BottomUpMutatorContext) {
139 m, ok := ctx.Module().(Module)
140 if !ok {
141 return
142 }
143
144 dir := ctx.ModuleDir() + "/"
145 properties := m.GetProperties()
146
147 for _, n := range neverallows {
148 if !n.appliesToPath(dir) {
149 continue
150 }
151
Colin Crossfd4f7432019-03-05 15:06:16 -0800152 if !n.appliesToModuleType(ctx.ModuleType()) {
153 continue
154 }
155
Steven Moreland65b3fd92017-12-06 14:18:35 -0800156 if !n.appliesToProperties(properties) {
157 continue
158 }
159
160 ctx.ModuleErrorf("violates " + n.String())
161 }
162}
163
164type ruleProperty struct {
165 fields []string // e.x.: Vndk.Enabled
166 value string // e.x.: true
167}
168
169type rule struct {
170 // User string for why this is a thing.
171 reason string
172
173 paths []string
174 unlessPaths []string
175
Colin Crossfd4f7432019-03-05 15:06:16 -0800176 moduleTypes []string
177 unlessModuleTypes []string
178
Steven Moreland65b3fd92017-12-06 14:18:35 -0800179 props []ruleProperty
180 unlessProps []ruleProperty
181}
182
183func neverallow() *rule {
184 return &rule{}
185}
Colin Crossfd4f7432019-03-05 15:06:16 -0800186
Steven Moreland65b3fd92017-12-06 14:18:35 -0800187func (r *rule) in(path ...string) *rule {
188 r.paths = append(r.paths, cleanPaths(path)...)
189 return r
190}
Colin Crossfd4f7432019-03-05 15:06:16 -0800191
Steven Moreland65b3fd92017-12-06 14:18:35 -0800192func (r *rule) notIn(path ...string) *rule {
193 r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
194 return r
195}
Colin Crossfd4f7432019-03-05 15:06:16 -0800196
197func (r *rule) moduleType(types ...string) *rule {
198 r.moduleTypes = append(r.moduleTypes, types...)
199 return r
200}
201
202func (r *rule) notModuleType(types ...string) *rule {
203 r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
204 return r
205}
206
Steven Moreland65b3fd92017-12-06 14:18:35 -0800207func (r *rule) with(properties, value string) *rule {
208 r.props = append(r.props, ruleProperty{
209 fields: fieldNamesForProperties(properties),
210 value: value,
211 })
212 return r
213}
Colin Crossfd4f7432019-03-05 15:06:16 -0800214
Steven Moreland65b3fd92017-12-06 14:18:35 -0800215func (r *rule) without(properties, value string) *rule {
216 r.unlessProps = append(r.unlessProps, ruleProperty{
217 fields: fieldNamesForProperties(properties),
218 value: value,
219 })
220 return r
221}
Colin Crossfd4f7432019-03-05 15:06:16 -0800222
Steven Moreland65b3fd92017-12-06 14:18:35 -0800223func (r *rule) because(reason string) *rule {
224 r.reason = reason
225 return r
226}
227
228func (r *rule) String() string {
229 s := "neverallow"
230 for _, v := range r.paths {
231 s += " dir:" + v + "*"
232 }
233 for _, v := range r.unlessPaths {
234 s += " -dir:" + v + "*"
235 }
Colin Crossfd4f7432019-03-05 15:06:16 -0800236 for _, v := range r.moduleTypes {
237 s += " type:" + v
238 }
239 for _, v := range r.unlessModuleTypes {
240 s += " -type:" + v
241 }
Steven Moreland65b3fd92017-12-06 14:18:35 -0800242 for _, v := range r.props {
243 s += " " + strings.Join(v.fields, ".") + "=" + v.value
244 }
245 for _, v := range r.unlessProps {
246 s += " -" + strings.Join(v.fields, ".") + "=" + v.value
247 }
248 if len(r.reason) != 0 {
249 s += " which is restricted because " + r.reason
250 }
251 return s
252}
253
254func (r *rule) appliesToPath(dir string) bool {
255 includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
256 excludePath := hasAnyPrefix(dir, r.unlessPaths)
257 return includePath && !excludePath
258}
259
Colin Crossfd4f7432019-03-05 15:06:16 -0800260func (r *rule) appliesToModuleType(moduleType string) bool {
261 return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
262}
263
Steven Moreland65b3fd92017-12-06 14:18:35 -0800264func (r *rule) appliesToProperties(properties []interface{}) bool {
265 includeProps := hasAllProperties(properties, r.props)
266 excludeProps := hasAnyProperty(properties, r.unlessProps)
267 return includeProps && !excludeProps
268}
269
270// assorted utils
271
272func cleanPaths(paths []string) []string {
273 res := make([]string, len(paths))
274 for i, v := range paths {
275 res[i] = filepath.Clean(v) + "/"
276 }
277 return res
278}
279
280func fieldNamesForProperties(propertyNames string) []string {
281 names := strings.Split(propertyNames, ".")
282 for i, v := range names {
283 names[i] = proptools.FieldNameForProperty(v)
284 }
285 return names
286}
287
288func hasAnyPrefix(s string, prefixes []string) bool {
289 for _, prefix := range prefixes {
290 if strings.HasPrefix(s, prefix) {
291 return true
292 }
293 }
294 return false
295}
296
297func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
298 for _, v := range props {
299 if hasProperty(properties, v) {
300 return true
301 }
302 }
303 return false
304}
305
306func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
307 for _, v := range props {
308 if !hasProperty(properties, v) {
309 return false
310 }
311 }
312 return true
313}
314
315func hasProperty(properties []interface{}, prop ruleProperty) bool {
316 for _, propertyStruct := range properties {
317 propertiesValue := reflect.ValueOf(propertyStruct).Elem()
318 for _, v := range prop.fields {
319 if !propertiesValue.IsValid() {
320 break
321 }
322 propertiesValue = propertiesValue.FieldByName(v)
323 }
324 if !propertiesValue.IsValid() {
325 continue
326 }
327
328 check := func(v string) bool {
329 return prop.value == "*" || prop.value == v
330 }
331
332 if matchValue(propertiesValue, check) {
333 return true
334 }
335 }
336 return false
337}
338
339func matchValue(value reflect.Value, check func(string) bool) bool {
340 if !value.IsValid() {
341 return false
342 }
343
344 if value.Kind() == reflect.Ptr {
345 if value.IsNil() {
346 return check("")
347 }
348 value = value.Elem()
349 }
350
351 switch value.Kind() {
352 case reflect.String:
353 return check(value.String())
354 case reflect.Bool:
355 return check(strconv.FormatBool(value.Bool()))
356 case reflect.Int:
357 return check(strconv.FormatInt(value.Int(), 10))
358 case reflect.Slice:
359 slice, ok := value.Interface().([]string)
360 if !ok {
361 panic("Can only handle slice of string")
362 }
363 for _, v := range slice {
364 if check(v) {
365 return true
366 }
367 }
368 return false
369 }
370
371 panic("Can't handle type: " + value.Kind().String())
372}