xref: /aosp_15_r20/art/tools/jfuzz/run_dex_fuzz_test.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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