blob: f6caebcedc9cda4365ebf4633862229177ed9b29 [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()...)
54 return rules
55}
Steven Moreland65b3fd92017-12-06 14:18:35 -080056
Neil Fullerdf5f3562018-10-21 17:19:10 +010057func createTrebleRules() []*rule {
58 return []*rule{
59 neverallow().
60 in("vendor", "device").
61 with("vndk.enabled", "true").
62 without("vendor", "true").
63 because("the VNDK can never contain a library that is device dependent."),
64 neverallow().
65 with("vndk.enabled", "true").
66 without("vendor", "true").
67 without("owner", "").
68 because("a VNDK module can never have an owner."),
Steven Moreland65b3fd92017-12-06 14:18:35 -080069
Neil Fullerdf5f3562018-10-21 17:19:10 +010070 // TODO(b/67974785): always enforce the manifest
71 neverallow().
72 without("name", "libhidltransport").
73 with("product_variables.enforce_vintf_manifest.cflags", "*").
74 because("manifest enforcement should be independent of ."),
75
76 // TODO(b/67975799): vendor code should always use /vendor/bin/sh
77 neverallow().
78 without("name", "libc_bionic_ndk").
79 with("product_variables.treble_linker_namespaces.cflags", "*").
80 because("nothing should care if linker namespaces are enabled or not"),
81
82 // Example:
83 // *neverallow().with("Srcs", "main.cpp"))
84 }
85}
86
87func createLibcoreRules() []*rule {
88 var coreLibraryProjects = []string{
89 "libcore",
90 "external/apache-harmony",
91 "external/apache-xml",
92 "external/bouncycastle",
93 "external/conscrypt",
94 "external/icu",
95 "external/okhttp",
96 "external/wycheproof",
97 }
98
99 var coreModules = []string{
100 "core-all",
101 "core-oj",
102 "core-libart",
103 "core-simple",
104 "okhttp",
105 "bouncycastle",
106 "conscrypt",
107 "apache-xml",
108 }
109
110 // Core library constraints. Prevent targets adding dependencies on core
111 // library internals, which could lead to compatibility issues with the ART
112 // mainline module. They should use core.platform.api.stubs instead.
113 rules := []*rule{
114 neverallow().
115 notIn(append(coreLibraryProjects, "development")...).
116 with("no_standard_libs", "true"),
117 }
118
119 for _, m := range coreModules {
120 r := neverallow().
121 notIn(coreLibraryProjects...).
122 with("libs", m).
123 because("Only core libraries projects can depend on " + m)
124 rules = append(rules, r)
125 }
126 return rules
Steven Moreland65b3fd92017-12-06 14:18:35 -0800127}
128
129func 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
143 if !n.appliesToProperties(properties) {
144 continue
145 }
146
147 ctx.ModuleErrorf("violates " + n.String())
148 }
149}
150
151type ruleProperty struct {
152 fields []string // e.x.: Vndk.Enabled
153 value string // e.x.: true
154}
155
156type rule struct {
157 // User string for why this is a thing.
158 reason string
159
160 paths []string
161 unlessPaths []string
162
163 props []ruleProperty
164 unlessProps []ruleProperty
165}
166
167func neverallow() *rule {
168 return &rule{}
169}
170func (r *rule) in(path ...string) *rule {
171 r.paths = append(r.paths, cleanPaths(path)...)
172 return r
173}
174func (r *rule) notIn(path ...string) *rule {
175 r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
176 return r
177}
178func (r *rule) with(properties, value string) *rule {
179 r.props = append(r.props, ruleProperty{
180 fields: fieldNamesForProperties(properties),
181 value: value,
182 })
183 return r
184}
185func (r *rule) without(properties, value string) *rule {
186 r.unlessProps = append(r.unlessProps, ruleProperty{
187 fields: fieldNamesForProperties(properties),
188 value: value,
189 })
190 return r
191}
192func (r *rule) because(reason string) *rule {
193 r.reason = reason
194 return r
195}
196
197func (r *rule) String() string {
198 s := "neverallow"
199 for _, v := range r.paths {
200 s += " dir:" + v + "*"
201 }
202 for _, v := range r.unlessPaths {
203 s += " -dir:" + v + "*"
204 }
205 for _, v := range r.props {
206 s += " " + strings.Join(v.fields, ".") + "=" + v.value
207 }
208 for _, v := range r.unlessProps {
209 s += " -" + strings.Join(v.fields, ".") + "=" + v.value
210 }
211 if len(r.reason) != 0 {
212 s += " which is restricted because " + r.reason
213 }
214 return s
215}
216
217func (r *rule) appliesToPath(dir string) bool {
218 includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
219 excludePath := hasAnyPrefix(dir, r.unlessPaths)
220 return includePath && !excludePath
221}
222
223func (r *rule) appliesToProperties(properties []interface{}) bool {
224 includeProps := hasAllProperties(properties, r.props)
225 excludeProps := hasAnyProperty(properties, r.unlessProps)
226 return includeProps && !excludeProps
227}
228
229// assorted utils
230
231func cleanPaths(paths []string) []string {
232 res := make([]string, len(paths))
233 for i, v := range paths {
234 res[i] = filepath.Clean(v) + "/"
235 }
236 return res
237}
238
239func fieldNamesForProperties(propertyNames string) []string {
240 names := strings.Split(propertyNames, ".")
241 for i, v := range names {
242 names[i] = proptools.FieldNameForProperty(v)
243 }
244 return names
245}
246
247func hasAnyPrefix(s string, prefixes []string) bool {
248 for _, prefix := range prefixes {
249 if strings.HasPrefix(s, prefix) {
250 return true
251 }
252 }
253 return false
254}
255
256func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
257 for _, v := range props {
258 if hasProperty(properties, v) {
259 return true
260 }
261 }
262 return false
263}
264
265func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
266 for _, v := range props {
267 if !hasProperty(properties, v) {
268 return false
269 }
270 }
271 return true
272}
273
274func hasProperty(properties []interface{}, prop ruleProperty) bool {
275 for _, propertyStruct := range properties {
276 propertiesValue := reflect.ValueOf(propertyStruct).Elem()
277 for _, v := range prop.fields {
278 if !propertiesValue.IsValid() {
279 break
280 }
281 propertiesValue = propertiesValue.FieldByName(v)
282 }
283 if !propertiesValue.IsValid() {
284 continue
285 }
286
287 check := func(v string) bool {
288 return prop.value == "*" || prop.value == v
289 }
290
291 if matchValue(propertiesValue, check) {
292 return true
293 }
294 }
295 return false
296}
297
298func matchValue(value reflect.Value, check func(string) bool) bool {
299 if !value.IsValid() {
300 return false
301 }
302
303 if value.Kind() == reflect.Ptr {
304 if value.IsNil() {
305 return check("")
306 }
307 value = value.Elem()
308 }
309
310 switch value.Kind() {
311 case reflect.String:
312 return check(value.String())
313 case reflect.Bool:
314 return check(strconv.FormatBool(value.Bool()))
315 case reflect.Int:
316 return check(strconv.FormatInt(value.Int(), 10))
317 case reflect.Slice:
318 slice, ok := value.Interface().([]string)
319 if !ok {
320 panic("Can only handle slice of string")
321 }
322 for _, v := range slice {
323 if check(v) {
324 return true
325 }
326 }
327 return false
328 }
329
330 panic("Can't handle type: " + value.Kind().String())
331}