1# Copyright 2021 The Android Open Source Project 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 15from optparse import OptionParser 16from optparse import Option, OptionValueError 17import os 18import mini_parser 19import re 20import shutil 21import sys 22import tempfile 23 24''' 25Verify that Treble compatibility are not broken. 26''' 27 28 29############################################################# 30# Tests 31############################################################# 32 33### 34# Make sure that any new public type introduced in the new policy that was not 35# present in the old policy has been recorded in the mapping file. 36def TestNoUnmappedNewTypes(base_pub_policy, old_pub_policy, mapping): 37 newt = base_pub_policy.types - old_pub_policy.types 38 ret = "" 39 violators = [] 40 41 for n in newt: 42 if mapping.rTypeattributesets.get(n) is None: 43 violators.append(n) 44 45 if len(violators) > 0: 46 ret += "SELinux: The following public types were found added to the " 47 ret += "policy without an entry into the compatibility mapping file(s) " 48 ret += "found in private/compat/V.v/V.v[.ignore].cil, where V.v is the " 49 ret += "latest API level.\n" 50 ret += " ".join(str(x) for x in sorted(violators)) + "\n\n" 51 ret += "See examples of how to fix this:\n" 52 ret += "https://android-review.googlesource.com/c/platform/system/sepolicy/+/781036\n" 53 ret += "https://android-review.googlesource.com/c/platform/system/sepolicy/+/852612\n" 54 return ret 55 56### 57# Make sure that any public type removed in the current policy has its 58# declaration added to the mapping file for use in non-platform policy 59def TestNoUnmappedRmTypes(base_pub_policy, old_pub_policy, mapping): 60 rmt = old_pub_policy.types - base_pub_policy.types 61 ret = "" 62 violators = [] 63 64 for o in rmt: 65 if o in mapping.pubtypes and not o in mapping.types: 66 violators.append(o) 67 68 if len(violators) > 0: 69 ret += "SELinux: The following formerly public types were removed from " 70 ret += "policy without a declaration in the compatibility mapping " 71 ret += "found in private/compat/V.v/V.v[.ignore].cil, where V.v is the " 72 ret += "latest API level.\n" 73 ret += " ".join(str(x) for x in sorted(violators)) + "\n\n" 74 ret += "See examples of how to fix this:\n" 75 ret += "https://android-review.googlesource.com/c/platform/system/sepolicy/+/822743\n" 76 return ret 77 78def TestTrebleCompatMapping(base_pub_policy, old_pub_policy, mapping): 79 ret = TestNoUnmappedNewTypes(base_pub_policy, old_pub_policy, mapping) 80 ret += TestNoUnmappedRmTypes(base_pub_policy, old_pub_policy, mapping) 81 return ret 82 83### 84# extend OptionParser to allow the same option flag to be used multiple times. 85# This is used to allow multiple file_contexts files and tests to be 86# specified. 87# 88class MultipleOption(Option): 89 ACTIONS = Option.ACTIONS + ("extend",) 90 STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",) 91 TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",) 92 ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",) 93 94 def take_action(self, action, dest, opt, value, values, parser): 95 if action == "extend": 96 values.ensure_value(dest, []).append(value) 97 else: 98 Option.take_action(self, action, dest, opt, value, values, parser) 99 100def do_main(): 101 usage = "treble_sepolicy_tests " 102 usage += "-b base_pub_policy -o old_pub_policy " 103 usage += "-m mapping file [--test test] [--help]" 104 parser = OptionParser(option_class=MultipleOption, usage=usage) 105 parser.add_option("-b", "--base-pub-policy", dest="base_pub_policy", 106 metavar="FILE") 107 parser.add_option("-m", "--mapping", dest="mapping", metavar="FILE") 108 parser.add_option("-o", "--old-pub-policy", dest="old_pub_policy", 109 metavar="FILE") 110 111 (options, args) = parser.parse_args() 112 113 # Mapping files and public platform policy are only necessary for the 114 # TrebleCompatMapping test. 115 if not options.mapping: 116 sys.exit("Must specify a compatibility mapping file\n" 117 + parser.usage) 118 if not options.old_pub_policy: 119 sys.exit("Must specify the previous public policy .cil file\n" 120 + parser.usage) 121 if not options.base_pub_policy: 122 sys.exit("Must specify the current platform-only public policy " 123 + ".cil file\n" + parser.usage) 124 mapping = mini_parser.MiniCilParser(options.mapping) 125 base_pub_policy = mini_parser.MiniCilParser(options.base_pub_policy) 126 old_pub_policy = mini_parser.MiniCilParser(options.old_pub_policy) 127 128 results = TestTrebleCompatMapping(base_pub_policy, old_pub_policy, mapping) 129 130 if len(results) > 0: 131 sys.exit(results) 132 133if __name__ == '__main__': 134 do_main() 135