• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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**** J/DexFuzz 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        print('Unexpected error while running JFuzz')
115        raise FatalError('Unexpected error while running JFuzz')
116      if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt',
117                    timeout=30) != RetCode.SUCCESS:
118        print('Unexpected error while running Jack')
119        raise FatalError('Unexpected error while running Jack')
120      shutil.move('Test.java', '../Test' + str(i) + '.java')
121      shutil.move('classes.dex', 'classes' + str(i) + '.dex')
122    os.unlink('jackerr.txt')
123
124  def RunDexFuzz(self):
125    """Starts the DexFuzz testing."""
126    os.chdir(self._dexfuzz_dir)
127    dexfuzz_args = ['--inputs=' + self._inputs_dir,
128                    '--execute',
129                    '--execute-class=Test',
130                    '--repeat=' + str(self._num_tests),
131                    '--quiet',
132                    '--dump-output',
133                    '--dump-verify',
134                    '--interpreter',
135                    '--optimizing',
136                    '--bisection-search']
137    if self._device is not None:
138      dexfuzz_args += ['--device=' + self._device, '--allarm']
139    else:
140      dexfuzz_args += ['--host']  # Assume host otherwise.
141    cmd = ['dexfuzz'] + dexfuzz_args
142    print('**** Running ****\n\n', cmd, '\n')
143    call(cmd, env=self._dexfuzz_env)
144    print('\n**** Results (report.log) ****\n')
145    call(['tail', '-n 24', 'report.log'])
146
147
148def main():
149  # Handle arguments.
150  parser = argparse.ArgumentParser()
151  parser.add_argument('--num_tests', default=1000,
152                      type=int, help='number of tests to run')
153  parser.add_argument('--num_inputs', default=10,
154                      type=int, help='number of JFuzz program to generate')
155  parser.add_argument('--device', help='target device serial number')
156  args = parser.parse_args()
157  # Run the DexFuzz tester.
158  with DexFuzzTester(args.num_tests, args.num_inputs, args.device) as fuzzer:
159    fuzzer.Run()
160
161if __name__ == '__main__':
162  main()
163