blob: 9881b4d5a7e9509fefc56614345cb57f78f18d5e [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()...)
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",
98 }
99
100 var coreModules = []string{
101 "core-all",
102 "core-oj",
103 "core-libart",
Neil Fullerdf5f3562018-10-21 17:19:10 +0100104 "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
Dongwon Kang50a299f2019-02-04 09:00:51 -0800129func createMediaRules() []*rule {
130 return []*rule{
131 neverallow().
132 with("libs", "updatable-media").
133 because("updatable-media includes private APIs. Use updatable_media_stubs instead."),
134 }
135}
136
Steven Moreland65b3fd92017-12-06 14:18:35 -0800137func neverallowMutator(ctx BottomUpMutatorContext) {
138 m, ok := ctx.Module().(Module)
139 if !ok {
140 return
141 }
142
143 dir := ctx.ModuleDir() + "/"
144 properties := m.GetProperties()
145
146 for _, n := range neverallows {
147 if !n.appliesToPath(dir) {
148 continue
149 }
150
151 if !n.appliesToProperties(properties) {
152 continue
153 }
154
155 ctx.ModuleErrorf("violates " + n.String())
156 }
157}
158
159type ruleProperty struct {
160 fields []string // e.x.: Vndk.Enabled
161 value string // e.x.: true
162}
163
164type rule struct {
165 // User string for why this is a thing.
166 reason string
167
168 paths []string
169 unlessPaths []string
170
171 props []ruleProperty
172 unlessProps []ruleProperty
173}
174
175func neverallow() *rule {
176 return &rule{}
177}
178func (r *rule) in(path ...string) *rule {
179 r.paths = append(r.paths, cleanPaths(path)...)
180 return r
181}
182func (r *rule) notIn(path ...string) *rule {
183 r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
184 return r
185}
186func (r *rule) with(properties, value string) *rule {
187 r.props = append(r.props, ruleProperty{
188 fields: fieldNamesForProperties(properties),
189 value: value,
190 })
191 return r
192}
193func (r *rule) without(properties, value string) *rule {
194 r.unlessProps = append(r.unlessProps, ruleProperty{
195 fields: fieldNamesForProperties(properties),
196 value: value,
197 })
198 return r
199}
200func (r *rule) because(reason string) *rule {
201 r.reason = reason
202 return r
203}
204
205func (r *rule) String() string {
206 s := "neverallow"
207 for _, v := range r.paths {
208 s += " dir:" + v + "*"
209 }
210 for _, v := range r.unlessPaths {
211 s += " -dir:" + v + "*"
212 }
213 for _, v := range r.props {
214 s += " " + strings.Join(v.fields, ".") + "=" + v.value
215 }
216 for _, v := range r.unlessProps {
217 s += " -" + strings.Join(v.fields, ".") + "=" + v.value
218 }
219 if len(r.reason) != 0 {
220 s += " which is restricted because " + r.reason
221 }
222 return s
223}
224
225func (r *rule) appliesToPath(dir string) bool {
226 includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
227 excludePath := hasAnyPrefix(dir, r.unlessPaths)
228 return includePath && !excludePath
229}
230
231func (r *rule) appliesToProperties(properties []interface{}) bool {
232 includeProps := hasAllProperties(properties, r.props)
233 excludeProps := hasAnyProperty(properties, r.unlessProps)
234 return includeProps && !excludeProps
235}
236
237// assorted utils
238
239func cleanPaths(paths []string) []string {
240 res := make([]string, len(paths))
241 for i, v := range paths {
242 res[i] = filepath.Clean(v) + "/"
243 }
244 return res
245}
246
247func fieldNamesForProperties(propertyNames string) []string {
248 names := strings.Split(propertyNames, ".")
249 for i, v := range names {
250 names[i] = proptools.FieldNameForProperty(v)
251 }
252 return names
253}
254
255func hasAnyPrefix(s string, prefixes []string) bool {
256 for _, prefix := range prefixes {
257 if strings.HasPrefix(s, prefix) {
258 return true
259 }
260 }
261 return false
262}
263
264func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
265 for _, v := range props {
266 if hasProperty(properties, v) {
267 return true
268 }
269 }
270 return false
271}
272
273func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
274 for _, v := range props {
275 if !hasProperty(properties, v) {
276 return false
277 }
278 }
279 return true
280}
281
282func hasProperty(properties []interface{}, prop ruleProperty) bool {
283 for _, propertyStruct := range properties {
284 propertiesValue := reflect.ValueOf(propertyStruct).Elem()
285 for _, v := range prop.fields {
286 if !propertiesValue.IsValid() {
287 break
288 }
289 propertiesValue = propertiesValue.FieldByName(v)
290 }
291 if !propertiesValue.IsValid() {
292 continue
293 }
294
295 check := func(v string) bool {
296 return prop.value == "*" || prop.value == v
297 }
298
299 if matchValue(propertiesValue, check) {
300 return true
301 }
302 }
303 return false
304}
305
306func matchValue(value reflect.Value, check func(string) bool) bool {
307 if !value.IsValid() {
308 return false
309 }
310
311 if value.Kind() == reflect.Ptr {
312 if value.IsNil() {
313 return check("")
314 }
315 value = value.Elem()
316 }
317
318 switch value.Kind() {
319 case reflect.String:
320 return check(value.String())
321 case reflect.Bool:
322 return check(strconv.FormatBool(value.Bool()))
323 case reflect.Int:
324 return check(strconv.FormatInt(value.Int(), 10))
325 case reflect.Slice:
326 slice, ok := value.Interface().([]string)
327 if !ok {
328 panic("Can only handle slice of string")
329 }
330 for _, v := range slice {
331 if check(v) {
332 return true
333 }
334 }
335 return false
336 }
337
338 panic("Can't handle type: " + value.Kind().String())
339}