blob: 207abe2555bbbb0958594b058e1a281bf3d447d2 [file] [log] [blame]
Daniel Norman2465fc82022-03-02 12:01:20 -08001#!/usr/bin/env python
2#
3# Copyright (C) 2022 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
16#
17"""Compatibility checks that should be performed on merged target_files."""
18
19import json
20import logging
21import os
22from xml.etree import ElementTree
23
24import apex_utils
25import check_target_files_vintf
26import common
27import find_shareduid_violation
28
29logger = logging.getLogger(__name__)
30OPTIONS = common.OPTIONS
31
32
33def CheckCompatibility(target_files_dir, partition_map):
34 """Runs various compatibility checks.
35
36 Returns a possibly-empty list of error messages.
37 """
38 errors = []
39
40 errors.extend(CheckVintf(target_files_dir))
41 errors.extend(CheckShareduidViolation(target_files_dir, partition_map))
42 errors.extend(CheckApexDuplicatePackages(target_files_dir, partition_map))
43
44 # The remaining checks only use the following partitions:
45 partition_map = {
46 partition: path
47 for partition, path in partition_map.items()
48 if partition in ('system', 'system_ext', 'product', 'vendor', 'odm')
49 }
50
51 errors.extend(CheckInitRcFiles(target_files_dir, partition_map))
52 errors.extend(CheckCombinedSepolicy(target_files_dir, partition_map))
53
54 return errors
55
56
57def CheckVintf(target_files_dir):
58 """Check for any VINTF issues using check_vintf."""
59 errors = []
60 try:
61 if not check_target_files_vintf.CheckVintf(target_files_dir):
62 errors.append('Incompatible VINTF.')
63 except RuntimeError as err:
64 errors.append(str(err))
65 return errors
66
67
68def CheckShareduidViolation(target_files_dir, partition_map):
69 """Check for any APK sharedUserId violations across partition sets.
70
71 Writes results to META/shareduid_violation_modules.json to help
72 with followup debugging.
73 """
74 errors = []
75 violation = find_shareduid_violation.FindShareduidViolation(
76 target_files_dir, partition_map)
77 shareduid_violation_modules = os.path.join(
78 target_files_dir, 'META', 'shareduid_violation_modules.json')
79 with open(shareduid_violation_modules, 'w') as f:
80 # Write the output to a file to enable debugging.
81 f.write(violation)
82
83 # Check for violations across the partition sets.
84 shareduid_errors = common.SharedUidPartitionViolations(
85 json.loads(violation),
86 [OPTIONS.framework_partition_set, OPTIONS.vendor_partition_set])
87 if shareduid_errors:
88 for error in shareduid_errors:
89 errors.append('APK sharedUserId error: %s' % error)
90 errors.append('See APK sharedUserId violations file: %s' %
91 shareduid_violation_modules)
92 return errors
93
94
95def CheckInitRcFiles(target_files_dir, partition_map):
96 """Check for any init.rc issues using host_init_verifier."""
97 try:
98 common.RunHostInitVerifier(
99 product_out=target_files_dir, partition_map=partition_map)
100 except RuntimeError as err:
101 return [str(err)]
102 return []
103
104
105def CheckCombinedSepolicy(target_files_dir, partition_map, execute=True):
106 """Uses secilc to compile a split sepolicy file.
107
108 Depends on various */etc/selinux/* and */etc/vintf/* files within partitions.
109 """
110 errors = []
111
112 def get_file(partition, path):
113 if partition not in partition_map:
114 logger.warning('Cannot load SEPolicy files for missing partition %s',
115 partition)
116 return None
117 file_path = os.path.join(target_files_dir, partition_map[partition], path)
118 if os.path.exists(file_path):
119 return file_path
120 return None
121
122 # Load the kernel sepolicy version from the FCM. This is normally provided
123 # directly to selinux.cpp as a build flag, but is also available in this file.
124 fcm_file = get_file('system', 'etc/vintf/compatibility_matrix.device.xml')
125 if not fcm_file:
126 errors.append('Missing required file for loading sepolicy: '
127 '/system/etc/vintf/compatibility_matrix.device.xml')
128 return errors
129 kernel_sepolicy_version = ElementTree.parse(fcm_file).getroot().find(
130 'sepolicy/kernel-sepolicy-version').text
131
132 # Load the vendor's plat sepolicy version. This is the version used for
133 # locating sepolicy mapping files.
134 vendor_plat_version_file = get_file('vendor',
135 'etc/selinux/plat_sepolicy_vers.txt')
136 if not vendor_plat_version_file:
137 errors.append('Missing required sepolicy file %s' %
138 vendor_plat_version_file)
139 return errors
140 with open(vendor_plat_version_file) as f:
141 vendor_plat_version = f.read().strip()
142
143 # Use the same flags and arguments as selinux.cpp OpenSplitPolicy().
144 cmd = ['secilc', '-m', '-M', 'true', '-G', '-N']
145 cmd.extend(['-c', kernel_sepolicy_version])
146 cmd.extend(['-o', os.path.join(target_files_dir, 'META/combined_sepolicy')])
147 cmd.extend(['-f', '/dev/null'])
148
149 required_policy_files = (
150 ('system', 'etc/selinux/plat_sepolicy.cil'),
151 ('system', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
152 ('vendor', 'etc/selinux/vendor_sepolicy.cil'),
153 ('vendor', 'etc/selinux/plat_pub_versioned.cil'),
154 )
155 for policy in (map(lambda partition_and_path: get_file(*partition_and_path),
156 required_policy_files)):
157 if not policy:
158 errors.append('Missing required sepolicy file %s' % policy)
159 return errors
160 cmd.append(policy)
161
162 optional_policy_files = (
163 ('system', 'etc/selinux/mapping/%s.compat.cil' % vendor_plat_version),
164 ('system_ext', 'etc/selinux/system_ext_sepolicy.cil'),
165 ('system_ext', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
166 ('product', 'etc/selinux/product_sepolicy.cil'),
167 ('product', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
168 ('odm', 'etc/selinux/odm_sepolicy.cil'),
169 )
170 for policy in (map(lambda partition_and_path: get_file(*partition_and_path),
171 optional_policy_files)):
172 if policy:
173 cmd.append(policy)
174
175 try:
176 if execute:
177 common.RunAndCheckOutput(cmd)
178 else:
179 return cmd
180 except RuntimeError as err:
181 errors.append(str(err))
182
183 return errors
184
185
186def CheckApexDuplicatePackages(target_files_dir, partition_map):
187 """Checks if the same APEX package name is provided by multiple partitions."""
188 errors = []
189
190 apex_packages = set()
191 for partition in partition_map.keys():
192 try:
193 apex_info = apex_utils.GetApexInfoFromTargetFiles(
194 target_files_dir, partition, compressed_only=False)
195 except RuntimeError as err:
196 errors.append(str(err))
197 apex_info = []
198 partition_apex_packages = set([info.package_name for info in apex_info])
199 duplicates = apex_packages.intersection(partition_apex_packages)
200 if duplicates:
201 errors.append(
202 'Duplicate APEX package_names found in multiple partitions: %s' %
203 ' '.join(duplicates))
204 apex_packages.update(partition_apex_packages)
205
206 return errors