Package FuzzManager :: Package FTB :: Package Running :: Module AutoRunner
[hide private]
[frames] | no frames]

Source Code for Module FuzzManager.FTB.Running.AutoRunner

  1  #!/usr/bin/env python 
  2  # encoding: utf-8 
  3  """ 
  4  AutoRunner -- Determine the correct runner class (GDB, ASan, etc) for 
  5                the given program, instantiate and return it. 
  6   
  7  @author:     Christian Holler (:decoder) 
  8   
  9  @license: 
 10   
 11  This Source Code Form is subject to the terms of the Mozilla Public 
 12  License, v. 2.0. If a copy of the MPL was not distributed with this 
 13  file, You can obtain one at http://mozilla.org/MPL/2.0/. 
 14   
 15  @contact:    choller@mozilla.com 
 16  """ 
 17   
 18  # Ensure print() compatibility with Python 3 
 19  from __future__ import print_function 
 20   
 21  import subprocess 
 22   
 23  from abc import ABCMeta 
 24  from distutils import spawn 
 25  from FTB.Signatures.CrashInfo import CrashInfo 
 26  import os 
 27  import re 
28 29 30 -class AutoRunner():
31 """ 32 Abstract base class that provides a method to instantiate the right sub class 33 for running the given program and obtaining crash information. 34 """ 35 __metaclass__ = ABCMeta 36
37 - def __init__(self, binary, args=None, env=None, cwd=None, stdin=None):
38 self.binary = binary 39 self.cwd = cwd 40 self.stdin = stdin 41 42 if self.stdin and isinstance(self.stdin, list): 43 self.stdin = "\n".join(self.stdin) 44 45 # Certain debuggers like GDB can run into problems when certain 46 # environment variables are missing. Hence we copy the system environment 47 # variables by default and overwrite them if they are specified through env. 48 self.env = dict(os.environ) 49 if env: 50 for envkey in env: 51 self.env[envkey] = env[envkey] 52 53 self.args = args 54 if self.args is None: 55 self.args = [] 56 57 assert isinstance(self.env, dict) 58 assert isinstance(self.args, list) 59 60 # The command that we will run for obtaining crash information 61 self.cmdArgs = [] 62 63 # These will hold our results from running 64 self.stdout = None 65 self.stderr = None 66 self.auxCrashData = None
67 68
69 - def getCrashInfo(self, configuration):
70 if not self.auxCrashData: 71 return None 72 73 return CrashInfo.fromRawCrashData(self.stdout, self.stderr, configuration, self.auxCrashData)
74 75 76 @staticmethod
77 - def fromBinaryArgs(binary, args=None, env=None, cwd=None, stdin=None):
78 process = subprocess.Popen( 79 ["nm", "-g", binary], 80 stdin=subprocess.PIPE, 81 stdout=subprocess.PIPE, 82 stderr=subprocess.PIPE, 83 cwd=cwd, 84 env=env 85 ) 86 87 (stdout, _) = process.communicate() 88 89 if stdout.find(" __asan_init") >= 0 or stdout.find("__ubsan_default_options") >= 0: 90 return ASanRunner(binary, args, env, cwd, stdin) 91 92 return GDBRunner(binary, args, env, cwd, stdin)
93
94 95 -class GDBRunner(AutoRunner):
96 - def __init__(self, binary, args=None, env=None, cwd=None, core=None, stdin=None):
97 AutoRunner.__init__(self, binary, args, env, cwd, stdin) 98 99 classPath = os.path.join(os.path.dirname(os.path.abspath(__file__)), "GDB.py") 100 self.gdbArgs = [ 101 "--batch", 102 "-ex", 103 "source %s" % classPath, 104 ] 105 106 if core is None: 107 self.gdbArgs.extend(["-ex", "run"]) 108 109 self.gdbArgs.extend([ 110 "-ex", "set pagination 0", 111 "-ex", "set backtrace limit 128", 112 "-ex", "bt", 113 "-ex", "python printImportantRegisters()", 114 "-ex", "x/2i $pc", 115 "-ex", "quit", 116 ]) 117 118 if core is None: 119 self.gdbArgs.append("--args") 120 121 self.cmdArgs.append("gdb") 122 self.cmdArgs.extend(self.gdbArgs) 123 self.cmdArgs.append(self.binary) 124 125 if core is not None: 126 self.cmdArgs.append(core) 127 else: 128 self.cmdArgs.extend(self.args)
129 130
131 - def run(self):
132 process = subprocess.Popen( 133 self.cmdArgs, 134 stdin=subprocess.PIPE, 135 stdout=subprocess.PIPE, 136 stderr=subprocess.PIPE, 137 cwd=self.cwd, 138 env=self.env 139 ) 140 141 (self.stdout, self.stderr) = process.communicate(input=self.stdin) 142 143 # Detect where the GDB trace starts/ends 144 traceStart = self.stdout.rfind("Program received signal") 145 traceStop = self.stdout.rfind("A debugging session is active") 146 147 # Alternative GDB start version when using core dumps 148 if traceStart < 0: 149 traceStart = self.stdout.rfind("Program terminated with signal") 150 151 if traceStart < 0: 152 return False 153 154 if traceStop < 0: 155 traceStop = len(self.stdout) 156 157 # Move the trace from stdout to auxCrashData 158 self.auxCrashData = self.stdout[traceStart:traceStop] 159 self.stdout = self.stdout[:traceStart] + self.stdout[traceStop:] 160 161 return True
162
163 164 -class ASanRunner(AutoRunner):
165 - def __init__(self, binary, args=None, env=None, cwd=None, stdin=None):
166 AutoRunner.__init__(self, binary, args, env, cwd, stdin) 167 168 self.cmdArgs.append(self.binary) 169 self.cmdArgs.extend(self.args) 170 171 if not "ASAN_SYMBOLIZER_PATH" in self.env: 172 if "ASAN_SYMBOLIZER_PATH" in os.environ: 173 self.env["ASAN_SYMBOLIZER_PATH"] = os.environ["ASAN_SYMBOLIZER_PATH"] 174 else: 175 self.env["ASAN_SYMBOLIZER_PATH"] = os.path.join(os.path.dirname(binary), "llvm-symbolizer") 176 if not os.path.isfile(self.env["ASAN_SYMBOLIZER_PATH"]): 177 self.env["ASAN_SYMBOLIZER_PATH"] = spawn.find_executable("llvm-symbolizer") 178 if not self.env["ASAN_SYMBOLIZER_PATH"]: 179 raise RuntimeError("Unable to locate llvm-symbolizer") 180 181 if not os.path.isfile(self.env["ASAN_SYMBOLIZER_PATH"]): 182 raise RuntimeError( 183 "Misconfigured ASAN_SYMBOLIZER_PATH: %s" % self.env["ASAN_SYMBOLIZER_PATH"] 184 ) 185 186 if not "UBSAN_OPTIONS" in self.env: 187 if "UBSAN_OPTIONS" in os.environ: 188 self.env["UBSAN_OPTIONS"] = os.environ["UBSAN_OPTIONS"] 189 else: 190 # Default to print stacktraces if no other options are set. If the caller overrides 191 # these options, they need to set this themselves, otherwise this code won't be able 192 # to isolate a UBSan trace. 193 self.env["UBSAN_OPTIONS"] = "print_stacktrace=1"
194
195 - def run(self):
196 process = subprocess.Popen( 197 self.cmdArgs, 198 stdin=subprocess.PIPE, 199 stdout=subprocess.PIPE, 200 stderr=subprocess.PIPE, 201 cwd=self.cwd, 202 env=self.env 203 ) 204 205 (self.stdout, stderr) = process.communicate(input=self.stdin) 206 207 inASanTrace = False 208 inUBSanTrace = False 209 self.auxCrashData = [] 210 self.stderr = [] 211 for line in stderr.splitlines(): 212 if inASanTrace or inUBSanTrace: 213 self.auxCrashData.append(line) 214 if inASanTrace and line.find("==ABORTING") >= 0: 215 inASanTrace = False 216 elif inUBSanTrace and "==SUMMARY: AddressSanitizer: undefined-behavior" in line: 217 inUBSanTrace = False 218 elif line.find("==ERROR: AddressSanitizer") >= 0: 219 self.auxCrashData.append(line) 220 inASanTrace = True 221 elif "runtime error" in line and re.search(":\\d+:\\d+: runtime error: ", line): 222 self.auxCrashData.append(line) 223 inUBSanTrace = True 224 else: 225 self.stderr.append(line) 226 227 if not self.auxCrashData: 228 return False 229 230 # Move the trace from stdout to auxCrashData 231 self.auxCrashData = os.linesep.join(self.auxCrashData) 232 self.stderr = os.linesep.join(self.stderr) 233 234 return True
235