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