Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 101dc0e0 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Finds APK shared UID violations when merging target files." am: 50c0f97f

Original change: https://android-review.googlesource.com/c/platform/build/+/1477396

Change-Id: I593b41fadd040a78b69be7031ecad813b4821e73
parents a216a3e2 50c0f97f
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

shareduid_violation_modules_filename := $(PRODUCT_OUT)/shareduid_violation_modules.json

find_shareduid_script := $(BUILD_SYSTEM)/tasks/find-shareduid-violation.py

$(shareduid_violation_modules_filename): $(INSTALLED_SYSTEMIMAGE_TARGET) \
    $(INSTALLED_RAMDISK_TARGET) \
    $(INSTALLED_BOOTIMAGE_TARGET) \
@@ -26,9 +24,9 @@ $(shareduid_violation_modules_filename): $(INSTALLED_SYSTEMIMAGE_TARGET) \
    $(INSTALLED_PRODUCTIMAGE_TARGET) \
    $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)

$(shareduid_violation_modules_filename): $(find_shareduid_script)
$(shareduid_violation_modules_filename): $(HOST_OUT_EXECUTABLES)/find_shareduid_violation
$(shareduid_violation_modules_filename): $(AAPT2)
	$(find_shareduid_script) \
	$(HOST_OUT_EXECUTABLES)/find_shareduid_violation \
		--product_out $(PRODUCT_OUT) \
		--aapt $(AAPT2) \
		--copy_out_system $(TARGET_COPY_OUT_SYSTEM) \
+0 −99
Original line number Diff line number Diff line
#!/usr/bin/env python3
#
# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import argparse
import json
import os
import subprocess
import sys

from collections import defaultdict
from glob import glob

def parse_args():
    """Parse commandline arguments."""
    parser = argparse.ArgumentParser(description='Find sharedUserId violators')
    parser.add_argument('--product_out', help='PRODUCT_OUT directory',
                        default=os.environ.get("PRODUCT_OUT"))
    parser.add_argument('--aapt', help='Path to aapt or aapt2',
                        default="aapt2")
    parser.add_argument('--copy_out_system', help='TARGET_COPY_OUT_SYSTEM',
                        default="system")
    parser.add_argument('--copy_out_vendor', help='TARGET_COPY_OUT_VENDOR',
                        default="vendor")
    parser.add_argument('--copy_out_product', help='TARGET_COPY_OUT_PRODUCT',
                        default="product")
    parser.add_argument('--copy_out_system_ext', help='TARGET_COPY_OUT_SYSTEM_EXT',
                        default="system_ext")
    return parser.parse_args()

def execute(cmd):
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = map(lambda b: b.decode('utf-8'), p.communicate())
    return p.returncode == 0, out, err

def make_aapt_cmds(file):
    return [aapt + ' dump ' + file + ' --file AndroidManifest.xml',
            aapt + ' dump xmltree ' + file + ' --file AndroidManifest.xml']

def extract_shared_uid(file):
    for cmd in make_aapt_cmds(file):
        success, manifest, error_msg = execute(cmd)
        if success:
            break
    else:
        print(error_msg, file=sys.stderr)
        sys.exit()

    for l in manifest.split('\n'):
        if "sharedUserId" in l:
            return l.split('"')[-2]
    return None


args = parse_args()

product_out = args.product_out
aapt = args.aapt

partitions = (
        ("system", args.copy_out_system),
        ("vendor", args.copy_out_vendor),
        ("product", args.copy_out_product),
        ("system_ext", args.copy_out_system_ext),
)

shareduid_app_dict = defaultdict(list)

for part, location in partitions:
    for f in glob(os.path.join(product_out, location, "*", "*", "*.apk")):
        apk_file = os.path.basename(f)
        shared_uid = extract_shared_uid(f)

        if shared_uid is None:
            continue
        shareduid_app_dict[shared_uid].append((part, apk_file))


output = defaultdict(lambda: defaultdict(list))

for uid, app_infos in shareduid_app_dict.items():
    partitions = {p for p, _ in app_infos}
    if len(partitions) > 1:
        for part in partitions:
            output[uid][part].extend([a for p, a in app_infos if p == part])

print(json.dumps(output, indent=2, sort_keys=True))
+28 −0
Original line number Diff line number Diff line
@@ -369,6 +369,32 @@ python_binary_host {
    ],
}

python_defaults {
    name: "releasetools_find_shareduid_violation_defaults",
    srcs: [
        "find_shareduid_violation.py",
    ],
    libs: [
        "releasetools_common",
    ],
}

python_binary_host {
    name: "find_shareduid_violation",
    defaults: [
        "releasetools_binary_defaults",
        "releasetools_find_shareduid_violation_defaults",
    ],
}

python_library_host {
    name: "releasetools_find_shareduid_violation",
    defaults: [
        "releasetools_find_shareduid_violation_defaults",
        "releasetools_library_defaults",
    ],
}

