1*795d594fSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*795d594fSAndroid Build Coastguard Worker# 3*795d594fSAndroid Build Coastguard Worker# Copyright (C) 2016 The Android Open Source Project 4*795d594fSAndroid Build Coastguard Worker# 5*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*795d594fSAndroid Build Coastguard Worker# 9*795d594fSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*795d594fSAndroid Build Coastguard Worker# 11*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*795d594fSAndroid Build Coastguard Worker# limitations under the License. 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Workerimport argparse 18*795d594fSAndroid Build Coastguard Workerimport os 19*795d594fSAndroid Build Coastguard Workerimport shutil 20*795d594fSAndroid Build Coastguard Workerimport sys 21*795d594fSAndroid Build Coastguard Worker 22*795d594fSAndroid Build Coastguard Workerfrom glob import glob 23*795d594fSAndroid Build Coastguard Workerfrom subprocess import call 24*795d594fSAndroid Build Coastguard Workerfrom tempfile import mkdtemp 25*795d594fSAndroid Build Coastguard Worker 26*795d594fSAndroid Build Coastguard Workersys.path.append(os.path.dirname(os.path.dirname( 27*795d594fSAndroid Build Coastguard Worker os.path.realpath(__file__)))) 28*795d594fSAndroid Build Coastguard Worker 29*795d594fSAndroid Build Coastguard Workerfrom common.common import FatalError 30*795d594fSAndroid Build Coastguard Workerfrom common.common import GetEnvVariableOrError 31*795d594fSAndroid Build Coastguard Workerfrom common.common import RetCode 32*795d594fSAndroid Build Coastguard Workerfrom common.common import RunCommand 33*795d594fSAndroid Build Coastguard Worker 34*795d594fSAndroid Build Coastguard Worker 35*795d594fSAndroid Build Coastguard Worker# 36*795d594fSAndroid Build Coastguard Worker# Tester class. 37*795d594fSAndroid Build Coastguard Worker# 38*795d594fSAndroid Build Coastguard Worker 39*795d594fSAndroid Build Coastguard Worker 40*795d594fSAndroid Build Coastguard Workerclass DexFuzzTester(object): 41*795d594fSAndroid Build Coastguard Worker """Tester that feeds JFuzz programs into DexFuzz testing.""" 42*795d594fSAndroid Build Coastguard Worker 43*795d594fSAndroid Build Coastguard Worker def __init__(self, num_tests, num_inputs, device, dexer, debug_info): 44*795d594fSAndroid Build Coastguard Worker """Constructor for the tester. 45*795d594fSAndroid Build Coastguard Worker 46*795d594fSAndroid Build Coastguard Worker Args: 47*795d594fSAndroid Build Coastguard Worker num_tests: int, number of tests to run 48*795d594fSAndroid Build Coastguard Worker num_inputs: int, number of JFuzz programs to generate 49*795d594fSAndroid Build Coastguard Worker device: string, target device serial number (or None) 50*795d594fSAndroid Build Coastguard Worker dexer: string, defines dexer 51*795d594fSAndroid Build Coastguard Worker debug_info: boolean, if True include debugging info 52*795d594fSAndroid Build Coastguard Worker """ 53*795d594fSAndroid Build Coastguard Worker self._num_tests = num_tests 54*795d594fSAndroid Build Coastguard Worker self._num_inputs = num_inputs 55*795d594fSAndroid Build Coastguard Worker self._device = device 56*795d594fSAndroid Build Coastguard Worker self._save_dir = None 57*795d594fSAndroid Build Coastguard Worker self._results_dir = None 58*795d594fSAndroid Build Coastguard Worker self._dexfuzz_dir = None 59*795d594fSAndroid Build Coastguard Worker self._inputs_dir = None 60*795d594fSAndroid Build Coastguard Worker self._dexfuzz_env = None 61*795d594fSAndroid Build Coastguard Worker self._dexer = dexer 62*795d594fSAndroid Build Coastguard Worker self._debug_info = debug_info 63*795d594fSAndroid Build Coastguard Worker 64*795d594fSAndroid Build Coastguard Worker def __enter__(self): 65*795d594fSAndroid Build Coastguard Worker """On entry, enters new temp directory after saving current directory. 66*795d594fSAndroid Build Coastguard Worker 67*795d594fSAndroid Build Coastguard Worker Raises: 68*795d594fSAndroid Build Coastguard Worker FatalError: error when temp directory cannot be constructed 69*795d594fSAndroid Build Coastguard Worker """ 70*795d594fSAndroid Build Coastguard Worker self._save_dir = os.getcwd() 71*795d594fSAndroid Build Coastguard Worker self._results_dir = mkdtemp(dir='/tmp/') 72*795d594fSAndroid Build Coastguard Worker self._dexfuzz_dir = mkdtemp(dir=self._results_dir) 73*795d594fSAndroid Build Coastguard Worker self._inputs_dir = mkdtemp(dir=self._dexfuzz_dir) 74*795d594fSAndroid Build Coastguard Worker if self._results_dir is None or self._dexfuzz_dir is None or \ 75*795d594fSAndroid Build Coastguard Worker self._inputs_dir is None: 76*795d594fSAndroid Build Coastguard Worker raise FatalError('Cannot obtain temp directory') 77*795d594fSAndroid Build Coastguard Worker self._dexfuzz_env = os.environ.copy() 78*795d594fSAndroid Build Coastguard Worker self._dexfuzz_env['ANDROID_DATA'] = self._dexfuzz_dir 79*795d594fSAndroid Build Coastguard Worker top = GetEnvVariableOrError('ANDROID_BUILD_TOP') 80*795d594fSAndroid Build Coastguard Worker self._dexfuzz_env['PATH'] = (top + '/art/tools/bisection_search:' + 81*795d594fSAndroid Build Coastguard Worker self._dexfuzz_env['PATH']) 82*795d594fSAndroid Build Coastguard Worker android_root = GetEnvVariableOrError('ANDROID_HOST_OUT') 83*795d594fSAndroid Build Coastguard Worker self._dexfuzz_env['ANDROID_ROOT'] = android_root 84*795d594fSAndroid Build Coastguard Worker self._dexfuzz_env['LD_LIBRARY_PATH'] = android_root + '/lib' 85*795d594fSAndroid Build Coastguard Worker os.chdir(self._dexfuzz_dir) 86*795d594fSAndroid Build Coastguard Worker os.mkdir('divergent_programs') 87*795d594fSAndroid Build Coastguard Worker os.mkdir('bisection_outputs') 88*795d594fSAndroid Build Coastguard Worker return self 89*795d594fSAndroid Build Coastguard Worker 90*795d594fSAndroid Build Coastguard Worker def __exit__(self, etype, evalue, etraceback): 91*795d594fSAndroid Build Coastguard Worker """On exit, re-enters previously saved current directory and cleans up.""" 92*795d594fSAndroid Build Coastguard Worker os.chdir(self._save_dir) 93*795d594fSAndroid Build Coastguard Worker # TODO: detect divergences or shutil.rmtree(self._results_dir) 94*795d594fSAndroid Build Coastguard Worker 95*795d594fSAndroid Build Coastguard Worker def Run(self): 96*795d594fSAndroid Build Coastguard Worker """Feeds JFuzz programs into DexFuzz testing.""" 97*795d594fSAndroid Build Coastguard Worker print() 98*795d594fSAndroid Build Coastguard Worker print('**\n**** J/DexFuzz Testing\n**') 99*795d594fSAndroid Build Coastguard Worker print() 100*795d594fSAndroid Build Coastguard Worker print('#Tests :', self._num_tests) 101*795d594fSAndroid Build Coastguard Worker print('Device :', self._device) 102*795d594fSAndroid Build Coastguard Worker print('Directory :', self._results_dir) 103*795d594fSAndroid Build Coastguard Worker print() 104*795d594fSAndroid Build Coastguard Worker self.GenerateJFuzzPrograms() 105*795d594fSAndroid Build Coastguard Worker self.RunDexFuzz() 106*795d594fSAndroid Build Coastguard Worker 107*795d594fSAndroid Build Coastguard Worker def CompileOnHost(self): 108*795d594fSAndroid Build Coastguard Worker """Compiles Test.java into classes.dex using either javac/dx or d8. 109*795d594fSAndroid Build Coastguard Worker 110*795d594fSAndroid Build Coastguard Worker Raises: 111*795d594fSAndroid Build Coastguard Worker FatalError: error when compilation fails 112*795d594fSAndroid Build Coastguard Worker """ 113*795d594fSAndroid Build Coastguard Worker if self._dexer == 'dx' or self._dexer == 'd8': 114*795d594fSAndroid Build Coastguard Worker dbg = '-g' if self._debug_info else '-g:none' 115*795d594fSAndroid Build Coastguard Worker if RunCommand(['javac', '--release=8', dbg, 'Test.java'], 116*795d594fSAndroid Build Coastguard Worker out=None, err='jerr.txt', timeout=30) != RetCode.SUCCESS: 117*795d594fSAndroid Build Coastguard Worker print('Unexpected error while running javac') 118*795d594fSAndroid Build Coastguard Worker raise FatalError('Unexpected error while running javac') 119*795d594fSAndroid Build Coastguard Worker cfiles = glob('*.class') 120*795d594fSAndroid Build Coastguard Worker dx = 'dx' if self._dexer == 'dx' else 'd8-compat-dx' 121*795d594fSAndroid Build Coastguard Worker if RunCommand([dx, '--dex', '--output=classes.dex'] + cfiles, 122*795d594fSAndroid Build Coastguard Worker out=None, err='dxerr.txt', timeout=30) != RetCode.SUCCESS: 123*795d594fSAndroid Build Coastguard Worker print('Unexpected error while running dx') 124*795d594fSAndroid Build Coastguard Worker raise FatalError('Unexpected error while running dx') 125*795d594fSAndroid Build Coastguard Worker # Cleanup on success (nothing to see). 126*795d594fSAndroid Build Coastguard Worker for cfile in cfiles: 127*795d594fSAndroid Build Coastguard Worker os.unlink(cfile) 128*795d594fSAndroid Build Coastguard Worker os.unlink('jerr.txt') 129*795d594fSAndroid Build Coastguard Worker os.unlink('dxerr.txt') 130*795d594fSAndroid Build Coastguard Worker else: 131*795d594fSAndroid Build Coastguard Worker raise FatalError('Unknown dexer: ' + self._dexer) 132*795d594fSAndroid Build Coastguard Worker 133*795d594fSAndroid Build Coastguard Worker def GenerateJFuzzPrograms(self): 134*795d594fSAndroid Build Coastguard Worker """Generates JFuzz programs. 135*795d594fSAndroid Build Coastguard Worker 136*795d594fSAndroid Build Coastguard Worker Raises: 137*795d594fSAndroid Build Coastguard Worker FatalError: error when generation fails 138*795d594fSAndroid Build Coastguard Worker """ 139*795d594fSAndroid Build Coastguard Worker os.chdir(self._inputs_dir) 140*795d594fSAndroid Build Coastguard Worker for i in range(1, self._num_inputs + 1): 141*795d594fSAndroid Build Coastguard Worker if RunCommand(['jfuzz'], out='Test.java', err=None) != RetCode.SUCCESS: 142*795d594fSAndroid Build Coastguard Worker print('Unexpected error while running JFuzz') 143*795d594fSAndroid Build Coastguard Worker raise FatalError('Unexpected error while running JFuzz') 144*795d594fSAndroid Build Coastguard Worker self.CompileOnHost() 145*795d594fSAndroid Build Coastguard Worker shutil.move('Test.java', '../Test' + str(i) + '.java') 146*795d594fSAndroid Build Coastguard Worker shutil.move('classes.dex', 'classes' + str(i) + '.dex') 147*795d594fSAndroid Build Coastguard Worker 148*795d594fSAndroid Build Coastguard Worker def RunDexFuzz(self): 149*795d594fSAndroid Build Coastguard Worker """Starts the DexFuzz testing.""" 150*795d594fSAndroid Build Coastguard Worker os.chdir(self._dexfuzz_dir) 151*795d594fSAndroid Build Coastguard Worker dexfuzz_args = ['--inputs=' + self._inputs_dir, 152*795d594fSAndroid Build Coastguard Worker '--execute', 153*795d594fSAndroid Build Coastguard Worker '--execute-class=Test', 154*795d594fSAndroid Build Coastguard Worker '--repeat=' + str(self._num_tests), 155*795d594fSAndroid Build Coastguard Worker '--quiet', 156*795d594fSAndroid Build Coastguard Worker '--dump-output', 157*795d594fSAndroid Build Coastguard Worker '--dump-verify', 158*795d594fSAndroid Build Coastguard Worker '--interpreter', 159*795d594fSAndroid Build Coastguard Worker '--optimizing', 160*795d594fSAndroid Build Coastguard Worker '--bisection-search'] 161*795d594fSAndroid Build Coastguard Worker if self._device is not None: 162*795d594fSAndroid Build Coastguard Worker dexfuzz_args += ['--device=' + self._device, '--allarm'] 163*795d594fSAndroid Build Coastguard Worker else: 164*795d594fSAndroid Build Coastguard Worker dexfuzz_args += ['--host'] # Assume host otherwise. 165*795d594fSAndroid Build Coastguard Worker cmd = ['dexfuzz'] + dexfuzz_args 166*795d594fSAndroid Build Coastguard Worker print('**** Running ****\n\n', cmd, '\n') 167*795d594fSAndroid Build Coastguard Worker call(cmd, env=self._dexfuzz_env) 168*795d594fSAndroid Build Coastguard Worker print('\n**** Results (report.log) ****\n') 169*795d594fSAndroid Build Coastguard Worker call(['tail', '-n 24', 'report.log']) 170*795d594fSAndroid Build Coastguard Worker 171*795d594fSAndroid Build Coastguard Worker 172*795d594fSAndroid Build Coastguard Workerdef main(): 173*795d594fSAndroid Build Coastguard Worker # Handle arguments. 174*795d594fSAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 175*795d594fSAndroid Build Coastguard Worker parser.add_argument('--num_tests', default=1000, type=int, 176*795d594fSAndroid Build Coastguard Worker help='number of tests to run (default: 1000)') 177*795d594fSAndroid Build Coastguard Worker parser.add_argument('--num_inputs', default=10, type=int, 178*795d594fSAndroid Build Coastguard Worker help='number of JFuzz program to generate (default: 10)') 179*795d594fSAndroid Build Coastguard Worker parser.add_argument('--device', help='target device serial number') 180*795d594fSAndroid Build Coastguard Worker parser.add_argument('--dexer', default='dx', type=str, 181*795d594fSAndroid Build Coastguard Worker help='defines dexer as dx or d8 (default: dx)') 182*795d594fSAndroid Build Coastguard Worker parser.add_argument('--debug_info', default=False, action='store_true', 183*795d594fSAndroid Build Coastguard Worker help='include debugging info') 184*795d594fSAndroid Build Coastguard Worker args = parser.parse_args() 185*795d594fSAndroid Build Coastguard Worker # Run the DexFuzz tester. 186*795d594fSAndroid Build Coastguard Worker with DexFuzzTester(args.num_tests, 187*795d594fSAndroid Build Coastguard Worker args.num_inputs, 188*795d594fSAndroid Build Coastguard Worker args.device, 189*795d594fSAndroid Build Coastguard Worker args.dexer, args.debug_info) as fuzzer: 190*795d594fSAndroid Build Coastguard Worker fuzzer.Run() 191*795d594fSAndroid Build Coastguard Worker 192*795d594fSAndroid Build Coastguard Workerif __name__ == '__main__': 193*795d594fSAndroid Build Coastguard Worker main() 194