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