Sync internal master and AOSP sepolicy.

Bug: 37916906
Test: Builds 'n' boots.
Change-Id: Ia1d86264446ebecc1ca79f32f11354921bc77668
Merged-In: I208ec6a864127a059fb389417a9c6b259d7474cb
diff --git a/tests/treble_sepolicy_tests.py b/tests/treble_sepolicy_tests.py
index 3d6a480..2c0cef3 100644
--- a/tests/treble_sepolicy_tests.py
+++ b/tests/treble_sepolicy_tests.py
@@ -1,7 +1,9 @@
 from optparse import OptionParser
 from optparse import Option, OptionValueError
 import os
+import mini_parser
 import policy
+from policy import MatchPathPrefix
 import re
 import sys
 
@@ -69,26 +71,13 @@
 appdomains = set()
 vendordomains = set()
 
-###
-# Check whether the regex will match a file path starting with the provided
-# prefix
-#
-# Compares regex entries in file_contexts with a path prefix. Regex entries
-# are often more specific than this file prefix. For example, the regex could
-# be /system/bin/foo\.sh and the prefix could be /system. This function
-# loops over the regex removing characters from the end until
-# 1) there is a match - return True or 2) run out of characters - return
-#    False.
-#
-def MatchPathPrefix(pathregex, prefix):
-    for i in range(len(pathregex), 0, -1):
-        try:
-            pattern = re.compile('^' + pathregex[0:i] + "$")
-        except:
-            continue
-        if pattern.match(prefix):
-            return True
-    return False
+# compat vars
+alltypes = set()
+oldalltypes = set()
+compatMapping = None
+
+# Distinguish between PRODUCT_FULL_TREBLE and PRODUCT_FULL_TREBLE_OVERRIDE
+FakeTreble = False
 
 def GetAllDomains(pol):
     global alldomains
@@ -107,12 +96,11 @@
             alldomains[d].appdomain = True
             appdomains.add(d)
 
-
 def GetCoreDomains():
     global alldomains
     global coredomains
     for d in alldomains:
-        # TestCoredomainViolators will verify if coredomain was incorrectly
+        # TestCoredomainViolations will verify if coredomain was incorrectly
         # applied.
         if "coredomain" in alldomains[d].attributes:
             alldomains[d].coredomain = True
@@ -144,7 +132,7 @@
 #
 def GetDomainEntrypoints(pol):
     global alldomains
-    for x in pol.QueryTERule(tclass="file", perms=["entrypoint"]):
+    for x in pol.QueryExpandedTERule(tclass=set(["file"]), perms=set(["entrypoint"])):
         if not x.sctx in alldomains:
             continue
         alldomains[x.sctx].entrypoints.append(str(x.tctx))
@@ -167,6 +155,12 @@
         for result in pol.QueryTypeAttribute(domain, False):
             alldomains[domain].attributes.add(result)
 
+def GetAllTypes(pol, oldpol):
+    global alltypes
+    global oldalltypes
+    alltypes = pol.GetAllTypes(False)
+    oldalltypes = oldpol.GetAllTypes(False)
+
 def setup(pol):
     GetAllDomains(pol)
     GetAttributes(pol)
@@ -174,6 +168,21 @@
     GetAppDomains()
     GetCoreDomains()
 
+# setup for the policy compatibility tests
+def compatSetup(pol, oldpol, mapping):
+    global compatMapping
+
+    GetAllTypes(pol, oldpol)
+    compatMapping = mapping
+
+def DomainsWithAttribute(attr):
+    global alldomains
+    domains = []
+    for domain in alldomains:
+        if attr in alldomains[domain].attributes:
+            domains.append(domain)
+    return domains
+
 #############################################################
 # Tests
 #############################################################
@@ -209,6 +218,75 @@
     return ret
 
 ###
+# Make sure that any new type introduced in the new policy that was not present
+# in the old policy has been recorded in the mapping file.
+def TestNoUnmappedNewTypes():
+    global alltypes
+    global oldalltypes
+    global compatMapping
+    newt = alltypes - oldalltypes
+    ret = ""
+    violators = []
+
+    for n in newt:
+        if compatMapping.rTypeattributesets.get(n) is None:
+            violators.append(n)
+
+    if len(violators) > 0:
+        ret += "SELinux: The following types were found added to the policy "
+        ret += "without an entry into the compatibility mapping file(s) found "
+        ret += "in private/compat/" + compatMapping.apiLevel + "/"
+        ret +=  compatMapping.apiLevel + "[.ignore].cil/n"
+        ret += " ".join(str(x) for x in sorted(violators)) + "\n"
+    return ret
+
+###
+# Make sure that any public type removed in the current policy has its
+# declaration added to the mapping file for use in non-platform policy
+def TestNoUnmappedRmTypes():
+    global alltypes
+    global oldalltypes
+    global compatMapping
+    rmt = oldalltypes - alltypes
+    ret = ""
+    violators = []
+
+    for o in rmt:
+        if o in compatMapping.pubtypes and not o in compatMapping.types:
+            violators.append(o)
+
+    if len(violators) > 0:
+        ret += "SELinux: The following formerly public types were removed from "
+        ret += "policy without a declaration in the compatibility mapping "
+        ret += "file(s) found in prebuilts/api/" + compatMapping.apiLevel + "/\n"
+        ret += " ".join(str(x) for x in sorted(violators)) + "\n"
+    return ret
+
+def TestTrebleCompatMapping():
+    ret = TestNoUnmappedNewTypes()
+    ret += TestNoUnmappedRmTypes()
+    return ret
+
+def TestViolatorAttribute(attribute):
+    global FakeTreble
+    ret = ""
+    if FakeTreble:
+        return ret
+
+    violators = DomainsWithAttribute(attribute)
+    if len(violators) > 0:
+        ret += "SELinux: The following domains violate the Treble ban "
+        ret += "against use of the " + attribute + " attribute: "
+        ret += " ".join(str(x) for x in sorted(violators)) + "\n"
+    return ret
+
+def TestViolatorAttributes():
+    ret = TestViolatorAttribute("binder_in_vendor_violators")
+    ret += TestViolatorAttribute("socket_between_core_and_vendor_violators")
+    ret += TestViolatorAttribute("vendor_executes_system_violators")
+    return ret
+
+###
 # extend OptionParser to allow the same option flag to be used multiple times.
 # This is used to allow multiple file_contexts files and tests to be
 # specified.
