1#!/usr/bin/env python3.4 2# 3# Copyright (C) 2016 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 17import argparse 18import os 19import shutil 20import sys 21 22from subprocess import call 23from tempfile import mkdtemp 24 25sys.path.append(os.path.dirname(os.path.dirname( 26 os.path.realpath(__file__)))) 27 28from common.common import FatalError 29from common.common import GetEnvVariableOrError 30from common.common import GetJackClassPath 31from common.common import RetCode 32from common.common import RunCommand 33 34 35# 36# Tester class. 37# 38 39 40class DexFuzzTester(object): 41 """Tester that feeds JFuzz programs into DexFuzz testing.""" 42 43 def __init__(self, num_tests, num_inputs, device): 44 """Constructor for the tester. 45 46 Args: 47 num_tests: int, number of tests to run 48 num_inputs: int, number of JFuzz programs to generate 49 device: string, target device serial number (or None) 50 """ 51 self._num_tests = num_tests 52 self._num_inputs = num_inputs 53 self._device = device 54 self._save_dir = None 55 self._results_dir = None 56 self._dexfuzz_dir = None 57 self._inputs_dir = None 58 self._dexfuzz_env = None 59 60 def __enter__(self): 61 """On entry, enters new temp directory after saving current directory. 62 63 Raises: 64 FatalError: error when temp directory cannot be constructed 65 """ 66 self._save_dir = os.getcwd() 67 self._results_dir = mkdtemp(dir='/tmp/') 68 self._dexfuzz_dir = mkdtemp(dir=self._results_dir) 69 self._inputs_dir = mkdtemp(dir=self._dexfuzz_dir) 70 if self._results_dir is None or self._dexfuzz_dir is None or \ 71 self._inputs_dir is None: 72 raise FatalError('Cannot obtain temp directory') 73 self._dexfuzz_env = os.environ.copy() 74 self._dexfuzz_env['ANDROID_DATA'] = self._dexfuzz_dir 75 top = GetEnvVariableOrError('ANDROID_BUILD_TOP') 76 self._dexfuzz_env['PATH'] = (top + '/art/tools/bisection_search:' + 77 self._dexfuzz_env['PATH']) 78 android_root = GetEnvVariableOrError('ANDROID_HOST_OUT') 79 self._dexfuzz_env['ANDROID_ROOT'] = android_root 80 self._dexfuzz_env['LD_LIBRARY_PATH'] = android_root + '/lib' 81 os.chdir(self._dexfuzz_dir) 82 os.mkdir('divergent_programs') 83 os.mkdir('bisection_outputs') 84 return self 85 86 def __exit__(self, etype, evalue, etraceback): 87 """On exit, re-enters previously saved current directory and cleans up.""" 88 os.chdir(self._save_dir) 89 # TODO: detect divergences or shutil.rmtree(self._results_dir) 90 91 def Run(self): 92 """Feeds JFuzz programs into DexFuzz testing.""" 93 print() 94 print('**\n**** JFuzz Testing\n**') 95 print() 96 print('#Tests :', self._num_tests) 97 print('Device :', self._device) 98 print('Directory :', self._results_dir) 99 print() 100 self.GenerateJFuzzPrograms() 101 self.RunDexFuzz() 102 103 104 def GenerateJFuzzPrograms(self): 105 """Generates JFuzz programs. 106 107 Raises: 108 FatalError: error when generation fails 109 """ 110 os.chdir(self._inputs_dir) 111 for i in range(1, self._num_inputs + 1): 112 jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java'] 113 if RunCommand(['jfuzz'], out='Test.java', err=None) != RetCode.SUCCESS: 114 raise FatalError('Unexpected error while running JFuzz') 115 if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt', 116 timeout=30) != RetCode.SUCCESS: 117 raise FatalError('Unexpected error while running Jack') 118 shutil.move('Test.java', '../Test' + str(i) + '.java') 119 shutil.move('classes.dex', 'classes' + str(i) + '.dex') 120 os.unlink('jackerr.txt') 121 122 def RunDexFuzz(self): 123 """Starts the DexFuzz testing.""" 124 os.chdir(self._dexfuzz_dir) 125 dexfuzz_args = ['--inputs=' + self._inputs_dir, 126 '--execute', 127 '--execute-class=Test', 128 '--repeat=' + str(self._num_tests), 129 '--dump-output', '--dump-verify', 130 '--interpreter', '--optimizing', 131 '--bisection-search'] 132 if self._device is not None: 133 dexfuzz_args += ['--device=' + self._device, '--allarm'] 134 else: 135 dexfuzz_args += ['--host'] # Assume host otherwise. 136 cmd = ['dexfuzz'] + dexfuzz_args 137 print('**** Running ****\n\n', cmd, '\n') 138 call(cmd, env=self._dexfuzz_env) 139 print('\n**** Results (report.log) ****\n') 140 call(['tail', '-n 24', 'report.log']) 141 142 143def main(): 144 # Handle arguments. 145 parser = argparse.ArgumentParser() 146 parser.add_argument('--num_tests', default=1000, 147 type=int, help='number of tests to run') 148 parser.add_argument('--num_inputs', default=10, 149 type=int, help='number of JFuzz program to generate') 150 parser.add_argument('--device', help='target device serial number') 151 args = parser.parse_args() 152 # Run the DexFuzz tester. 153 with DexFuzzTester(args.num_tests, args.num_inputs, args.device) as fuzzer: 154 fuzzer.Run() 155 156if __name__ == '__main__': 157 main() 158