compat_generator: find new types and removed types

To generate compat files, we need:

- base plat sepolicy
- old plat sepolicy
- base plat pub sepolicy
- mapping file from the device
- latest compat files

Generator now triggers the build system itself to get necessary base
files, and then uses the artifacts to extract new types and removed
types.

For the next step, the new/removed types will be mapped to old types,
based on the latest compat files.

Bug: 214336258
Test: sepolicy_generate_compat --branch sc-v2-dev --target-version \
    32.0 --latest-version 31.0 -vvvv --build latest
Change-Id: I1f228233c1e3638e78bc0630ae51e48667a12ef5
This commit is contained in:
Inseob Kim 2022-01-21 19:32:53 +09:00
parent 4ec796aa2f
commit cbc95ea5e2
3 changed files with 146 additions and 29 deletions

View file

@ -25,34 +25,46 @@ cc_library_host_shared {
},
}
python_library_host {
name: "mini_cil_parser",
srcs: ["mini_parser.py"],
}
python_library_host {
name: "pysepolwrap",
srcs: [
"fc_sort.py",
"policy.py",
],
}
python_binary_host {
name: "treble_sepolicy_tests",
srcs: [
"fc_sort.py",
"mini_parser.py",
"policy.py",
"treble_sepolicy_tests.py",
],
libs: [
"mini_cil_parser",
"pysepolwrap",
],
data: [":libsepolwrap"],
}
python_binary_host {
name: "sepolicy_tests",
srcs: [
"fc_sort.py",
"policy.py",
"sepolicy_tests.py",
],
libs: ["pysepolwrap"],
data: [":libsepolwrap"],
}
python_binary_host {
name: "searchpolicy",
srcs: [
"fc_sort.py",
"policy.py",
"searchpolicy.py",
],
libs: ["pysepolwrap"],
required: ["libsepolwrap"],
}
@ -60,8 +72,8 @@ python_binary_host {
name: "combine_maps",
srcs: [
"combine_maps.py",
"mini_parser.py",
],
libs: ["mini_cil_parser"],
}
python_binary_host {

View file

@ -67,4 +67,6 @@ python_binary_host {
python_binary_host {
name: "sepolicy_generate_compat",
srcs: ["sepolicy_generate_compat.py"],
libs: ["mini_cil_parser", "pysepolwrap"],
data: [":libsepolwrap"],
}

View file

@ -15,19 +15,27 @@
# limitations under the License.
import argparse
import distutils.ccompiler
import glob
import logging
import mini_parser
import os
import policy
import shutil
import subprocess
import tempfile
import zipfile
"""This tool generates a mapping file for {ver} core sepolicy."""
temp_dir = ''
def check_run(cmd):
logging.debug('Running cmd: %s' % cmd)
subprocess.run(cmd, check=True)
def check_run(cmd, cwd=None):
if cwd:
logging.debug('Running cmd at %s: %s' % (cwd, cmd))
else:
logging.debug('Running cmd: %s' % cmd)
subprocess.run(cmd, cwd=cwd, check=True)
def check_output(cmd):
@ -35,6 +43,15 @@ def check_output(cmd):
return subprocess.run(cmd, check=True, stdout=subprocess.PIPE)
def get_android_build_top():
ANDROID_BUILD_TOP = os.getenv('ANDROID_BUILD_TOP')
if not ANDROID_BUILD_TOP:
sys.exit(
'Error: Missing ANDROID_BUILD_TOP env variable. Please run '
'\'. build/envsetup.sh; lunch <build target>\'. Exiting script.')
return ANDROID_BUILD_TOP
def fetch_artifact(branch, build, pattern, destination='.'):
"""Fetches build artifacts from Android Build server.
@ -64,15 +81,20 @@ def extract_mapping_file_from_img(img_path, ver, destination='.'):
img_path: string, path to system.img file
ver: string, version of designated mapping file
destination: string, destination to pull the mapping file to
Returns:
string, path to extracted mapping file
"""
cmd = [
'debugfs', '-R',
'cat system/etc/selinux/mapping/%s.cil' % ver, img_path
]
with open(os.path.join(destination, '%s.cil' % ver), 'wb') as f:
path = os.path.join(destination, '%s.cil' % ver)
with open(path, 'wb') as f:
logging.debug('Extracting %s.cil to %s' % (ver, destination))
f.write(check_output(cmd).stdout)
return path
def download_mapping_file(branch, build, ver, destination='.'):
@ -83,24 +105,55 @@ def download_mapping_file(branch, build, ver, destination='.'):
build: string, build ID or "latest"
ver: string, version of designated mapping file (e.g. "32.0")
destination: string, destination to pull build artifact to
Returns:
string, path to extracted mapping file
"""
temp_dir = tempfile.mkdtemp()
logging.info('Downloading %s mapping file from branch %s build %s...' %
(ver, branch, build))
artifact_pattern = 'aosp_arm64-img-*.zip'
fetch_artifact(branch, build, artifact_pattern, temp_dir)
try:
artifact_pattern = 'aosp_arm64-img-*.zip'
fetch_artifact(branch, build, artifact_pattern, temp_dir)
# glob must succeed
zip_path = glob.glob(os.path.join(temp_dir, artifact_pattern))[0]
with zipfile.ZipFile(zip_path) as zip_file:
logging.debug('Extracting system.img to %s' % temp_dir)
zip_file.extract('system.img', temp_dir)
# glob must succeed
zip_path = glob.glob(os.path.join(temp_dir, artifact_pattern))[0]
with zipfile.ZipFile(zip_path) as zip_file:
logging.debug('Extracting system.img to %s' % temp_dir)
zip_file.extract('system.img', temp_dir)
system_img_path = os.path.join(temp_dir, 'system.img')
return extract_mapping_file_from_img(system_img_path, ver, destination)
system_img_path = os.path.join(temp_dir, 'system.img')
extract_mapping_file_from_img(system_img_path, ver, destination)
finally:
logging.info('Deleting temporary dir: {}'.format(temp_dir))
shutil.rmtree(temp_dir)
def build_base_files(target_version):
""" Builds needed base policy files from the source code.
Args:
target_version: string, target version to gerenate the mapping file
Returns:
(string, string, string), paths to base policy, old policy, and pub policy
cil
"""
logging.info('building base sepolicy files')
build_top = get_android_build_top()
cmd = [
'build/soong/soong_ui.bash',
'--make-mode',
'dist',
'base-sepolicy-files-for-mapping',
'TARGET_PRODUCT=aosp_arm64',
'TARGET_BUILD_VARIANT=userdebug',
]
check_run(cmd, cwd=build_top)
dist_dir = os.path.join(build_top, 'out', 'dist')
base_policy_path = os.path.join(dist_dir, 'base_plat_sepolicy')
old_policy_path = os.path.join(dist_dir,
'%s_plat_sepolicy' % target_version)
pub_policy_cil_path = os.path.join(dist_dir, 'base_plat_pub_policy.cil')
return base_policy_path, old_policy_path, pub_policy_cil_path
def get_args():
@ -111,9 +164,13 @@ def get_args():
help='Branch to pull build from. e.g. "sc-v2-dev"')
parser.add_argument('--build', required=True, help='Build ID, or "latest"')
parser.add_argument(
'--version',
'--target-version',
required=True,
help='Version of designated mapping file. e.g. "32.0"')
help='Target version of designated mapping file. e.g. "32.0"')
parser.add_argument(
'--latest-version',
required=True,
help='Latest version for mapping of newer types. e.g. "31.0"')
parser.add_argument(
'-v',
'--verbose',
@ -131,7 +188,53 @@ def main():
format='%(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
level=(logging.WARNING, logging.INFO, logging.DEBUG)[verbosity])
download_mapping_file(args.branch, args.build, args.version)
global temp_dir
temp_dir = tempfile.mkdtemp()
try:
libpath = os.path.join(
os.path.dirname(os.path.realpath(__file__)), 'libsepolwrap' +
distutils.ccompiler.new_compiler().shared_lib_extension)
if not os.path.exists(libpath):
sys.exit(
'Error: libsepolwrap does not exist. Is this binary corrupted?\n'
)
build_top = get_android_build_top()
sepolicy_path = os.path.join(build_top, 'system', 'sepolicy')
target_compat_path = os.path.join(sepolicy_path, 'private', 'compat',
args.target_version)
# Step 1. Download system/etc/selinux/mapping/{ver}.cil, and remove types/typeattributes
mapping_file = download_mapping_file(args.branch, args.build,
args.target_version)
mapping_file_cil = mini_parser.MiniCilParser(mapping_file)
mapping_file_cil.types = set()
mapping_file_cil.typeattributes = set()
# Step 2. Build base policy files and parse latest mapping files
base_policy_path, old_policy_path, pub_policy_cil_path = build_base_files(
args.target_version)
base_policy = policy.Policy(base_policy_path, None, libpath)
old_policy = policy.Policy(old_policy_path, None, libpath)
pub_policy_cil = mini_parser.MiniCilParser(pub_policy_cil_path)
all_types = base_policy.GetAllTypes(False)
old_all_types = old_policy.GetAllTypes(False)
pub_types = pub_policy_cil.types
# Step 3. Find new types and removed types
new_types = pub_types & (all_types - old_all_types)
removed_types = (mapping_file_cil.pubtypes - mapping_file_cil.types) & (
old_all_types - all_types)
logging.info('new types: %s' % new_types)
logging.info('removed types: %s' % removed_types)
# TODO: Step 4. Map new types and removed types appropriately
finally:
logging.info('Deleting temporary dir: {}'.format(temp_dir))
shutil.rmtree(temp_dir)
if __name__ == '__main__':