• 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	"regexp"
21	"strconv"
22	"strings"
23
24	"github.com/google/blueprint/proptools"
25)
26
27// "neverallow" rules for the build system.
28//
29// This allows things which aren't related to the build system and are enforced
30// for sanity, in progress code refactors, or policy to be expressed in a
31// straightforward away disjoint from implementations and tests which should
32// work regardless of these restrictions.
33//
34// A module is disallowed if all of the following are true:
35// - it is in one of the "In" paths
36// - it is not in one of the "NotIn" paths
37// - it has all "With" properties matched
38// - - values are matched in their entirety
39// - - nil is interpreted as an empty string
40// - - nested properties are separated with a '.'
41// - - if the property is a list, any of the values in the list being matches
42//     counts as a match
43// - it has none of the "Without" properties matched (same rules as above)
44
45func RegisterNeverallowMutator(ctx RegisterMutatorsContext) {
46	ctx.BottomUp("neverallow", neverallowMutator).Parallel()
47}
48
49var neverallows = []Rule{}
50
51func init() {
52	AddNeverAllowRules(createIncludeDirsRules()...)
53	AddNeverAllowRules(createTrebleRules()...)
54	AddNeverAllowRules(createLibcoreRules()...)
55	AddNeverAllowRules(createMediaRules()...)
56	AddNeverAllowRules(createJavaDeviceForHostRules()...)
57	AddNeverAllowRules(createCcSdkVariantRules()...)
58	AddNeverAllowRules(createUncompressDexRules()...)
59}
60
61// Add a NeverAllow rule to the set of rules to apply.
62func AddNeverAllowRules(rules ...Rule) {
63	neverallows = append(neverallows, rules...)
64}
65
66func createIncludeDirsRules() []Rule {
67	// The list of paths that cannot be referenced using include_dirs
68	paths := []string{
69		"art",
70		"art/libnativebridge",
71		"art/libnativeloader",
72		"libcore",
73		"libnativehelper",
74		"external/apache-harmony",
75		"external/apache-xml",
76		"external/boringssl",
77		"external/bouncycastle",
78		"external/conscrypt",
79		"external/icu",
80		"external/okhttp",
81		"external/vixl",
82		"external/wycheproof",
83	}
84
85	// Create a composite matcher that will match if the value starts with any of the restricted
86	// paths. A / is appended to the prefix to ensure that restricting path X does not affect paths
87	// XY.
88	rules := make([]Rule, 0, len(paths))
89	for _, path := range paths {
90		rule :=
91			NeverAllow().
92				WithMatcher("include_dirs", StartsWith(path+"/")).
93				Because("include_dirs is deprecated, all usages of '" + path + "' have been migrated" +
94					" to use alternate mechanisms and so can no longer be used.")
95
96		rules = append(rules, rule)
97	}
98
99	return rules
100}
101
102func createTrebleRules() []Rule {
103	return []Rule{
104		NeverAllow().
105			In("vendor", "device").
106			With("vndk.enabled", "true").
107			Without("vendor", "true").
108			Without("product_specific", "true").
109			Because("the VNDK can never contain a library that is device dependent."),
110		NeverAllow().
111			With("vndk.enabled", "true").
112			Without("vendor", "true").
113			Without("owner", "").
114			Because("a VNDK module can never have an owner."),
115
116		// TODO(b/67974785): always enforce the manifest
117		NeverAllow().
118			Without("name", "libhidlbase-combined-impl").
119			Without("name", "libhidlbase").
120			Without("name", "libhidlbase_pgo").
121			With("product_variables.enforce_vintf_manifest.cflags", "*").
122			Because("manifest enforcement should be independent of ."),
123
124		// TODO(b/67975799): vendor code should always use /vendor/bin/sh
125		NeverAllow().
126			Without("name", "libc_bionic_ndk").
127			With("product_variables.treble_linker_namespaces.cflags", "*").
128			Because("nothing should care if linker namespaces are enabled or not"),
129
130		// Example:
131		// *NeverAllow().with("Srcs", "main.cpp"))
132	}
133}
134
135func createLibcoreRules() []Rule {
136	var coreLibraryProjects = []string{
137		"libcore",
138		"external/apache-harmony",
139		"external/apache-xml",
140		"external/bouncycastle",
141		"external/conscrypt",
142		"external/icu",
143		"external/okhttp",
144		"external/wycheproof",
145		"prebuilts",
146	}
147
148	// Core library constraints. The sdk_version: "none" can only be used in core library projects.
149	// Access to core library targets is restricted using visibility rules.
150	rules := []Rule{
151		NeverAllow().
152			NotIn(coreLibraryProjects...).
153			With("sdk_version", "none").
154			WithoutMatcher("name", Regexp("^android_.*stubs_current$")),
155	}
156
157	return rules
158}
159
160func createMediaRules() []Rule {
161	return []Rule{
162		NeverAllow().
163			With("libs", "updatable-media").
164			Because("updatable-media includes private APIs. Use updatable_media_stubs instead."),
165	}
166}
167
168func createJavaDeviceForHostRules() []Rule {
169	javaDeviceForHostProjectsAllowedList := []string{
170		"external/guava",
171		"external/robolectric-shadows",
172		"framework/layoutlib",
173	}
174
175	return []Rule{
176		NeverAllow().
177			NotIn(javaDeviceForHostProjectsAllowedList...).
178			ModuleType("java_device_for_host", "java_host_for_device").
179			Because("java_device_for_host can only be used in allowed projects"),
180	}
181}
182
183func createCcSdkVariantRules() []Rule {
184	sdkVersionOnlyAllowedList := []string{
185		// derive_sdk_prefer32 has stem: "derive_sdk" which conflicts with the derive_sdk.
186		// This sometimes works because the APEX modules that contain derive_sdk and
187		// derive_sdk_prefer32 suppress the platform installation rules, but fails when
188		// the APEX modules contain the SDK variant and the platform variant still exists.
189		"frameworks/base/apex/sdkextensions/derive_sdk",
190	}
191
192	platformVariantPropertiesAllowedList := []string{
193		// android_native_app_glue and libRSSupport use native_window.h but target old
194		// sdk versions (minimum and 9 respectively) where libnativewindow didn't exist,
195		// so they can't add libnativewindow to shared_libs to get the header directory
196		// for the platform variant.  Allow them to use the platform variant
197		// property to set shared_libs.
198		"prebuilts/ndk",
199		"frameworks/rs",
200	}
201
202	return []Rule{
203		NeverAllow().
204			NotIn(sdkVersionOnlyAllowedList...).
205			WithMatcher("sdk_variant_only", isSetMatcherInstance).
206			Because("sdk_variant_only can only be used in allowed projects"),
207		NeverAllow().
208			NotIn(platformVariantPropertiesAllowedList...).
209			WithMatcher("platform.shared_libs", isSetMatcherInstance).
210			Because("platform variant properties can only be used in allowed projects"),
211	}
212}
213
214func createUncompressDexRules() []Rule {
215	return []Rule{
216		NeverAllow().
217			NotIn("art").
218			WithMatcher("uncompress_dex", isSetMatcherInstance).
219			Because("uncompress_dex is only allowed for certain jars for test in art."),
220	}
221}
222
223func neverallowMutator(ctx BottomUpMutatorContext) {
224	m, ok := ctx.Module().(Module)
225	if !ok {
226		return
227	}
228
229	dir := ctx.ModuleDir() + "/"
230	properties := m.GetProperties()
231
232	osClass := ctx.Module().Target().Os.Class
233
234	for _, r := range neverallowRules(ctx.Config()) {
235		n := r.(*rule)
236		if !n.appliesToPath(dir) {
237			continue
238		}
239
240		if !n.appliesToModuleType(ctx.ModuleType()) {
241			continue
242		}
243
244		if !n.appliesToProperties(properties) {
245			continue
246		}
247
248		if !n.appliesToOsClass(osClass) {
249			continue
250		}
251
252		if !n.appliesToDirectDeps(ctx) {
253			continue
254		}
255
256		if !n.appliesToBootclasspathJar(ctx) {
257			continue
258		}
259
260		ctx.ModuleErrorf("violates " + n.String())
261	}
262}
263
264type ValueMatcher interface {
265	Test(string) bool
266	String() string
267}
268
269type equalMatcher struct {
270	expected string
271}
272
273func (m *equalMatcher) Test(value string) bool {
274	return m.expected == value
275}
276
277func (m *equalMatcher) String() string {
278	return "=" + m.expected
279}
280
281type anyMatcher struct {
282}
283
284func (m *anyMatcher) Test(value string) bool {
285	return true
286}
287
288func (m *anyMatcher) String() string {
289	return "=*"
290}
291
292var anyMatcherInstance = &anyMatcher{}
293
294type startsWithMatcher struct {
295	prefix string
296}
297
298func (m *startsWithMatcher) Test(value string) bool {
299	return strings.HasPrefix(value, m.prefix)
300}
301
302func (m *startsWithMatcher) String() string {
303	return ".starts-with(" + m.prefix + ")"
304}
305
306type regexMatcher struct {
307	re *regexp.Regexp
308}
309
310func (m *regexMatcher) Test(value string) bool {
311	return m.re.MatchString(value)
312}
313
314func (m *regexMatcher) String() string {
315	return ".regexp(" + m.re.String() + ")"
316}
317
318type notInListMatcher struct {
319	allowed []string
320}
321
322func (m *notInListMatcher) Test(value string) bool {
323	return !InList(value, m.allowed)
324}
325
326func (m *notInListMatcher) String() string {
327	return ".not-in-list(" + strings.Join(m.allowed, ",") + ")"
328}
329
330type isSetMatcher struct{}
331
332func (m *isSetMatcher) Test(value string) bool {
333	return value != ""
334}
335
336func (m *isSetMatcher) String() string {
337	return ".is-set"
338}
339
340var isSetMatcherInstance = &isSetMatcher{}
341
342type ruleProperty struct {
343	fields  []string // e.x.: Vndk.Enabled
344	matcher ValueMatcher
345}
346
347// A NeverAllow rule.
348type Rule interface {
349	In(path ...string) Rule
350
351	NotIn(path ...string) Rule
352
353	InDirectDeps(deps ...string) Rule
354
355	WithOsClass(osClasses ...OsClass) Rule
356
357	ModuleType(types ...string) Rule
358
359	NotModuleType(types ...string) Rule
360
361	BootclasspathJar() Rule
362
363	With(properties, value string) Rule
364
365	WithMatcher(properties string, matcher ValueMatcher) Rule
366
367	Without(properties, value string) Rule
368
369	WithoutMatcher(properties string, matcher ValueMatcher) Rule
370
371	Because(reason string) Rule
372}
373
374type rule struct {
375	// User string for why this is a thing.
376	reason string
377
378	paths       []string
379	unlessPaths []string
380
381	directDeps map[string]bool
382
383	osClasses []OsClass
384
385	moduleTypes       []string
386	unlessModuleTypes []string
387
388	props       []ruleProperty
389	unlessProps []ruleProperty
390
391	onlyBootclasspathJar bool
392}
393
394// Create a new NeverAllow rule.
395func NeverAllow() Rule {
396	return &rule{directDeps: make(map[string]bool)}
397}
398
399func (r *rule) In(path ...string) Rule {
400	r.paths = append(r.paths, cleanPaths(path)...)
401	return r
402}
403
404func (r *rule) NotIn(path ...string) Rule {
405	r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
406	return r
407}
408
409func (r *rule) InDirectDeps(deps ...string) Rule {
410	for _, d := range deps {
411		r.directDeps[d] = true
412	}
413	return r
414}
415
416func (r *rule) WithOsClass(osClasses ...OsClass) Rule {
417	r.osClasses = append(r.osClasses, osClasses...)
418	return r
419}
420
421func (r *rule) ModuleType(types ...string) Rule {
422	r.moduleTypes = append(r.moduleTypes, types...)
423	return r
424}
425
426func (r *rule) NotModuleType(types ...string) Rule {
427	r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
428	return r
429}
430
431func (r *rule) With(properties, value string) Rule {
432	return r.WithMatcher(properties, selectMatcher(value))
433}
434
435func (r *rule) WithMatcher(properties string, matcher ValueMatcher) Rule {
436	r.props = append(r.props, ruleProperty{
437		fields:  fieldNamesForProperties(properties),
438		matcher: matcher,
439	})
440	return r
441}
442
443func (r *rule) Without(properties, value string) Rule {
444	return r.WithoutMatcher(properties, selectMatcher(value))
445}
446
447func (r *rule) WithoutMatcher(properties string, matcher ValueMatcher) Rule {
448	r.unlessProps = append(r.unlessProps, ruleProperty{
449		fields:  fieldNamesForProperties(properties),
450		matcher: matcher,
451	})
452	return r
453}
454
455func selectMatcher(expected string) ValueMatcher {
456	if expected == "*" {
457		return anyMatcherInstance
458	}
459	return &equalMatcher{expected: expected}
460}
461
462func (r *rule) Because(reason string) Rule {
463	r.reason = reason
464	return r
465}
466
467func (r *rule) BootclasspathJar() Rule {
468	r.onlyBootclasspathJar = true
469	return r
470}
471
472func (r *rule) String() string {
473	s := "neverallow"
474	for _, v := range r.paths {
475		s += " dir:" + v + "*"
476	}
477	for _, v := range r.unlessPaths {
478		s += " -dir:" + v + "*"
479	}
480	for _, v := range r.moduleTypes {
481		s += " type:" + v
482	}
483	for _, v := range r.unlessModuleTypes {
484		s += " -type:" + v
485	}
486	for _, v := range r.props {
487		s += " " + strings.Join(v.fields, ".") + v.matcher.String()
488	}
489	for _, v := range r.unlessProps {
490		s += " -" + strings.Join(v.fields, ".") + v.matcher.String()
491	}
492	for k := range r.directDeps {
493		s += " deps:" + k
494	}
495	for _, v := range r.osClasses {
496		s += " os:" + v.String()
497	}
498	if r.onlyBootclasspathJar {
499		s += " inBcp"
500	}
501	if len(r.reason) != 0 {
502		s += " which is restricted because " + r.reason
503	}
504	return s
505}
506
507func (r *rule) appliesToPath(dir string) bool {
508	includePath := len(r.paths) == 0 || HasAnyPrefix(dir, r.paths)
509	excludePath := HasAnyPrefix(dir, r.unlessPaths)
510	return includePath && !excludePath
511}
512
513func (r *rule) appliesToDirectDeps(ctx BottomUpMutatorContext) bool {
514	if len(r.directDeps) == 0 {
515		return true
516	}
517
518	matches := false
519	ctx.VisitDirectDeps(func(m Module) {
520		if !matches {
521			name := ctx.OtherModuleName(m)
522			matches = r.directDeps[name]
523		}
524	})
525
526	return matches
527}
528
529func (r *rule) appliesToBootclasspathJar(ctx BottomUpMutatorContext) bool {
530	if !r.onlyBootclasspathJar {
531		return true
532	}
533
534	return InList(ctx.ModuleName(), ctx.Config().BootJars())
535}
536
537func (r *rule) appliesToOsClass(osClass OsClass) bool {
538	if len(r.osClasses) == 0 {
539		return true
540	}
541
542	for _, c := range r.osClasses {
543		if c == osClass {
544			return true
545		}
546	}
547
548	return false
549}
550
551func (r *rule) appliesToModuleType(moduleType string) bool {
552	return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
553}
554
555func (r *rule) appliesToProperties(properties []interface{}) bool {
556	includeProps := hasAllProperties(properties, r.props)
557	excludeProps := hasAnyProperty(properties, r.unlessProps)
558	return includeProps && !excludeProps
559}
560
561func StartsWith(prefix string) ValueMatcher {
562	return &startsWithMatcher{prefix}
563}
564
565func Regexp(re string) ValueMatcher {
566	r, err := regexp.Compile(re)
567	if err != nil {
568		panic(err)
569	}
570	return &regexMatcher{r}
571}
572
573func NotInList(allowed []string) ValueMatcher {
574	return &notInListMatcher{allowed}
575}
576
577// assorted utils
578
579func cleanPaths(paths []string) []string {
580	res := make([]string, len(paths))
581	for i, v := range paths {
582		res[i] = filepath.Clean(v) + "/"
583	}
584	return res
585}
586
587func fieldNamesForProperties(propertyNames string) []string {
588	names := strings.Split(propertyNames, ".")
589	for i, v := range names {
590		names[i] = proptools.FieldNameForProperty(v)
591	}
592	return names
593}
594
595func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
596	for _, v := range props {
597		if hasProperty(properties, v) {
598			return true
599		}
600	}
601	return false
602}
603
604func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
605	for _, v := range props {
606		if !hasProperty(properties, v) {
607			return false
608		}
609	}
610	return true
611}
612
613func hasProperty(properties []interface{}, prop ruleProperty) bool {
614	for _, propertyStruct := range properties {
615		propertiesValue := reflect.ValueOf(propertyStruct).Elem()
616		for _, v := range prop.fields {
617			if !propertiesValue.IsValid() {
618				break
619			}
620			propertiesValue = propertiesValue.FieldByName(v)
621		}
622		if !propertiesValue.IsValid() {
623			continue
624		}
625
626		check := func(value string) bool {
627			return prop.matcher.Test(value)
628		}
629
630		if matchValue(propertiesValue, check) {
631			return true
632		}
633	}
634	return false
635}
636
637func matchValue(value reflect.Value, check func(string) bool) bool {
638	if !value.IsValid() {
639		return false
640	}
641
642	if value.Kind() == reflect.Ptr {
643		if value.IsNil() {
644			return check("")
645		}
646		value = value.Elem()
647	}
648
649	switch value.Kind() {
650	case reflect.String:
651		return check(value.String())
652	case reflect.Bool:
653		return check(strconv.FormatBool(value.Bool()))
654	case reflect.Int:
655		return check(strconv.FormatInt(value.Int(), 10))
656	case reflect.Slice:
657		slice, ok := value.Interface().([]string)
658		if !ok {
659			panic("Can only handle slice of string")
660		}
661		for _, v := range slice {
662			if check(v) {
663				return true
664			}
665		}
666		return false
667	}
668
669	panic("Can't handle type: " + value.Kind().String())
670}
671
672var neverallowRulesKey = NewOnceKey("neverallowRules")
673
674func neverallowRules(config Config) []Rule {
675	return config.Once(neverallowRulesKey, func() interface{} {
676		// No test rules were set by setTestNeverallowRules, use the global rules
677		return neverallows
678	}).([]Rule)
679}
680
681// Overrides the default neverallow rules for the supplied config.
682//
683// For testing only.
684func SetTestNeverallowRules(config Config, testRules []Rule) {
685	config.Once(neverallowRulesKey, func() interface{} { return testRules })
686}
687