1
2
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
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
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
46
47
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
61 self.cmdArgs = []
62
63
64 self.stdout = None
65 self.stderr = None
66 self.auxCrashData = None
67
68
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
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
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
144 traceStart = self.stdout.rfind("Program received signal")
145 traceStop = self.stdout.rfind("A debugging session is active")
146
147
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
158 self.auxCrashData = self.stdout[traceStart:traceStop]
159 self.stdout = self.stdout[:traceStart] + self.stdout[traceStop:]
160
161 return True
162
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
191
192
193 self.env["UBSAN_OPTIONS"] = "print_stacktrace=1"
194
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
231 self.auxCrashData = os.linesep.join(self.auxCrashData)
232 self.stderr = os.linesep.join(self.stderr)
233
234 return True
235