python_binary_host {
    name: "make_recovery_patch",
    defaults: ["releasetools_binary_defaults"],
@@ -403,6 +429,7 @@ python_binary_host {
        "releasetools_build_super_image",
        "releasetools_check_target_files_vintf",
        "releasetools_common",
        "releasetools_find_shareduid_violation",
        "releasetools_img_from_target_files",
        "releasetools_ota_from_target_files",
    ],
@@ -505,6 +532,7 @@ python_defaults {
        "releasetools_build_super_image",
        "releasetools_check_target_files_vintf",
        "releasetools_common",
        "releasetools_find_shareduid_violation",
        "releasetools_img_from_target_files",
        "releasetools_ota_from_target_files",
        "releasetools_verity_utils",
+175 −0
Original line number Diff line number Diff line
#!/usr/bin/env python
#
# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Find APK sharedUserId violators.

Usage: find_shareduid_violation [args]

  --product_out
    PRODUCT_OUT directory

  --aapt
    Path to aapt or aapt2

  --copy_out_system
    TARGET_COPY_OUT_SYSTEM

  --copy_out_vendor_
    TARGET_COPY_OUT_VENDOR

  --copy_out_product
    TARGET_COPY_OUT_PRODUCT

  --copy_out_system_ext
    TARGET_COPY_OUT_SYSTEM_EXT
"""

import json
import logging
import os
import re
import subprocess
import sys

from collections import defaultdict
from glob import glob

import common

logger = logging.getLogger(__name__)

OPTIONS = common.OPTIONS
OPTIONS.product_out = os.environ.get("PRODUCT_OUT")
OPTIONS.aapt = "aapt2"
OPTIONS.copy_out_system = "system"
OPTIONS.copy_out_vendor = "vendor"
OPTIONS.copy_out_product = "product"
OPTIONS.copy_out_system_ext = "system_ext"


def execute(cmd):
  p = subprocess.Popen(
      cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  out, err = map(lambda b: b.decode("utf-8"), p.communicate())
  return p.returncode == 0, out, err


def make_aapt_cmds(aapt, apk):
  return [
      aapt + " dump " + apk + " --file AndroidManifest.xml",
      aapt + " dump xmltree " + apk + " --file AndroidManifest.xml"
  ]


def extract_shared_uid(aapt, apk):
  for cmd in make_aapt_cmds(aapt, apk):
    success, manifest, error_msg = execute(cmd)
    if success:
      break
  else:
    logger.error(error_msg)
    sys.exit()

  pattern = re.compile(r"sharedUserId.*=\"([^\"]*)")

  for line in manifest.split("\n"):
    match = pattern.search(line)
    if match:
      return match.group(1)
  return None


def FindShareduidViolation(product_out, partition_map, aapt="aapt2"):
  """Find sharedUserId violators in the given partitions.

  Args:
    product_out: The base directory containing the partition directories.
    partition_map: A map of partition name -> directory name.
    aapt: The name of the aapt binary. Defaults to aapt2.

  Returns:
    A string containing a JSON object describing the shared UIDs.
  """
  shareduid_app_dict = defaultdict(lambda: defaultdict(list))

  for part, location in partition_map.items():
    for f in glob(os.path.join(product_out, location, "*", "*", "*.apk")):
      apk_file = os.path.basename(f)
      shared_uid = extract_shared_uid(aapt, f)

      if shared_uid is None:
        continue
      shareduid_app_dict[shared_uid][part].append(apk_file)

  # Only output sharedUserId values that appear in >1 partition.
  output = {}
  for uid, partitions in shareduid_app_dict.items():
    if len(partitions) > 1:
      output[uid] = shareduid_app_dict[uid]

  return json.dumps(output, indent=2, sort_keys=True)


def main():
  common.InitLogging()

  def option_handler(o, a):
    if o == "--product_out":
      OPTIONS.product_out = a
    elif o == "--aapt":
      OPTIONS.aapt = a
    elif o == "--copy_out_system":
      OPTIONS.copy_out_system = a
    elif o == "--copy_out_vendor":
      OPTIONS.copy_out_vendor = a
    elif o == "--copy_out_product":
      OPTIONS.copy_out_product = a
    elif o == "--copy_out_system_ext":
      OPTIONS.copy_out_system_ext = a
    else:
      return False
    return True

  args = common.ParseOptions(
      sys.argv[1:],
      __doc__,
      extra_long_opts=[
          "product_out=",
          "aapt=",
          "copy_out_system=",
          "copy_out_vendor=",
          "copy_out_product=",
          "copy_out_system_ext=",
      ],
      extra_option_handler=option_handler)

  if args:
    common.Usage(__doc__)
    sys.exit(1)

  partition_map = {
      "system": OPTIONS.copy_out_system,
      "vendor": OPTIONS.copy_out_vendor,
      "product": OPTIONS.copy_out_product,
      "system_ext": OPTIONS.copy_out_system_ext,
  }

  print(
      FindShareduidViolation(OPTIONS.product_out, partition_map, OPTIONS.aapt))


if __name__ == "__main__":
  main()
+16 −0
Original line number Diff line number Diff line
@@ -98,6 +98,7 @@ import build_super_image
import check_target_files_vintf
import common
import img_from_target_files
import find_shareduid_violation
import ota_from_target_files

logger = logging.getLogger(__name__)
@@ -943,6 +944,21 @@ def merge_target_files(temp_dir, framework_target_files, framework_item_list,
  if not check_target_files_vintf.CheckVintf(output_target_files_temp_dir):
    raise RuntimeError('Incompatible VINTF metadata')

  shareduid_violation_modules = os.path.join(
      output_target_files_temp_dir, 'META', 'shareduid_violation_modules.json')
  with open(shareduid_violation_modules, 'w') as f:
    partition_map = {
        'system': 'SYSTEM',
        'vendor': 'VENDOR',
        'product': 'PRODUCT',
        'system_ext': 'SYSTEM_EXT',
    }
    violation = find_shareduid_violation.FindShareduidViolation(
        output_target_files_temp_dir, partition_map)
    f.write(violation)
    # TODO(b/171431774): Add a check to common.py to check if the
    # shared UIDs cross the input build partition boundary.

  generate_images(output_target_files_temp_dir, rebuild_recovery)

  generate_super_empty_image(output_target_files_temp_dir, output_super_empty)