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 ®exMatcher{r} 622} 623 624func NotInList(allowed []string) ValueMatcher { 625 return ¬InListMatcher{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