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