Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(145)

Side by Side Diff: fuzzer/go/frontend/data/result.go

Issue 1668543004: Add AddressSanitizer to fuzzer analysis (Closed) Base URL: https://skia.googlesource.com/buildbot@remove-old-tests
Patch Set: add multi threaded delete Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 package data 1 package data
2 2
3 import ( 3 import (
4 "fmt" 4 "fmt"
5 "regexp"
6 "sort"
5 "strings" 7 "strings"
8
9 "go.skia.org/infra/fuzzer/go/common"
6 ) 10 )
7 11
8 // Represents the metadata about a crash, hopefully easing debugging. 12 // Represents the metadata about a crash, hopefully easing debugging.
9 type FuzzResult struct { 13 type FuzzResult struct {
10 » DebugStackTrace StackTrace 14 » Debug BuildData
11 » ReleaseStackTrace StackTrace 15 » Release BuildData
12 » DebugDump string
13 » ReleaseDump string
14 » DebugStdErr string
15 » ReleaseStdErr string
16 » Flags FuzzFlag
17 } 16 }
18 17
19 // A bit mask representing what happened when a fuzz ran against a debug version of Skia and a release version 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.
20 type FuzzFlag int 41 type FuzzFlag int
21 42
22 const ( 43 const (
23 » DebugCrashed FuzzFlag = 1 << iota 44 » TerminatedGracefully FuzzFlag = 1 << iota
24 » ReleaseCrashed 45 » ClangCrashed
25 » DebugFailedGracefully 46 » ASANCrashed
26 » ReleaseFailedGracefully 47 » AssertionViolated
27 » DebugAssertionViolated 48 » BadAlloc
28 » DebugBadAlloc 49 » NoStackTrace
29 » ReleaseBadAlloc 50 » SKAbortHit
30 » DebugTimedOut 51 » TimedOut
31 » ReleaseTimedOut 52 » Other
32 » DebugNoStackTrace 53
33 » ReleaseNoStackTrace 54 » ASAN_GlobalBufferOverflow
34 » DebugOther 55 » ASAN_HeapBufferOverflow
35 » ReleaseOther 56 » ASAN_StackBufferOverflow
57 » ASAN_HeapUseAfterFree
58
59 » SKPICTURE_DuringRendering
36 ) 60 )
37 61
38 var flagNames = []string{ 62 var flagNames = []string{
39 » "DebugCrashed", 63 » "FailedGracefully",
40 » "ReleaseCrashed", 64 » "ClangCrashed",
41 » "DebugFailedGracefully", 65 » "ASANCrashed",
42 » "ReleaseFailedGracefully", 66 » "AssertionViolated",
43 » "DebugAssertionViolated", 67 » "BadAlloc",
44 » "DebugBadAlloc", 68 » "NoStackTrace",
45 » "ReleaseBadAlloc", 69 » "SKAbortHit",
46 » "DebugTimedOut", 70 » "TimedOut",
47 » "ReleaseTimedOut", 71 » "Other",
48 » "DebugNoStackTrace", 72
49 » "ReleaseNoStackTrace", 73 » "ASAN_global-buffer-overflow",
50 » "DebugOther", 74 » "ASAN_heap-buffer-overflow",
51 » "ReleaseOther", 75 » "ASAN_stack-buffer-overflow",
76 » "ASAN_heap-use-after-free",
77
78 » "SKPICTURE_DuringRendering",
52 } 79 }
53 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().
54 func (f FuzzFlag) ToHumanReadableFlags() []string { 83 func (f FuzzFlag) ToHumanReadableFlags() []string {
55 flags := make([]string, 0) 84 flags := make([]string, 0)
56 i := 0 85 i := 0
57 for mask := 1; mask < (2 << 16); mask *= 2 { 86 for mask := 1; mask < (2 << 16); mask *= 2 {
58 if int(f)&mask != 0 { 87 if int(f)&mask != 0 {
59 flags = append(flags, flagNames[i]) 88 flags = append(flags, flagNames[i])
60 } 89 }
61 i++ 90 i++
62 } 91 }
92 // Front end filtering logic will expect the flags to be in alphabetical order.
93 sort.Strings(flags)
63 return flags 94 return flags
64 } 95 }
65 96
66 func (f FuzzFlag) String() string { 97 func (f FuzzFlag) String() string {
67 return fmt.Sprintf("FuzzFlag: %016b (%d) [%s]", f, f, strings.Join(f.ToH umanReadableFlags(), " | ")) 98 return fmt.Sprintf("FuzzFlag: %016b (%d) [%s]", f, f, strings.Join(f.ToH umanReadableFlags(), " | "))
68 } 99 }
69 100
70 func ParseFuzzResult(debugDump, debugErr, releaseDump, releaseErr string) FuzzRe sult { 101 // ParseGCSPackage parses the results of analysis of a fuzz and creates a FuzzRe sult with it.
71 » result := FuzzResult{ 102 // This includes parsing the stacktraces and computing the flags about the fuzz.
72 » » DebugDump: debugDump, 103 func ParseGCSPackage(g GCSPackage) FuzzResult {
73 » » DebugStackTrace: ParseStackTrace(debugDump), 104 » result := FuzzResult{}
74 » » DebugStdErr: debugErr, 105 » result.Debug.Asan = g.Debug.Asan
75 » » ReleaseDump: releaseDump, 106 » result.Debug.Dump = g.Debug.Dump
76 » » ReleaseStackTrace: ParseStackTrace(releaseDump), 107 » result.Debug.StdErr = g.Debug.StdErr
77 » » ReleaseStdErr: releaseErr, 108 » result.Debug.StackTrace = getStackTrace(g.Debug.Asan, g.Debug.Dump)
78 » » Flags: 0, //dummy value, to be updated shortly 109 » result.Release.Asan = g.Release.Asan
79 » } 110 » result.Release.Dump = g.Release.Dump
80 » result.computeFlags() 111 » result.Release.StdErr = g.Release.StdErr
112 » result.Release.StackTrace = getStackTrace(g.Release.Asan, g.Release.Dump )
113 » result.computeFlags(g.FuzzCategory)
81 114
82 return result 115 return result
83 } 116 }
84 117
85 func (r *FuzzResult) computeFlags() { 118 // getStackTrace creates a StackTrace output from one of the two dumps given. I t first tries to
86 » flags := FuzzFlag(0) 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 }
87 126
88 » if r.DebugDump != "" { 127 // computeFlags parses the raw data to set both the Debug and Release flags.
89 » » flags |= DebugCrashed 128 func (r *FuzzResult) computeFlags(category string) {
90 » » if r.DebugStackTrace.IsEmpty() { 129 » r.Debug.Flags = parseAll(category, &r.Debug)
91 » » » flags |= DebugNoStackTrace 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)
92 } 143 }
93 } 144 }
94 145 » if strings.Contains(data.StdErr, "fatal error") {
95 » if r.ReleaseDump != "" { 146 » » f |= ClangCrashed
96 » » flags |= ReleaseCrashed 147 » » f |= SKAbortHit
97 » » if r.ReleaseStackTrace.IsEmpty() { 148 » » if data.StackTrace.IsEmpty() {
98 » » » flags |= ReleaseNoStackTrace 149 » » » data.StackTrace = extractSkAbortTrace(data.StdErr)
99 } 150 }
100 } 151 }
101 152 » // If no sk abort message and no evidence of crashes, we either terminat ed gracefully or
102 » if r.DebugStdErr == "" && r.DebugDump == "" { 153 » // timed out.
103 » » flags |= DebugTimedOut 154 » if f == 0 && !asanCrashed(data.Asan) && !clangDumped(data.Dump) {
104 » } else if strings.Contains(r.DebugStdErr, "failed assertion") { 155 » » if strings.Contains(data.Asan, "[terminated]") && strings.Contai ns(data.StdErr, "[terminated]") {
105 » » flags |= DebugAssertionViolated 156 » » » return TerminatedGracefully
106 » } else if strings.Contains(r.DebugStdErr, `terminate called after throwi ng an instance of 'std::bad_alloc'`) { 157 » » }
107 » » flags |= DebugCrashed | DebugBadAlloc 158 » » return TimedOut
108 » } else if strings.Contains(r.DebugStdErr, `Success`) {
109 » » flags |= DebugFailedGracefully
110 » } else if r.DebugStdErr != "" {
111 » » flags |= DebugOther
112 } 159 }
113 160
114 » if r.ReleaseStdErr == "" && r.ReleaseDump == "" { 161 » // Look for clues from the various dumps.
115 » » flags |= ReleaseTimedOut 162 » f |= parseAsan(category, data.Asan)
116 » } else if strings.Contains(r.ReleaseStdErr, `terminate called after thro wing an instance of 'std::bad_alloc'`) { 163 » f |= parseCatchsegv(category, data.Dump, data.StdErr)
117 » » flags |= ReleaseCrashed | ReleaseBadAlloc 164 » if f == 0 {
118 » } else if strings.Contains(r.ReleaseStdErr, `Success`) { 165 » » // I don't know what this means (yet).
119 » » flags |= ReleaseFailedGracefully 166 » » return Other
120 » } else if r.ReleaseStdErr != "" { 167 » }
121 » » flags |= ReleaseOther 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
122 } 199 }
123 200
124 » r.Flags = flags 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
125 } 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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698