OLD | NEW |
| (Empty) |
1 package data | |
2 | |
3 import ( | |
4 "fmt" | |
5 "regexp" | |
6 "sort" | |
7 "strings" | |
8 | |
9 "go.skia.org/infra/fuzzer/go/common" | |
10 ) | |
11 | |
12 // Represents the metadata about a crash, hopefully easing debugging. | |
13 type FuzzResult struct { | |
14 Debug BuildData | |
15 Release BuildData | |
16 } | |
17 | |
18 // BuildData represents the results of parsing a given skia build's output. | |
19 type BuildData struct { | |
20 OutputFiles | |
21 StackTrace StackTrace | |
22 Flags FuzzFlag | |
23 } | |
24 | |
25 // OutputFiles are the files output by the analysis | |
26 type OutputFiles struct { | |
27 Asan string | |
28 Dump string | |
29 StdErr string | |
30 } | |
31 | |
32 // GCSPackage is a struct containing all the pieces of a fuzz that exist in Goog
le Storage. | |
33 type GCSPackage struct { | |
34 Name string | |
35 FuzzCategory string | |
36 Debug OutputFiles | |
37 Release OutputFiles | |
38 } | |
39 | |
40 // A bit mask representing what happened when a fuzz ran against Skia. | |
41 type FuzzFlag int | |
42 | |
43 const ( | |
44 TerminatedGracefully FuzzFlag = 1 << iota | |
45 ClangCrashed | |
46 ASANCrashed | |
47 AssertionViolated | |
48 BadAlloc | |
49 NoStackTrace | |
50 SKAbortHit | |
51 TimedOut | |
52 Other | |
53 | |
54 ASAN_GlobalBufferOverflow | |
55 ASAN_HeapBufferOverflow | |
56 ASAN_StackBufferOverflow | |
57 ASAN_HeapUseAfterFree | |
58 | |
59 SKPICTURE_DuringRendering | |
60 ) | |
61 | |
62 var flagNames = []string{ | |
63 "FailedGracefully", | |
64 "ClangCrashed", | |
65 "ASANCrashed", | |
66 "AssertionViolated", | |
67 "BadAlloc", | |
68 "NoStackTrace", | |
69 "SKAbortHit", | |
70 "TimedOut", | |
71 "Other", | |
72 | |
73 "ASAN_global-buffer-overflow", | |
74 "ASAN_heap-buffer-overflow", | |
75 "ASAN_stack-buffer-overflow", | |
76 "ASAN_heap-use-after-free", | |
77 | |
78 "SKPICTURE_DuringRendering", | |
79 } | |
80 | |
81 // ToHumanReadableFlags creates a sorted slice of strings that represents the fl
ags. The slice | |
82 // is sorted by unicode points, as per sort.Strings(). | |
83 func (f FuzzFlag) ToHumanReadableFlags() []string { | |
84 flags := make([]string, 0) | |
85 i := 0 | |
86 for mask := 1; mask < (2 << 16); mask *= 2 { | |
87 if int(f)&mask != 0 { | |
88 flags = append(flags, flagNames[i]) | |
89 } | |
90 i++ | |
91 } | |
92 // Front end filtering logic will expect the flags to be in alphabetical
order. | |
93 sort.Strings(flags) | |
94 return flags | |
95 } | |
96 | |
97 func (f FuzzFlag) String() string { | |
98 return fmt.Sprintf("FuzzFlag: %016b (%d) [%s]", f, f, strings.Join(f.ToH
umanReadableFlags(), " | ")) | |
99 } | |
100 | |
101 // ParseGCSPackage parses the results of analysis of a fuzz and creates a FuzzRe
sult with it. | |
102 // This includes parsing the stacktraces and computing the flags about the fuzz. | |
103 func ParseGCSPackage(g GCSPackage) FuzzResult { | |
104 result := FuzzResult{} | |
105 result.Debug.Asan = g.Debug.Asan | |
106 result.Debug.Dump = g.Debug.Dump | |
107 result.Debug.StdErr = g.Debug.StdErr | |
108 result.Debug.StackTrace = getStackTrace(g.Debug.Asan, g.Debug.Dump) | |
109 result.Release.Asan = g.Release.Asan | |
110 result.Release.Dump = g.Release.Dump | |
111 result.Release.StdErr = g.Release.StdErr | |
112 result.Release.StackTrace = getStackTrace(g.Release.Asan, g.Release.Dump
) | |
113 result.computeFlags(g.FuzzCategory) | |
114 | |
115 return result | |
116 } | |
117 | |
118 // getStackTrace creates a StackTrace output from one of the two dumps given. I
t first tries to | |
119 // use the AddressSanitizer dump, with the Clang dump as a fallback. | |
120 func getStackTrace(asan, dump string) StackTrace { | |
121 if asanCrashed(asan) { | |
122 return parseASANStackTrace(asan) | |
123 } | |
124 return parseCatchsegvStackTrace(dump) | |
125 } | |
126 | |
127 // computeFlags parses the raw data to set both the Debug and Release flags. | |
128 func (r *FuzzResult) computeFlags(category string) { | |
129 r.Debug.Flags = parseAll(category, &r.Debug) | |
130 r.Release.Flags = parseAll(category, &r.Release) | |
131 } | |
132 | |
133 // parseAll looks at the three input files and parses the results, based on the
category. The | |
134 // category allows for specialized flags, like SKPICTURE_DuringRendering. | |
135 func parseAll(category string, data *BuildData) FuzzFlag { | |
136 f := FuzzFlag(0) | |
137 // Check for SKAbort message | |
138 if strings.Contains(data.Asan, "fatal error") { | |
139 f |= ASANCrashed | |
140 f |= SKAbortHit | |
141 if data.StackTrace.IsEmpty() { | |
142 data.StackTrace = extractSkAbortTrace(data.StdErr) | |
143 } | |
144 } | |
145 if strings.Contains(data.StdErr, "fatal error") { | |
146 f |= ClangCrashed | |
147 f |= SKAbortHit | |
148 if data.StackTrace.IsEmpty() { | |
149 data.StackTrace = extractSkAbortTrace(data.StdErr) | |
150 } | |
151 } | |
152 // If no sk abort message and no evidence of crashes, we either terminat
ed gracefully or | |
153 // timed out. | |
154 if f == 0 && !asanCrashed(data.Asan) && !clangDumped(data.Dump) { | |
155 if strings.Contains(data.Asan, "[terminated]") && strings.Contai
ns(data.StdErr, "[terminated]") { | |
156 return TerminatedGracefully | |
157 } | |
158 return TimedOut | |
159 } | |
160 | |
161 // Look for clues from the various dumps. | |
162 f |= parseAsan(category, data.Asan) | |
163 f |= parseCatchsegv(category, data.Dump, data.StdErr) | |
164 if f == 0 { | |
165 // I don't know what this means (yet). | |
166 return Other | |
167 } | |
168 if data.StackTrace.IsEmpty() { | |
169 f |= NoStackTrace | |
170 } | |
171 return f | |
172 } | |
173 | |
174 // parseAsan returns the flags discovered while looking through the AddressSanit
izer output. This | |
175 // includes things like ASAN_GlobalBufferOverflow. | |
176 func parseAsan(category, asan string) FuzzFlag { | |
177 f := FuzzFlag(0) | |
178 if !asanCrashed(asan) { | |
179 return f | |
180 } | |
181 f |= ASANCrashed | |
182 if strings.Contains(asan, "failed assertion") { | |
183 f |= AssertionViolated | |
184 } | |
185 if strings.Contains(asan, "global-buffer-overflow") { | |
186 f |= ASAN_GlobalBufferOverflow | |
187 } | |
188 if strings.Contains(asan, "heap-buffer-overflow") { | |
189 f |= ASAN_HeapBufferOverflow | |
190 } | |
191 if strings.Contains(asan, "stack-buffer-overflow") { | |
192 f |= ASAN_StackBufferOverflow | |
193 } | |
194 if strings.Contains(asan, "heap-use-after-free") { | |
195 f |= ASAN_HeapUseAfterFree | |
196 } | |
197 if strings.Contains(asan, "AddressSanitizer failed to allocate") { | |
198 f |= BadAlloc | |
199 } | |
200 | |
201 // Split off the stderr that happened before the crash. | |
202 errs := strings.Split(asan, "=================") | |
203 if len(errs) > 0 { | |
204 err := errs[0] | |
205 if category == "skpicture" && strings.Contains(err, "Rendering")
{ | |
206 f |= SKPICTURE_DuringRendering | |
207 } | |
208 } | |
209 return f | |
210 } | |
211 | |
212 // asanCrashed returns true if the asan output is consistent with a crash. | |
213 func asanCrashed(asan string) bool { | |
214 return strings.Contains(asan, "ERROR: AddressSanitizer:") | |
215 } | |
216 | |
217 // parseAsan returns the flags discovered while looking through the Clang dump a
nd standard error. | |
218 // This includes things like | |
219 func parseCatchsegv(category, dump, err string) FuzzFlag { | |
220 f := FuzzFlag(0) | |
221 if !clangDumped(dump) && strings.Contains(err, "[terminated]") { | |
222 return f | |
223 } | |
224 f |= ClangCrashed | |
225 if strings.Contains(err, "failed assertion") { | |
226 f |= AssertionViolated | |
227 } | |
228 if category == "skpicture" && strings.Contains(err, "Rendering") { | |
229 f |= SKPICTURE_DuringRendering | |
230 } | |
231 if strings.Contains(err, "std::bad_alloc") { | |
232 f |= BadAlloc | |
233 } | |
234 return f | |
235 } | |
236 | |
237 // clangDumped returns true if the clang output is consistent with a crash, that
is, non empty. | |
238 func clangDumped(dump string) bool { | |
239 return len(dump) != 0 | |
240 } | |
241 | |
242 var skAbortStackTraceLine = regexp.MustCompile(`(?:\.\./)+(?P<package>(?:\w+/)+)
(?P<file>.+):(?P<line>\d+): fatal error`) | |
243 | |
244 // extractSkAbortTrace looks for the fatal error string indicative of the SKAbor
t termination | |
245 // and tries to pull out the stacktrace frame on which it happened. | |
246 func extractSkAbortTrace(err string) StackTrace { | |
247 st := StackTrace{} | |
248 if match := skAbortStackTraceLine.FindStringSubmatch(err); match != nil
{ | |
249 st.Frames = append(st.Frames, FullStackFrame(match[1], match[2],
common.UNKNOWN_FUNCTION, safeParseInt(match[3]))) | |
250 } | |
251 return st | |
252 } | |
OLD | NEW |