blob: f35d1fed9cd89cc9ad1f299a3c8ecfaed8b661f0 [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 Duffina3d09862019-06-11 13:40:47 +0100103 // Core library constraints. The sdk_version: "none" can only be used in core library projects.
104 // Access to core library targets is restricted using visibility rules.
Neil Fullerdf5f3562018-10-21 17:19:10 +0100105 rules := []*rule{
106 neverallow().
Paul Duffinb6c6bdd2019-06-07 11:43:55 +0100107 notIn(coreLibraryProjects...).
Paul Duffin52d398a2019-06-11 12:31:14 +0100108 with("sdk_version", "none"),
Neil Fullerdf5f3562018-10-21 17:19:10 +0100109 }
110
Neil Fullerdf5f3562018-10-21 17:19:10 +0100111 return rules
Steven Moreland65b3fd92017-12-06 14:18:35 -0800112}
113
Colin Crossc35c5f92019-03-05 15:06:16 -0800114func createJavaDeviceForHostRules() []*rule {
115 javaDeviceForHostProjectsWhitelist := []string{
Colin Cross97add502019-04-11 14:07:38 -0700116 "external/guava",
Colin Crossc35c5f92019-03-05 15:06:16 -0800117 "external/robolectric-shadows",
118 "framework/layoutlib",
119 }
120
121 return []*rule{
122 neverallow().
123 notIn(javaDeviceForHostProjectsWhitelist...).
124 moduleType("java_device_for_host", "java_host_for_device").
125 because("java_device_for_host can only be used in whitelisted projects"),
126 }
127}
128
Steven Moreland65b3fd92017-12-06 14:18:35 -0800129func neverallowMutator(ctx BottomUpMutatorContext) {
130 m, ok := ctx.Module().(Module)
131 if !ok {
132 return
133 }
134
135 dir := ctx.ModuleDir() + "/"
136 properties := m.GetProperties()
137
138 for _, n := range neverallows {
139 if !n.appliesToPath(dir) {
140 continue
141 }
142
Colin Crossc35c5f92019-03-05 15:06:16 -0800143 if !n.appliesToModuleType(ctx.ModuleType()) {
144 continue
145 }
146
Steven Moreland65b3fd92017-12-06 14:18:35 -0800147 if !n.appliesToProperties(properties) {
148 continue
149 }
150
151 ctx.ModuleErrorf("violates " + n.String())
152 }
153}
154
155type ruleProperty struct {
156 fields []string // e.x.: Vndk.Enabled
157 value string // e.x.: true
158}
159
160type rule struct {
161 // User string for why this is a thing.
162 reason string
163
164 paths []string
165 unlessPaths []string
166
Colin Crossc35c5f92019-03-05 15:06:16 -0800167 moduleTypes []string
168 unlessModuleTypes []string
169
Steven Moreland65b3fd92017-12-06 14:18:35 -0800170 props []ruleProperty
171 unlessProps []ruleProperty
172}
173
174func neverallow() *rule {
175 return &rule{}
176}
Colin Crossc35c5f92019-03-05 15:06:16 -0800177
Steven Moreland65b3fd92017-12-06 14:18:35 -0800178func (r *rule) in(path ...string) *rule {
179 r.paths = append(r.paths, cleanPaths(path)...)
180 return r
181}
Colin Crossc35c5f92019-03-05 15:06:16 -0800182
Steven Moreland65b3fd92017-12-06 14:18:35 -0800183func (r *rule) notIn(path ...string) *rule {
184 r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
185 return r
186}
Colin Crossc35c5f92019-03-05 15:06:16 -0800187
188func (r *rule) moduleType(types ...string) *rule {
189 r.moduleTypes = append(r.moduleTypes, types...)
190 return r
191}
192
193func (r *rule) notModuleType(types ...string) *rule {
194 r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
195 return r
196}
197
Steven Moreland65b3fd92017-12-06 14:18:35 -0800198func (r *rule) with(properties, value string) *rule {
199 r.props = append(r.props, ruleProperty{
200 fields: fieldNamesForProperties(properties),
201 value: value,
202 })
203 return r
204}
Colin Crossc35c5f92019-03-05 15:06:16 -0800205
Steven Moreland65b3fd92017-12-06 14:18:35 -0800206func (r *rule) without(properties, value string) *rule {
207 r.unlessProps = append(r.unlessProps, ruleProperty{
208 fields: fieldNamesForProperties(properties),
209 value: value,
210 })
211 return r
212}
Colin Crossc35c5f92019-03-05 15:06:16 -0800213
Steven Moreland65b3fd92017-12-06 14:18:35 -0800214func (r *rule) because(reason string) *rule {
215 r.reason = reason
216 return r
217}
218
219func (r *rule) String() string {
220 s := "neverallow"
221 for _, v := range r.paths {
222 s += " dir:" + v + "*"
223 }
224 for _, v := range r.unlessPaths {
225 s += " -dir:" + v + "*"
226 }
Colin Crossc35c5f92019-03-05 15:06:16 -0800227 for _, v := range r.moduleTypes {
228 s += " type:" + v
229 }
230 for _, v := range r.unlessModuleTypes {
231 s += " -type:" + v
232 }
Steven Moreland65b3fd92017-12-06 14:18:35 -0800233 for _, v := range r.props {
234 s += " " + strings.Join(v.fields, ".") + "=" + v.value
235 }
236 for _, v := range r.unlessProps {
237 s += " -" + strings.Join(v.fields, ".") + "=" + v.value
238 }
239 if len(r.reason) != 0 {
240 s += " which is restricted because " + r.reason
241 }
242 return s
243}
244
245func (r *rule) appliesToPath(dir string) bool {
246 includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
247 excludePath := hasAnyPrefix(dir, r.unlessPaths)
248 return includePath && !excludePath
249}
250
Colin Crossc35c5f92019-03-05 15:06:16 -0800251func (r *rule) appliesToModuleType(moduleType string) bool {
252 return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
253}
254
Steven Moreland65b3fd92017-12-06 14:18:35 -0800255func (r *rule) appliesToProperties(properties []interface{}) bool {
256 includeProps := hasAllProperties(properties, r.props)
257 excludeProps := hasAnyProperty(properties, r.unlessProps)
258 return includeProps && !excludeProps
259}
260
261// assorted utils
262
263func cleanPaths(paths []string) []string {
264 res := make([]string, len(paths))
265 for i, v := range paths {
266 res[i] = filepath.Clean(v) + "/"
267 }
268 return res
269}
270
271func fieldNamesForProperties(propertyNames string) []string {
272 names := strings.Split(propertyNames, ".")
273 for i, v := range names {
274 names[i] = proptools.FieldNameForProperty(v)
275 }
276 return names
277}
278
279func hasAnyPrefix(s string, prefixes []string) bool {
280 for _, prefix := range prefixes {
281 if strings.HasPrefix(s, prefix) {
282 return true
283 }
284 }
285 return false
286}
287
288func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
289 for _, v := range props {
290 if hasProperty(properties, v) {
291 return true
292 }
293 }
294 return false
295}
296
297func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
298 for _, v := range props {
299 if !hasProperty(properties, v) {
300 return false
301 }
302 }
303 return true
304}
305
306func hasProperty(properties []interface{}, prop ruleProperty) bool {
307 for _, propertyStruct := range properties {
308 propertiesValue := reflect.ValueOf(propertyStruct).Elem()
309 for _, v := range prop.fields {
310 if !propertiesValue.IsValid() {
311 break
312 }
313 propertiesValue = propertiesValue.FieldByName(v)
314 }
315 if !propertiesValue.IsValid() {
316 continue
317 }
318
319 check := func(v string) bool {
320 return prop.value == "*" || prop.value == v
321 }
322
323 if matchValue(propertiesValue, check) {
324 return true
325 }
326 }
327 return false
328}
329
330func matchValue(value reflect.Value, check func(string) bool) bool {
331 if !value.IsValid() {
332 return false
333 }
334
335 if value.Kind() == reflect.Ptr {
336 if value.IsNil() {
337 return check("")
338 }
339 value = value.Elem()
340 }
341
342 switch value.Kind() {
343 case reflect.String:
344 return check(value.String())
345 case reflect.Bool:
346 return check(strconv.FormatBool(value.Bool()))
347 case reflect.Int:
348 return check(strconv.FormatInt(value.Int(), 10))
349 case reflect.Slice:
350 slice, ok := value.Interface().([]string)
351 if !ok {
352 panic("Can only handle slice of string")
353 }
354 for _, v := range slice {
355 if check(v) {
356 return true
357 }
358 }
359 return false
360 }
361
362 panic("Can't handle type: " + value.Kind().String())
363}