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