@@ -225,18 +303,27 @@
         else:
             Option.take_action(self, action, dest, opt, value, values, parser)
 
-Tests = ["CoredomainViolators"]
+Tests = {"CoredomainViolations": TestCoredomainViolations,
+         "TrebleCompatMapping": TestTrebleCompatMapping,
+         "ViolatorAttributes": TestViolatorAttributes}
 
 if __name__ == '__main__':
-    usage = "sepolicy-trebletests -f nonplat_file_contexts -f "
-    usage +="plat_file_contexts -p policy [--test test] [--help]"
+    usage = "treble_sepolicy_tests.py -l out/host/linux-x86/lib64 "
+    usage += "-f nonplat_file_contexts -f plat_file_contexts "
+    usage += "-p curr_policy -b base_policy -o old_policy "
+    usage +="-m mapping file [--test test] [--help]"
     parser = OptionParser(option_class=MultipleOption, usage=usage)
+    parser.add_option("-b", "--basepolicy", dest="basepolicy", metavar="FILE")
     parser.add_option("-f", "--file_contexts", dest="file_contexts",
             metavar="FILE", action="extend", type="string")
-    parser.add_option("-p", "--policy", dest="policy", metavar="FILE")
     parser.add_option("-l", "--library-path", dest="libpath", metavar="FILE")
-    parser.add_option("-t", "--test", dest="test", action="extend",
+    parser.add_option("-m", "--mapping", dest="mapping", metavar="FILE")
+    parser.add_option("-o", "--oldpolicy", dest="oldpolicy", metavar="FILE")
+    parser.add_option("-p", "--policy", dest="policy", metavar="FILE")
+    parser.add_option("-t", "--test", dest="tests", action="extend",
             help="Test options include "+str(Tests))
+    parser.add_option("--fake-treble", action="store_true", dest="faketreble",
+            default=False)
 
     (options, args) = parser.parse_args()
 
@@ -245,9 +332,14 @@
     if not os.path.exists(options.libpath):
         sys.exit("Error: library-path " + options.libpath + " does not exist\n"
                 + parser.usage)
-
+    if not options.basepolicy:
+        sys.exit("Must specify the current platform-only policy file\n" + parser.usage)
+    if not options.mapping:
+        sys.exit("Must specify a compatibility mapping file\n" + parser.usage)
+    if not options.oldpolicy:
+        sys.exit("Must specify the previous monolithic policy file\n" + parser.usage)
     if not options.policy:
-        sys.exit("Must specify monolithic policy file\n" + parser.usage)
+        sys.exit("Must specify current monolithic policy file\n" + parser.usage)
     if not os.path.exists(options.policy):
         sys.exit("Error: policy file " + options.policy + " does not exist\n"
                 + parser.usage)
@@ -259,16 +351,35 @@
             sys.exit("Error: File_contexts file " + f + " does not exist\n" +
                     parser.usage)
 
+    if options.faketreble:
+        FakeTreble = True
+
     pol = policy.Policy(options.policy, options.file_contexts, options.libpath)
     setup(pol)
+    basepol = policy.Policy(options.basepolicy, None, options.libpath)
+    oldpol = policy.Policy(options.oldpolicy, None, options.libpath)
+    mapping = mini_parser.MiniCilParser(options.mapping)
+    compatSetup(basepol, oldpol, mapping)
 
     if DEBUG:
         PrintScontexts()
 
     results = ""
     # If an individual test is not specified, run all tests.
-    if options.test is None or "CoredomainViolations" in options.tests:
-        results += TestCoredomainViolations()
+    if options.tests is None:
+        for t in Tests.values():
+            results += t()
+    else:
+        for tn in options.tests:
+            t = Tests.get(tn)
+            if t:
+                results += t()
+            else:
+                err = "Error: unknown test: " + tn + "\n"
+                err += "Available tests:\n"
+                for tn in Tests.keys():
+                    err += tn + "\n"
+                sys.exit(err)
 
     if len(results) > 0:
         sys.exit(results)