blob: 18744e811fb0f815d00a2c5425451b201b7374be [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",
Neil Fullerdf5f3562018-10-21 17:19:10 +0100103 "okhttp",
104 "bouncycastle",
105 "conscrypt",
106 "apache-xml",
107 }
108
109 // Core library constraints. Prevent targets adding dependencies on core
110 // library internals, which could lead to compatibility issues with the ART
111 // mainline module. They should use core.platform.api.stubs instead.
112 rules := []*rule{
113 neverallow().
114 notIn(append(coreLibraryProjects, "development")...).
115 with("no_standard_libs", "true"),
116 }
117
118 for _, m := range coreModules {
119 r := neverallow().
120 notIn(coreLibraryProjects...).
121 with("libs", m).
122 because("Only core libraries projects can depend on " + m)
123 rules = append(rules, r)
124 }
125 return rules
Steven Moreland65b3fd92017-12-06 14:18:35 -0800126}
127
128func neverallowMutator(ctx BottomUpMutatorContext) {
129 m, ok := ctx.Module().(Module)
130 if !ok {
131 return
132 }
133
134 dir := ctx.ModuleDir() + "/"
135 properties := m.GetProperties()
136
137 for _, n := range neverallows {
138 if !n.appliesToPath(dir) {
139 continue
140 }
141
142 if !n.appliesToProperties(properties) {
143 continue
144 }
145
146 ctx.ModuleErrorf("violates " + n.String())
147 }
148}
149
150type ruleProperty struct {
151 fields []string // e.x.: Vndk.Enabled
152 value string // e.x.: true
153}
154
155type rule struct {
156 // User string for why this is a thing.
157 reason string
158
159 paths []string
160 unlessPaths []string
161
162 props []ruleProperty
163 unlessProps []ruleProperty
164}
165
166func neverallow() *rule {
167 return &rule{}
168}
169func (r *rule) in(path ...string) *rule {
170 r.paths = append(r.paths, cleanPaths(path)...)
171 return r
172}
173func (r *rule) notIn(path ...string) *rule {
174 r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
175 return r
176}
177func (r *rule) with(properties, value string) *rule {
178 r.props = append(r.props, ruleProperty{
179 fields: fieldNamesForProperties(properties),
180 value: value,
181 })
182 return r
183}
184func (r *rule) without(properties, value string) *rule {
185 r.unlessProps = append(r.unlessProps, ruleProperty{
186 fields: fieldNamesForProperties(properties),
187 value: value,
188 })
189 return r
190}
191func (r *rule) because(reason string) *rule {
192 r.reason = reason
193 return r
194}
195
196func (r *rule) String() string {
197 s := "neverallow"
198 for _, v := range r.paths {
199 s += " dir:" + v + "*"
200 }
201 for _, v := range r.unlessPaths {
202 s += " -dir:" + v + "*"
203 }
204 for _, v := range r.props {
205 s += " " + strings.Join(v.fields, ".") + "=" + v.value
206 }
207 for _, v := range r.unlessProps {
208 s += " -" + strings.Join(v.fields, ".") + "=" + v.value
209 }
210 if len(r.reason) != 0 {
211 s += " which is restricted because " + r.reason
212 }
213 return s
214}
215
216func (r *rule) appliesToPath(dir string) bool {
217 includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
218 excludePath := hasAnyPrefix(dir, r.unlessPaths)
219 return includePath && !excludePath
220}
221
222func (r *rule) appliesToProperties(properties []interface{}) bool {
223 includeProps := hasAllProperties(properties, r.props)
224 excludeProps := hasAnyProperty(properties, r.unlessProps)
225 return includeProps && !excludeProps
226}
227
228// assorted utils
229
230func cleanPaths(paths []string) []string {
231 res := make([]string, len(paths))
232 for i, v := range paths {
233 res[i] = filepath.Clean(v) + "/"
234 }
235 return res
236}
237
238func fieldNamesForProperties(propertyNames string) []string {
239 names := strings.Split(propertyNames, ".")
240 for i, v := range names {
241 names[i] = proptools.FieldNameForProperty(v)
242 }
243 return names
244}
245
246func hasAnyPrefix(s string, prefixes []string) bool {
247 for _, prefix := range prefixes {
248 if strings.HasPrefix(s, prefix) {
249 return true
250 }
251 }
252 return false
253}
254
255func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
256 for _, v := range props {
257 if hasProperty(properties, v) {
258 return true
259 }
260 }
261 return false
262}
263
264func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
265 for _, v := range props {
266 if !hasProperty(properties, v) {
267 return false
268 }
269 }
270 return true
271}
272
273func hasProperty(properties []interface{}, prop ruleProperty) bool {
274 for _, propertyStruct := range properties {
275 propertiesValue := reflect.ValueOf(propertyStruct).Elem()
276 for _, v := range prop.fields {
277 if !propertiesValue.IsValid() {
278 break
279 }
280 propertiesValue = propertiesValue.FieldByName(v)
281 }
282 if !propertiesValue.IsValid() {
283 continue
284 }
285
286 check := func(v string) bool {
287 return prop.value == "*" || prop.value == v
288 }
289
290 if matchValue(propertiesValue, check) {
291 return true
292 }
293 }
294 return false
295}
296
297func matchValue(value reflect.Value, check func(string) bool) bool {
298 if !value.IsValid() {
299 return false
300 }
301
302 if value.Kind() == reflect.Ptr {
303 if value.IsNil() {
304 return check("")
305 }
306 value = value.Elem()
307 }
308
309 switch value.Kind() {
310 case reflect.String:
311 return check(value.String())
312 case reflect.Bool:
313 return check(strconv.FormatBool(value.Bool()))
314 case reflect.Int:
315 return check(strconv.FormatInt(value.Int(), 10))
316 case reflect.Slice:
317 slice, ok := value.Interface().([]string)
318 if !ok {
319 panic("Can only handle slice of string")
320 }
321 for _, v := range slice {
322 if check(v) {
323 return true
324 }
325 }
326 return false
327 }
328
329 panic("Can't handle type: " + value.Kind().String())
330}