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