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