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 ®exMatcher{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