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 "strconv" 21 "strings" 22 23 "github.com/google/blueprint/proptools" 24) 25 26// "neverallow" rules for the build system. 27// 28// This allows things which aren't related to the build system and are enforced 29// for sanity, in progress code refactors, or policy to be expressed in a 30// straightforward away disjoint from implementations and tests which should 31// work regardless of these restrictions. 32// 33// A module is disallowed if all of the following are true: 34// - it is in one of the "in" paths 35// - it is not in one of the "notIn" paths 36// - it has all "with" properties matched 37// - - values are matched in their entirety 38// - - nil is interpreted as an empty string 39// - - nested properties are separated with a '.' 40// - - if the property is a list, any of the values in the list being matches 41// counts as a match 42// - it has none of the "without" properties matched (same rules as above) 43 44func registerNeverallowMutator(ctx RegisterMutatorsContext) { 45 ctx.BottomUp("neverallow", neverallowMutator).Parallel() 46} 47 48var neverallows = createNeverAllows() 49 50func createNeverAllows() []*rule { 51 rules := []*rule{} 52 rules = append(rules, createTrebleRules()...) 53 rules = append(rules, createLibcoreRules()...) 54 rules = append(rules, createMediaRules()...) 55 rules = append(rules, createJavaDeviceForHostRules()...) 56 return rules 57} 58 59func createTrebleRules() []*rule { 60 return []*rule{ 61 neverallow(). 62 in("vendor", "device"). 63 with("vndk.enabled", "true"). 64 without("vendor", "true"). 65 because("the VNDK can never contain a library that is device dependent."), 66 neverallow(). 67 with("vndk.enabled", "true"). 68 without("vendor", "true"). 69 without("owner", ""). 70 because("a VNDK module can never have an owner."), 71 72 // TODO(b/67974785): always enforce the manifest 73 neverallow(). 74 without("name", "libhidltransport-impl-internal"). 75 with("product_variables.enforce_vintf_manifest.cflags", "*"). 76 because("manifest enforcement should be independent of ."), 77 78 // TODO(b/67975799): vendor code should always use /vendor/bin/sh 79 neverallow(). 80 without("name", "libc_bionic_ndk"). 81 with("product_variables.treble_linker_namespaces.cflags", "*"). 82 because("nothing should care if linker namespaces are enabled or not"), 83 84 // Example: 85 // *neverallow().with("Srcs", "main.cpp")) 86 } 87} 88 89func createLibcoreRules() []*rule { 90 var coreLibraryProjects = []string{ 91 "libcore", 92 "external/apache-harmony", 93 "external/apache-xml", 94 "external/bouncycastle", 95 "external/conscrypt", 96 "external/icu", 97 "external/okhttp", 98 "external/wycheproof", 99 } 100 101 var coreModules = []string{ 102 "core-all", 103 "core-oj", 104 "core-libart", 105 "okhttp", 106 "bouncycastle", 107 "conscrypt", 108 "apache-xml", 109 } 110 111 // Core library constraints. Prevent targets adding dependencies on core 112 // library internals, which could lead to compatibility issues with the ART 113 // mainline module. They should use core.platform.api.stubs instead. 114 rules := []*rule{ 115 neverallow(). 116 notIn(append(coreLibraryProjects, "development")...). 117 with("no_standard_libs", "true"), 118 } 119 120 for _, m := range coreModules { 121 r := neverallow(). 122 notIn(coreLibraryProjects...). 123 with("libs", m). 124 because("Only core libraries projects can depend on " + m) 125 rules = append(rules, r) 126 } 127 return rules 128} 129 130func createMediaRules() []*rule { 131 return []*rule{ 132 neverallow(). 133 with("libs", "updatable-media"). 134 because("updatable-media includes private APIs. Use updatable_media_stubs instead."), 135 } 136} 137 138func createJavaDeviceForHostRules() []*rule { 139 javaDeviceForHostProjectsWhitelist := []string{ 140 "external/robolectric-shadows", 141 "framework/layoutlib", 142 } 143 144 return []*rule{ 145 neverallow(). 146 notIn(javaDeviceForHostProjectsWhitelist...). 147 moduleType("java_device_for_host", "java_host_for_device"). 148 because("java_device_for_host can only be used in whitelisted projects"), 149 } 150} 151 152func neverallowMutator(ctx BottomUpMutatorContext) { 153 m, ok := ctx.Module().(Module) 154 if !ok { 155 return 156 } 157 158 dir := ctx.ModuleDir() + "/" 159 properties := m.GetProperties() 160 161 for _, n := range neverallows { 162 if !n.appliesToPath(dir) { 163 continue 164 } 165 166 if !n.appliesToModuleType(ctx.ModuleType()) { 167 continue 168 } 169 170 if !n.appliesToProperties(properties) { 171 continue 172 } 173 174 ctx.ModuleErrorf("violates " + n.String()) 175 } 176} 177 178type ruleProperty struct { 179 fields []string // e.x.: Vndk.Enabled 180 value string // e.x.: true 181} 182 183type rule struct { 184 // User string for why this is a thing. 185 reason string 186 187 paths []string 188 unlessPaths []string 189 190 moduleTypes []string 191 unlessModuleTypes []string 192 193 props []ruleProperty 194 unlessProps []ruleProperty 195} 196 197func neverallow() *rule { 198 return &rule{} 199} 200 201func (r *rule) in(path ...string) *rule { 202 r.paths = append(r.paths, cleanPaths(path)...) 203 return r 204} 205 206func (r *rule) notIn(path ...string) *rule { 207 r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...) 208 return r 209} 210 211func (r *rule) moduleType(types ...string) *rule { 212 r.moduleTypes = append(r.moduleTypes, types...) 213 return r 214} 215 216func (r *rule) notModuleType(types ...string) *rule { 217 r.unlessModuleTypes = append(r.unlessModuleTypes, types...) 218 return r 219} 220 221func (r *rule) with(properties, value string) *rule { 222 r.props = append(r.props, ruleProperty{ 223 fields: fieldNamesForProperties(properties), 224 value: value, 225 }) 226 return r 227} 228 229func (r *rule) without(properties, value string) *rule { 230 r.unlessProps = append(r.unlessProps, ruleProperty{ 231 fields: fieldNamesForProperties(properties), 232 value: value, 233 }) 234 return r 235} 236 237func (r *rule) because(reason string) *rule { 238 r.reason = reason 239 return r 240} 241 242func (r *rule) String() string { 243 s := "neverallow" 244 for _, v := range r.paths { 245 s += " dir:" + v + "*" 246 } 247 for _, v := range r.unlessPaths { 248 s += " -dir:" + v + "*" 249 } 250 for _, v := range r.moduleTypes { 251 s += " type:" + v 252 } 253 for _, v := range r.unlessModuleTypes { 254 s += " -type:" + v 255 } 256 for _, v := range r.props { 257 s += " " + strings.Join(v.fields, ".") + "=" + v.value 258 } 259 for _, v := range r.unlessProps { 260 s += " -" + strings.Join(v.fields, ".") + "=" + v.value 261 } 262 if len(r.reason) != 0 { 263 s += " which is restricted because " + r.reason 264 } 265 return s 266} 267 268func (r *rule) appliesToPath(dir string) bool { 269 includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths) 270 excludePath := hasAnyPrefix(dir, r.unlessPaths) 271 return includePath && !excludePath 272} 273 274func (r *rule) appliesToModuleType(moduleType string) bool { 275 return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes) 276} 277 278func (r *rule) appliesToProperties(properties []interface{}) bool { 279 includeProps := hasAllProperties(properties, r.props) 280 excludeProps := hasAnyProperty(properties, r.unlessProps) 281 return includeProps && !excludeProps 282} 283 284// assorted utils 285 286func cleanPaths(paths []string) []string { 287 res := make([]string, len(paths)) 288 for i, v := range paths { 289 res[i] = filepath.Clean(v) + "/" 290 } 291 return res 292} 293 294func fieldNamesForProperties(propertyNames string) []string { 295 names := strings.Split(propertyNames, ".") 296 for i, v := range names { 297 names[i] = proptools.FieldNameForProperty(v) 298 } 299 return names 300} 301 302func hasAnyPrefix(s string, prefixes []string) bool { 303 for _, prefix := range prefixes { 304 if strings.HasPrefix(s, prefix) { 305 return true 306 } 307 } 308 return false 309} 310 311func hasAnyProperty(properties []interface{}, props []ruleProperty) bool { 312 for _, v := range props { 313 if hasProperty(properties, v) { 314 return true 315 } 316 } 317 return false 318} 319 320func hasAllProperties(properties []interface{}, props []ruleProperty) bool { 321 for _, v := range props { 322 if !hasProperty(properties, v) { 323 return false 324 } 325 } 326 return true 327} 328 329func hasProperty(properties []interface{}, prop ruleProperty) bool { 330 for _, propertyStruct := range properties { 331 propertiesValue := reflect.ValueOf(propertyStruct).Elem() 332 for _, v := range prop.fields { 333 if !propertiesValue.IsValid() { 334 break 335 } 336 propertiesValue = propertiesValue.FieldByName(v) 337 } 338 if !propertiesValue.IsValid() { 339 continue 340 } 341 342 check := func(v string) bool { 343 return prop.value == "*" || prop.value == v 344 } 345 346 if matchValue(propertiesValue, check) { 347 return true 348 } 349 } 350 return false 351} 352 353func matchValue(value reflect.Value, check func(string) bool) bool { 354 if !value.IsValid() { 355 return false 356 } 357 358 if value.Kind() == reflect.Ptr { 359 if value.IsNil() { 360 return check("") 361 } 362 value = value.Elem() 363 } 364 365 switch value.Kind() { 366 case reflect.String: 367 return check(value.String()) 368 case reflect.Bool: 369 return check(strconv.FormatBool(value.Bool())) 370 case reflect.Int: 371 return check(strconv.FormatInt(value.Int(), 10)) 372 case reflect.Slice: 373 slice, ok := value.Interface().([]string) 374 if !ok { 375 panic("Can only handle slice of string") 376 } 377 for _, v := range slice { 378 if check(v) { 379 return true 380 } 381 } 382 return false 383 } 384 385 panic("Can't handle type: " + value.Kind().String()) 386} 387