diff options
author | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-03-13 02:55:30 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-03-13 02:55:30 +0000 |
commit | dc983e04f07814be7440c85754c1dd8d12d96b90 (patch) | |
tree | 10c0a6fa97d3c840080084324ac401664c9bb6e4 | |
parent | e6de5c26c4f26c9b2f7f2a69e2cc116374dac99d (diff) | |
parent | 2d354379a0ed78bf0a777bde65025557df077377 (diff) | |
download | vndk-dc983e04f07814be7440c85754c1dd8d12d96b90.tar.gz |
Add vts_vndk_open_libraries_test am: 2d354379a0
Change-Id: I209cf06853d25bb1474d4dd6645109e005b9d2f3
-rw-r--r-- | Android.bp | 13 | ||||
-rw-r--r-- | open_libraries/vts_vndk_open_libraries_test.py | 162 | ||||
-rw-r--r-- | open_libraries/vts_vndk_open_libraries_test.xml | 23 | ||||
-rw-r--r-- | utils.py | 17 |
4 files changed, 206 insertions, 9 deletions
@@ -75,3 +75,16 @@ python_test_host { ], test_config: "files/vts_vndk_files_test.xml", } + +python_test_host { + name: "vts_vndk_open_libraries_test", + defaults: ["vts_vndk_default"], + main: "open_libraries/vts_vndk_open_libraries_test.py", + srcs: [ + "open_libraries/vts_vndk_open_libraries_test.py", + ], + test_suites: [ + "vts-core", + ], + test_config: "open_libraries/vts_vndk_open_libraries_test.xml", +} diff --git a/open_libraries/vts_vndk_open_libraries_test.py b/open_libraries/vts_vndk_open_libraries_test.py new file mode 100644 index 0000000..aac1b66 --- /dev/null +++ b/open_libraries/vts_vndk_open_libraries_test.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 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 logging +import os +import posixpath as target_path_module +import re +import unittest + +from vts.testcases.vndk import utils +from vts.testcases.vndk.golden import vndk_data +from vts.utils.python.vndk import vndk_utils + + +class VtsVndkOpenLibrariesTest(unittest.TestCase): + """A test module to verify libraries opened by running processes. + + Attributes: + _dut: The AndroidDevice under test. + """ + + def setUp(self): + """Initializes attributes.""" + serial_number = os.environ.get("ANDROID_SERIAL") + self.assertTrue(serial_number, "$ANDROID_SERIAL is empty.") + self._dut = utils.AndroidDevice(serial_number) + + def _ListProcessCommands(self, cmd_filter): + """Finds current processes whose commands match the filter. + + Args: + cmd_filter: A function that takes a binary file path as argument + and returns whether the path matches the condition. + + Returns: + A dict of {pid: command} where pid and command are strings. + """ + ps_cmd = ["ps", "-Aw", "-o", "PID,COMMAND"] + out, err, return_code = self._dut.Execute(*ps_cmd) + if err.strip(): + logging.info("`%s` stderr: %s", " ".join(ps_cmd), err) + self.assertEqual(return_code, 0) + + lines = out.split("\n") + pid_end = lines[0].index("PID") + len("PID") + cmd_begin = lines[0].index("COMMAND", pid_end) + cmds = {} + for line in lines[1:]: + cmd = line[cmd_begin:] + if not cmd_filter(cmd): + continue + pid = line[:pid_end].lstrip() + cmds[pid] = cmd + return cmds + + def _ListOpenFiles(self, pids, file_filter): + """Finds open files whose names match the filter. + + Args: + pids: A collection of strings, the PIDs to list open files. + file_filter: A function that takes a file path as argument and + returns whether the path matches the condition. + + Returns: + A dict of {pid: [file, ...]} where pid and file are strings. + """ + lsof_cmd = ["lsof", "-p", ",".join(pids)] + out, err, return_code = self._dut.Execute(*lsof_cmd) + if err.strip(): + logging.info("`%s` stderr: %s", " ".join(lsof), err) + self.assertEqual(return_code, 0) + # The first line consists of the column names: + # COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME + # PID is right-justified. NAME is left-justified. + lines = out.split("\n") + pid_end = lines[0].index("PID") + len("PID") + name_begin = lines[0].index("NAME") + files = {} + for line in lines[1:]: + if not line.strip(): + continue + # On Android, COMMAND may exceed the column and causes the right + # columns to be misaligned. This program looks for digits in the + # PID column or on the right of the column. + try: + match_pid = next(match for match in + re.finditer(r"\s(\d+)\s", line) if + match.end(1) >= pid_end) + except StopIteration: + self.fail("Cannot parse PID from lsof output: " + line) + offset = match_pid.end(1) - pid_end + self.assertEqual(line[name_begin + offset - 1], " ", + "Cannot parse NAME from lsof output: " + line) + name = line[name_begin + offset:] + if not file_filter(name): + continue + pid = match_pid.group(1) + if pid in files: + files[pid].append(name) + else: + files[pid] = [name] + return files + + def testVendorProcessOpenLibraries(self): + """Checks if vendor processes load shared libraries on system.""" + if not vndk_utils.IsVndkRuntimeEnforced(self._dut): + logging.info("Skip the test as VNDK runtime is not enforced on " + "the device.") + return + vndk_lists = vndk_data.LoadVndkLibraryListsFromResources( + self._dut.GetVndkVersion(), + vndk_data.LL_NDK, + vndk_data.LL_NDK_PRIVATE, + vndk_data.VNDK, + vndk_data.VNDK_PRIVATE, + vndk_data.VNDK_SP, + vndk_data.VNDK_SP_PRIVATE) + self.assertTrue(vndk_lists, "Cannot load VNDK library lists.") + allowed_libs = set().union(*vndk_lists) + logging.debug("Allowed system libraries: %s", allowed_libs) + + self.assertTrue(self._dut.IsRoot(), + "Must be root to find all libraries in use.") + cmds = self._ListProcessCommands(lambda x: (x.startswith("/odm/") or + x.startswith("/vendor/"))) + + def _IsDisallowedSystemLib(lib_path): + return ((lib_path.startswith("/system/") or + lib_path.startswith("/apex/")) and + lib_path.endswith(".so") and + target_path_module.basename(lib_path) not in allowed_libs) + + deps = self._ListOpenFiles(cmds.keys(), _IsDisallowedSystemLib) + if deps: + error_lines = ["%s %s %s" % (pid, cmds[pid], libs) + for pid, libs in deps.items()] + logging.error("pid command libraries\n%s", "\n".join(error_lines)) + + assert_lines = ["pid command libraries"] + error_lines[:20] + if len(deps) > 20: + assert_lines.append("...") + assert_lines.append("Number of vendor processes using system " + "libraries: " + str(len(deps))) + self.fail("\n".join(assert_lines)) + + +if __name__ == "__main__": + unittest.main() diff --git a/open_libraries/vts_vndk_open_libraries_test.xml b/open_libraries/vts_vndk_open_libraries_test.xml new file mode 100644 index 0000000..664f60e --- /dev/null +++ b/open_libraries/vts_vndk_open_libraries_test.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 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. +--> +<configuration description="Config for vts_vndk_open_libraries_test"> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> + <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest"> + <option name="par-file-name" value="vts_vndk_open_libraries_test" /> + <option name="test-timeout" value="2m" /> + </test> +</configuration> + @@ -32,7 +32,7 @@ class AndroidDevice(object): subprocess.check_call(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - def _ExecuteCommand(self, *args): + def Execute(self, *args): """Executes a command. Args: @@ -66,7 +66,7 @@ class AndroidDevice(object): Raises: IOError if the command fails. """ - out, err, return_code = self._ExecuteCommand("getprop", name) + out, err, return_code = self.Execute("getprop", name) if err.strip() or return_code != 0: raise IOError("`getprop %s` stdout: %s\nstderr: %s" % (name, out, err)) @@ -143,14 +143,14 @@ class AndroidDevice(object): def IsRoot(self): """Returns whether adb has root privilege on the device.""" - out, err, return_code = self._ExecuteCommand("id") + out, err, return_code = self.Execute("id") if err.strip() or return_code != 0: raise IOError("`id` stdout: %s\nstderr: %s \n" % (out, err)) return "uid=0(root)" in out.strip() def _Test(self, *args): """Tests file types and status.""" - out, err, return_code = self._ExecuteCommand("test", *args) + out, err, return_code = self.Execute("test", *args) if out.strip() or err.strip(): raise IOError("`test` args: %s\nstdout: %s\nstderr: %s" % (args, out, err)) @@ -166,8 +166,7 @@ class AndroidDevice(object): def _Stat(self, fmt, path): """Executes stat command.""" - out, err, return_code = self._ExecuteCommand("stat", "--format", fmt, - path) + out, err, return_code = self.Execute("stat", "--format", fmt, path) if return_code != 0 or err.strip(): raise IOError("`stat --format %s %s` stdout: %s\nstderr: %s" % (fmt, path, out, err)) @@ -194,9 +193,9 @@ class AndroidDevice(object): """ if '"' in name_pattern or "'" in name_pattern: raise ValueError("File name pattern contains quotes.") - out, err, return_code = self._ExecuteCommand("find", path, "-name", - "'" + name_pattern + "'", - *options) + out, err, return_code = self.Execute("find", path, "-name", + "'" + name_pattern + "'", + *options) if return_code != 0 or err.strip(): raise IOError("`find %s -name '%s' %s` stdout: %s\nstderr: %s" % (path, name_pattern, " ".join(options), out, err)) |