• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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
48var neverallows = createNeverAllows()
49
50func createNeverAllows() []*rule {
51	rules := []*rule{}
52	rules = append(rules, createTrebleRules()...)
53	rules = append(rules, createLibcoreRules()...)
54	rules = append(rules, createMediaRules()...)
55	rules = append(rules, createJavaDeviceForHostRules()...)
56	return rules
57}
58
59func 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."),
71
72		// TODO(b/67974785): always enforce the manifest
73		neverallow().
74			without("name", "libhidltransport-impl-internal").
75			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",
99	}
100
101	var coreModules = []string{
102		"core-all",
103		"core-oj",
104		"core-libart",
105		"okhttp",
106		"bouncycastle",
107		"conscrypt",
108		"apache-xml",
109	}
110
111	// Core library constraints. Prevent targets adding dependencies on core
112	// library internals, which could lead to compatibility issues with the ART
113	// mainline module. They should use core.platform.api.stubs instead.
114	rules := []*rule{
115		neverallow().
116			notIn(append(coreLibraryProjects, "development")...).
117			with("no_standard_libs", "true"),
118	}
119
120	for _, m := range coreModules {
121		r := neverallow().
122			notIn(coreLibraryProjects...).
123			with("libs", m).
124			because("Only core libraries projects can depend on " + m)
125		rules = append(rules, r)
126	}
127	return rules
128}
129
130func createMediaRules() []*rule {
131	return []*rule{
132		neverallow().
133			with("libs", "updatable-media").
134			because("updatable-media includes private APIs. Use updatable_media_stubs instead."),
135	}
136}
137
138func createJavaDeviceForHostRules() []*rule {
139	javaDeviceForHostProjectsWhitelist := []string{
140		"external/robolectric-shadows",
141		"framework/layoutlib",
142	}
143
144	return []*rule{
145		neverallow().
146			notIn(javaDeviceForHostProjectsWhitelist...).
147			moduleType("java_device_for_host", "java_host_for_device").
148			because("java_device_for_host can only be used in whitelisted projects"),
149	}
150}
151
152func neverallowMutator(ctx BottomUpMutatorContext) {
153	m, ok := ctx.Module().(Module)
154	if !ok {
155		return
156	}
157
158	dir := ctx.ModuleDir() + "/"
159	properties := m.GetProperties()
160
161	for _, n := range neverallows {
162		if !n.appliesToPath(dir) {
163			continue
164		}
165
166		if !n.appliesToModuleType(ctx.ModuleType()) {
167			continue
168		}
169
170		if !n.appliesToProperties(properties) {
171			continue
172		}
173
174		ctx.ModuleErrorf("violates " + n.String())
175	}
176}
177
178type ruleProperty struct {
179	fields []string // e.x.: Vndk.Enabled
180	value  string   // e.x.: true
181}
182
183type rule struct {
184	// User string for why this is a thing.
185	reason string
186
187	paths       []string
188	unlessPaths []string
189
190	moduleTypes       []string
191	unlessModuleTypes []string
192
193	props       []ruleProperty
194	unlessProps []ruleProperty
195}
196
197func neverallow() *rule {
198	return &rule{}
199}
200
201func (r *rule) in(path ...string) *rule {
202	r.paths = append(r.paths, cleanPaths(path)...)
203	return r
204}
205
206func (r *rule) notIn(path ...string) *rule {
207	r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
208	return r
209}
210
211func (r *rule) moduleType(types ...string) *rule {
212	r.moduleTypes = append(r.moduleTypes, types...)
213	return r
214}
215
216func (r *rule) notModuleType(types ...string) *rule {
217	r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
218	return r
219}
220
221func (r *rule) with(properties, value string) *rule {
222	r.props = append(r.props, ruleProperty{
223		fields: fieldNamesForProperties(properties),
224		value:  value,
225	})
226	return r
227}
228
229func (r *rule) without(properties, value string) *rule {
230	r.unlessProps = append(r.unlessProps, ruleProperty{
231		fields: fieldNamesForProperties(properties),
232		value:  value,
233	})
234	return r
235}
236
237func (r *rule) because(reason string) *rule {
238	r.reason = reason
239	return r
240}
241
242func (r *rule) String() string {
243	s := "neverallow"
244	for _, v := range r.paths {
245		s += " dir:" + v + "*"
246	}
247	for _, v := range r.unlessPaths {
248		s += " -dir:" + v + "*"
249	}
250	for _, v := range r.moduleTypes {
251		s += " type:" + v
252	}
253	for _, v := range r.unlessModuleTypes {
254		s += " -type:" + v
255	}
256	for _, v := range r.props {
257		s += " " + strings.Join(v.fields, ".") + "=" + v.value
258	}
259	for _, v := range r.unlessProps {
260		s += " -" + strings.Join(v.fields, ".") + "=" + v.value
261	}
262	if len(r.reason) != 0 {
263		s += " which is restricted because " + r.reason
264	}
265	return s
266}
267
268func (r *rule) appliesToPath(dir string) bool {
269	includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths)
270	excludePath := hasAnyPrefix(dir, r.unlessPaths)
271	return includePath && !excludePath
272}
273
274func (r *rule) appliesToModuleType(moduleType string) bool {
275	return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
276}
277
278func (r *rule) appliesToProperties(properties []interface{}) bool {
279	includeProps := hasAllProperties(properties, r.props)
280	excludeProps := hasAnyProperty(properties, r.unlessProps)
281	return includeProps && !excludeProps
282}
283
284// assorted utils
285
286func cleanPaths(paths []string) []string {
287	res := make([]string, len(paths))
288	for i, v := range paths {
289		res[i] = filepath.Clean(v) + "/"
290	}
291	return res
292}
293
294func fieldNamesForProperties(propertyNames string) []string {
295	names := strings.Split(propertyNames, ".")
296	for i, v := range names {
297		names[i] = proptools.FieldNameForProperty(v)
298	}
299	return names
300}
301
302func hasAnyPrefix(s string, prefixes []string) bool {
303	for _, prefix := range prefixes {
304		if strings.HasPrefix(s, prefix) {
305			return true
306		}
307	}
308	return false
309}
310
311func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
312	for _, v := range props {
313		if hasProperty(properties, v) {
314			return true
315		}
316	}
317	return false
318}
319
320func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
321	for _, v := range props {
322		if !hasProperty(properties, v) {
323			return false
324		}
325	}
326	return true
327}
328
329func hasProperty(properties []interface{}, prop ruleProperty) bool {
330	for _, propertyStruct := range properties {
331		propertiesValue := reflect.ValueOf(propertyStruct).Elem()
332		for _, v := range prop.fields {
333			if !propertiesValue.IsValid() {
334				break
335			}
336			propertiesValue = propertiesValue.FieldByName(v)
337		}
338		if !propertiesValue.IsValid() {
339			continue
340		}
341
342		check := func(v string) bool {
343			return prop.value == "*" || prop.value == v
344		}
345
346		if matchValue(propertiesValue, check) {
347			return true
348		}
349	}
350	return false
351}
352
353func matchValue(value reflect.Value, check func(string) bool) bool {
354	if !value.IsValid() {
355		return false
356	}
357
358	if value.Kind() == reflect.Ptr {
359		if value.IsNil() {
360			return check("")
361		}
362		value = value.Elem()
363	}
364
365	switch value.Kind() {
366	case reflect.String:
367		return check(value.String())
368	case reflect.Bool:
369		return check(strconv.FormatBool(value.Bool()))
370	case reflect.Int:
371		return check(strconv.FormatInt(value.Int(), 10))
372	case reflect.Slice:
373		slice, ok := value.Interface().([]string)
374		if !ok {
375			panic("Can only handle slice of string")
376		}
377		for _, v := range slice {
378			if check(v) {
379				return true
380			}
381		}
382		return false
383	}
384
385	panic("Can't handle type: " + value.Kind().String())
386}
387