• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/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    vendor_partitions = set()
99    if OPTIONS.vendor_otatools:
100      vendor_partitions = {"vendor", "odm"}
101      common.RunVendoredHostInitVerifier(
102          product_out=target_files_dir,
103          partition_map={p: partition_map[p] for p in vendor_partitions})
104
105    common.RunHostInitVerifier(
106        product_out=target_files_dir,
107        partition_map={
108            p: partition_map[p]
109            for p in partition_map.keys() - vendor_partitions
110        })
111  except RuntimeError as err:
112    return [str(err)]
113  return []
114
115
116def CheckCombinedSepolicy(target_files_dir, partition_map, execute=True):
117  """Uses secilc to compile a split sepolicy file.
118
119  Depends on various */etc/selinux/* and */etc/vintf/* files within partitions.
120  """
121  errors = []
122
123  def get_file(partition, path):
124    if partition not in partition_map:
125      logger.warning('Cannot load SEPolicy files for missing partition %s',
126                     partition)
127      return None
128    file_path = os.path.join(target_files_dir, partition_map[partition], path)
129    if os.path.exists(file_path):
130      return file_path
131    return None
132
133  # Load the kernel sepolicy version from the FCM. This is normally provided
134  # directly to selinux.cpp as a build flag, but is also available in this file.
135  fcm_file = get_file('system', 'etc/vintf/compatibility_matrix.device.xml')
136  if not fcm_file:
137    errors.append('Missing required file for loading sepolicy: '
138                  '/system/etc/vintf/compatibility_matrix.device.xml')
139    return errors
140  kernel_sepolicy_version = ElementTree.parse(fcm_file).getroot().find(
141      'sepolicy/kernel-sepolicy-version').text
142
143  # Load the vendor's plat sepolicy version. This is the version used for
144  # locating sepolicy mapping files.
145  vendor_plat_version_file = get_file('vendor',
146                                      'etc/selinux/plat_sepolicy_vers.txt')
147  if not vendor_plat_version_file:
148    errors.append('Missing required sepolicy file %s' %
149                  vendor_plat_version_file)
150    return errors
151  with open(vendor_plat_version_file) as f:
152    vendor_plat_version = f.read().strip()
153
154  # Use the same flags and arguments as selinux.cpp OpenSplitPolicy().
155  cmd = ['secilc', '-m', '-M', 'true', '-G', '-N']
156  cmd.extend(['-c', kernel_sepolicy_version])
157  cmd.extend(['-o', os.path.join(target_files_dir, 'META/combined_sepolicy')])
158  cmd.extend(['-f', '/dev/null'])
159
160  required_policy_files = (
161      ('system', 'etc/selinux/plat_sepolicy.cil'),
162      ('system', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
163      ('vendor', 'etc/selinux/vendor_sepolicy.cil'),
164      ('vendor', 'etc/selinux/plat_pub_versioned.cil'),
165  )
166  for policy in (map(lambda partition_and_path: get_file(*partition_and_path),
167                     required_policy_files)):
168    if not policy:
169      errors.append('Missing required sepolicy file %s' % policy)
170      return errors
171    cmd.append(policy)
172
173  optional_policy_files = (
174      ('system', 'etc/selinux/mapping/%s.compat.cil' % vendor_plat_version),
175      ('system_ext', 'etc/selinux/system_ext_sepolicy.cil'),
176      ('system_ext', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
177      ('product', 'etc/selinux/product_sepolicy.cil'),
178      ('product', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
179      ('odm', 'etc/selinux/odm_sepolicy.cil'),
180  )
181  for policy in (map(lambda partition_and_path: get_file(*partition_and_path),
182                     optional_policy_files)):
183    if policy:
184      cmd.append(policy)
185
186  try:
187    if execute:
188      common.RunAndCheckOutput(cmd)
189    else:
190      return cmd
191  except RuntimeError as err:
192    errors.append(str(err))
193
194  return errors
195
196
197def CheckApexDuplicatePackages(target_files_dir, partition_map):
198  """Checks if the same APEX package name is provided by multiple partitions."""
199  errors = []
200
201  apex_packages = set()
202  for partition in partition_map.keys():
203    try:
204      apex_info = apex_utils.GetApexInfoForPartition(
205          target_files_dir, partition)
206    except RuntimeError as err:
207      errors.append(str(err))
208      apex_info = []
209    partition_apex_packages = set([info.package_name for info in apex_info])
210    duplicates = apex_packages.intersection(partition_apex_packages)
211    if duplicates:
212      errors.append(
213          'Duplicate APEX package_names found in multiple partitions: %s' %
214          ' '.join(duplicates))
215    apex_packages.update(partition_apex_packages)
216
217  return errors
218