#!/usr/bin/env python3.4 # # Copyright (C) 2016 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 os import shutil import sys from subprocess import call from tempfile import mkdtemp sys.path.append(os.path.dirname(os.path.dirname( os.path.realpath(__file__)))) from common.common import FatalError from common.common import GetEnvVariableOrError from common.common import GetJackClassPath from common.common import RetCode from common.common import RunCommand # # Tester class. # class DexFuzzTester(object): """Tester that feeds JFuzz programs into DexFuzz testing.""" def __init__(self, num_tests, num_inputs, device): """Constructor for the tester. Args: num_tests: int, number of tests to run num_inputs: int, number of JFuzz programs to generate device: string, target device serial number (or None) """ self._num_tests = num_tests self._num_inputs = num_inputs self._device = device self._save_dir = None self._results_dir = None self._dexfuzz_dir = None self._inputs_dir = None self._dexfuzz_env = None def __enter__(self): """On entry, enters new temp directory after saving current directory. Raises: FatalError: error when temp directory cannot be constructed """ self._save_dir = os.getcwd() self._results_dir = mkdtemp(dir='/tmp/') self._dexfuzz_dir = mkdtemp(dir=self._results_dir) self._inputs_dir = mkdtemp(dir=self._dexfuzz_dir) if self._results_dir is None or self._dexfuzz_dir is None or \ self._inputs_dir is None: raise FatalError('Cannot obtain temp directory') self._dexfuzz_env = os.environ.copy() self._dexfuzz_env['ANDROID_DATA'] = self._dexfuzz_dir top = GetEnvVariableOrError('ANDROID_BUILD_TOP') self._dexfuzz_env['PATH'] = (top + '/art/tools/bisection_search:' + self._dexfuzz_env['PATH']) android_root = GetEnvVariableOrError('ANDROID_HOST_OUT') self._dexfuzz_env['ANDROID_ROOT'] = android_root self._dexfuzz_env['LD_LIBRARY_PATH'] = android_root + '/lib' os.chdir(self._dexfuzz_dir) os.mkdir('divergent_programs') os.mkdir('bisection_outputs') return self def __exit__(self, etype, evalue, etraceback): """On exit, re-enters previously saved current directory and cleans up.""" os.chdir(self._save_dir) # TODO: detect divergences or shutil.rmtree(self._results_dir) def Run(self): """Feeds JFuzz programs into DexFuzz testing.""" print() print('**\n**** J/DexFuzz Testing\n**') print() print('#Tests :', self._num_tests) print('Device :', self._device) print('Directory :', self._results_dir) print() self.GenerateJFuzzPrograms() self.RunDexFuzz() def GenerateJFuzzPrograms(self): """Generates JFuzz programs. Raises: FatalError: error when generation fails """ os.chdir(self._inputs_dir) for i in range(1, self._num_inputs + 1): jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java'] if RunCommand(['jfuzz'], out='Test.java', err=None) != RetCode.SUCCESS: print('Unexpected error while running JFuzz') raise FatalError('Unexpected error while running JFuzz') if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt', timeout=30) != RetCode.SUCCESS: print('Unexpected error while running Jack') raise FatalError('Unexpected error while running Jack') shutil.move('Test.java', '../Test' + str(i) + '.java') shutil.move('classes.dex', 'classes' + str(i) + '.dex') os.unlink('jackerr.txt') def RunDexFuzz(self): """Starts the DexFuzz testing.""" os.chdir(self._dexfuzz_dir) dexfuzz_args = ['--inputs=' + self._inputs_dir, '--execute', '--execute-class=Test', '--repeat=' + str(self._num_tests), '--quiet', '--dump-output', '--dump-verify', '--interpreter', '--optimizing', '--bisection-search'] if self._device is not None: dexfuzz_args += ['--device=' + self._device, '--allarm'] else: dexfuzz_args += ['--host'] # Assume host otherwise. cmd = ['dexfuzz'] + dexfuzz_args print('**** Running ****\n\n', cmd, '\n') call(cmd, env=self._dexfuzz_env) print('\n**** Results (report.log) ****\n') call(['tail', '-n 24', 'report.log']) def main(): # Handle arguments. parser = argparse.ArgumentParser() parser.add_argument('--num_tests', default=1000, type=int, help='number of tests to run') parser.add_argument('--num_inputs', default=10, type=int, help='number of JFuzz program to generate') parser.add_argument('--device', help='target device serial number') args = parser.parse_args() # Run the DexFuzz tester. with DexFuzzTester(args.num_tests, args.num_inputs, args.device) as fuzzer: fuzzer.Run() if __name__ == '__main__': main()