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