1#!/usr/bin/env python
2#
3# Copyright (C) 2017 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the 'License');
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of 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,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import logging
19import os
20import tempfile
21
22from vts.runners.host import keys
23from vts.runners.host import test_runner
24from vts.utils.python.controllers import adb
25from vts.utils.python.controllers import android_device
26from vts.utils.python.common import vts_spec_utils
27from vts.utils.python.fuzzer import corpus_manager
28
29from vts.testcases.fuzz.template.libfuzzer_test import libfuzzer_test_config as config
30from vts.testcases.fuzz.template.libfuzzer_test import libfuzzer_test
31from vts.testcases.fuzz.template.libfuzzer_test import libfuzzer_test_case
32
33
34class FuncFuzzerTest(libfuzzer_test.LibFuzzerTest):
35    """Runs function fuzzer tests on target.
36
37    Attributes:
38        _dut: AndroidDevice, the device under test as config.
39        _test_cases: LibFuzzerTestCase list, list of test cases to run.
40        _vts_spec_parser: VtsSpecParser, used to parse .vts files.
41        _temp_dir: temporary directory, used as buffer between target and GCS.
42    """
43
44    def setUpClass(self):
45        """Creates a remote shell instance, and copies data files."""
46        required_params = [
47            keys.ConfigKeys.IKEY_DATA_FILE_PATH,
48            keys.ConfigKeys.IKEY_HAL_HIDL_PACKAGE_NAME,
49        ]
50        self.getUserParams(required_params)
51        logging.info('%s: %s', keys.ConfigKeys.IKEY_DATA_FILE_PATH,
52                     self.data_file_path)
53        logging.info('%s: %s', keys.ConfigKeys.IKEY_HAL_HIDL_PACKAGE_NAME,
54                     self.hal_hidl_package_name)
55
56        self._dut = self.android_devices[0]
57        self._dut.adb.shell('mkdir %s -p' % config.FUZZER_TEST_DIR)
58        self._vts_spec_parser = vts_spec_utils.VtsSpecParser(
59            self.data_file_path)
60        self._temp_dir = tempfile.mkdtemp()
61        self._corpus_manager = corpus_manager.CorpusManager(self.user_params, self._dut)
62
63    def _RegisteredInterfaces(self, hal_package):
64        """Returns a list of registered interfaces for a given hal package.
65
66        Args:
67            hal_package: string, name of hal package,
68                e.g. android.hardware.nfc@1.0
69
70        Returns:
71            list of string, list of interfaces from this package that are
72                registered on device under test.
73        """
74        # TODO: find a more robust way to query registered interfaces.
75        cmd = '"lshal | grep -v \* | grep -o %s::[[:alpha:]]* | sort -u"' % hal_package
76        out = str(self._dut.adb.shell(cmd)).split()
77        interfaces = map(lambda x: x.split('::')[-1], out)
78        return interfaces
79
80    def _FuzzerBinHostPath(self, hal_package, vts_spec_name):
81        """Returns path to fuzzer binary on host."""
82        vts_spec_name = vts_spec_name.replace('.vts', '')
83        bin_name = hal_package + '-vts.func_fuzzer.' + vts_spec_name
84        bin_host_path = os.path.join(self.data_file_path, 'DATA', 'bin',
85                                     bin_name)
86        return str(bin_host_path)
87
88    def _CreateTestCasesFromSpec(self, hal_package, vts_spec_name,
89                                 vts_spec_proto):
90        """Creates LibFuzzerTestCases.
91
92        Args:
93            hal_package: string, name of hal package,
94                e.g. android.hardware.nfc@1.0
95            vts_spec_name: string, e.g. 'Nfc.vts'.
96
97        Returns:
98            LibFuzzerTestCase list, one per function of interface corresponding
99                to vts_spec_name.
100        """
101        test_cases = []
102        for api in vts_spec_proto.interface.api:
103            additional_params = {'vts_target_func': api.name}
104            libfuzzer_params = config.FUZZER_DEFAULT_PARAMS
105            bin_host_path = self._FuzzerBinHostPath(hal_package, vts_spec_name)
106            test_case = libfuzzer_test_case.LibFuzzerTestCase(
107                bin_host_path, libfuzzer_params, additional_params)
108            test_case.test_name = api.name
109            test_cases.append(test_case)
110        return test_cases
111
112    # Override
113    def CreateTestCases(self):
114        """See base class."""
115        hal_package = self.hal_hidl_package_name
116        hal_name, hal_version = vts_spec_utils.HalPackageToNameAndVersion(
117            hal_package)
118        vts_spec_names = self._vts_spec_parser.VtsSpecNames(
119            hal_name, hal_version)
120
121        registered_interfaces = self._RegisteredInterfaces(
122            self.hal_hidl_package_name)
123        test_cases = []
124        for vts_spec_name in vts_spec_names:
125            vts_spec_proto = self._vts_spec_parser.VtsSpecProto(
126                hal_name, hal_version, vts_spec_name)
127            if not vts_spec_proto.component_name in registered_interfaces:
128                continue
129            test_cases += self._CreateTestCasesFromSpec(
130                hal_package, vts_spec_name, vts_spec_proto)
131        return test_cases
132
133
134if __name__ == '__main__':
135    test_runner.main()
136