| #!/usr/bin/env python |
| # |
| # Copyright (C) 2017 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 |
| |
| from vts.runners.host import asserts |
| from vts.runners.host import base_test |
| from vts.runners.host import const |
| from vts.runners.host import keys |
| from vts.runners.host import test_runner |
| from vts.testcases.vndk.golden import vndk_data |
| from vts.utils.python.os import path_utils |
| from vts.utils.python.vndk import vndk_utils |
| |
| |
| class VtsVndkOpenLibrariesTest(base_test.BaseTestClass): |
| """A test module to verify libraries opened by running processes. |
| |
| Attributes: |
| data_file_path: The path to VTS data directory. |
| _dut: The AndroidDevice under test. |
| _shell: The ShellMirrorObject to execute commands |
| """ |
| |
| def setUpClass(self): |
| """Initializes the data file path and shell.""" |
| required_params = [keys.ConfigKeys.IKEY_DATA_FILE_PATH] |
| self.getUserParams(required_params) |
| self._dut = self.android_devices[0] |
| self._shell = self._dut.shell |
| |
| 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. |
| """ |
| result = self._shell.Execute("ps -Aw -o PID,COMMAND") |
| asserts.assertEqual(result[const.EXIT_CODE][0], 0) |
| lines = result[const.STDOUT][0].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) |
| result = self._shell.Execute(lsof_cmd) |
| asserts.assertEqual(result[const.EXIT_CODE][0], 0) |
| lines = result[const.STDOUT][0].split("\n") |
| pid_end = lines[0].index("PID") + len("PID") |
| name_begin = lines[0].index("NAME") |
| files = {} |
| for line in lines[1:]: |
| name = line[name_begin:] |
| if not file_filter(name): |
| continue |
| pid_begin = line.rindex(" ", 0, pid_end) + 1 |
| pid = line[pid_begin:pid_end] |
| 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.""" |
| asserts.skipIf(not vndk_utils.IsVndkRuntimeEnforced(self._dut), |
| "VNDK runtime is not enforced on the device.") |
| vndk_lists = vndk_data.LoadVndkLibraryLists( |
| self.data_file_path, |
| self._dut.vndk_version, |
| 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) |
| asserts.assertTrue(vndk_lists, "Cannot load VNDK library lists.") |
| allowed_libs = set().union(*vndk_lists) |
| logging.debug("Allowed system libraries: %s", allowed_libs) |
| |
| asserts.assertTrue(self._dut.isAdbRoot, |
| "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/") and |
| lib_path.endswith(".so") and |
| path_utils.TargetBaseName(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.iteritems()] |
| logging.error("pid command libraries\n%s", "\n".join(error_lines)) |
| asserts.fail("Number of vendor processes using system libraries: " + |
| str(len(deps))) |
| |
| |
| if __name__ == "__main__": |
| test_runner.main() |