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