1 [!fuzz] skip
2
3 # Tests that a crash caused by a mutator-discovered input writes the bad input
4 # to testdata, and fails+reports correctly. This tests the end-to-end behavior
5 # of the mutator finding a crash while fuzzing, adding it as a regression test
6 # to the seed corpus in testdata, and failing the next time the test is run.
7
8 [short] skip
9
10 # Running the seed corpus for all of the targets should pass the first
11 # time, since nothing in the seed corpus will cause a crash.
12 go test
13
14 # Running the fuzzer should find a crashing input quickly.
15 ! go test -fuzz=FuzzWithBug -fuzztime=100x -fuzzminimizetime=1000x
16 stdout 'testdata[/\\]fuzz[/\\]FuzzWithBug[/\\]'
17 stdout 'this input caused a crash!'
18 go run check_testdata.go FuzzWithBug
19
20 # Now, the failing bytes should have been added to the seed corpus for
21 # the target, and should fail when run without fuzzing.
22 ! go test
23 stdout 'FuzzWithBug/[a-f0-9]{64}'
24 stdout 'this input caused a crash!'
25
26 ! go test -run=FuzzWithNilPanic -fuzz=FuzzWithNilPanic -fuzztime=100x -fuzzminimizetime=1000x
27 stdout 'testdata[/\\]fuzz[/\\]FuzzWithNilPanic[/\\]'
28 stdout 'runtime.Goexit'
29 go run check_testdata.go FuzzWithNilPanic
30
31 ! go test -run=FuzzWithGoexit -fuzz=FuzzWithGoexit -fuzztime=100x -fuzzminimizetime=1000x
32 stdout 'testdata[/\\]fuzz[/\\]FuzzWithGoexit[/\\]'
33 stdout 'runtime.Goexit'
34 go run check_testdata.go FuzzWithGoexit
35
36 ! go test -run=FuzzWithFail -fuzz=FuzzWithFail -fuzztime=100x -fuzzminimizetime=1000x
37 stdout 'testdata[/\\]fuzz[/\\]FuzzWithFail[/\\]'
38 go run check_testdata.go FuzzWithFail
39
40 ! go test -run=FuzzWithLogFail -fuzz=FuzzWithLogFail -fuzztime=100x -fuzzminimizetime=1000x
41 stdout 'testdata[/\\]fuzz[/\\]FuzzWithLogFail[/\\]'
42 stdout 'logged something'
43 go run check_testdata.go FuzzWithLogFail
44
45 ! go test -run=FuzzWithErrorf -fuzz=FuzzWithErrorf -fuzztime=100x -fuzzminimizetime=1000x
46 stdout 'testdata[/\\]fuzz[/\\]FuzzWithErrorf[/\\]'
47 stdout 'errorf was called here'
48 go run check_testdata.go FuzzWithErrorf
49
50 ! go test -run=FuzzWithFatalf -fuzz=FuzzWithFatalf -fuzztime=100x -fuzzminimizetime=1000x
51 stdout 'testdata[/\\]fuzz[/\\]FuzzWithFatalf[/\\]'
52 stdout 'fatalf was called here'
53 go run check_testdata.go FuzzWithFatalf
54
55 ! go test -run=FuzzWithBadExit -fuzz=FuzzWithBadExit -fuzztime=100x -fuzzminimizetime=1000x
56 stdout 'testdata[/\\]fuzz[/\\]FuzzWithBadExit[/\\]'
57 stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status'
58 go run check_testdata.go FuzzWithBadExit
59
60 ! go test -run=FuzzDeadlock -fuzz=FuzzDeadlock -fuzztime=100x -fuzzminimizetime=0x
61 stdout 'testdata[/\\]fuzz[/\\]FuzzDeadlock[/\\]'
62 stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status'
63 go run check_testdata.go FuzzDeadlock
64
65 # Running the fuzzer should find a crashing input quickly for fuzzing two types.
66 ! go test -run=FuzzWithTwoTypes -fuzz=FuzzWithTwoTypes -fuzztime=100x -fuzzminimizetime=1000x
67 stdout 'testdata[/\\]fuzz[/\\]FuzzWithTwoTypes[/\\]'
68 stdout 'these inputs caused a crash!'
69 go run check_testdata.go FuzzWithTwoTypes
70
71 # Running the fuzzer should find a crashing input quickly for an integer.
72 ! go test -run=FuzzInt -fuzz=FuzzInt -fuzztime=100x -fuzzminimizetime=1000x
73 stdout 'testdata[/\\]fuzz[/\\]FuzzInt[/\\]'
74 stdout 'this input caused a crash!'
75 go run check_testdata.go FuzzInt
76
77 ! go test -run=FuzzUint -fuzz=FuzzUint -fuzztime=100x -fuzzminimizetime=1000x
78 stdout 'testdata[/\\]fuzz[/\\]FuzzUint[/\\]'
79 stdout 'this input caused a crash!'
80 go run check_testdata.go FuzzUint
81
82 # Running the fuzzer should find a crashing input quickly for a bool.
83 ! go test -run=FuzzBool -fuzz=FuzzBool -fuzztime=100x -fuzzminimizetime=1000x
84 stdout 'testdata[/\\]fuzz[/\\]FuzzBool[/\\]'
85 stdout 'this input caused a crash!'
86 go run check_testdata.go FuzzBool
87
88 # Running the fuzzer should find a crashing input quickly for a float.
89 ! go test -run=FuzzFloat -fuzz=FuzzFloat -fuzztime=100x -fuzzminimizetime=1000x
90 stdout 'testdata[/\\]fuzz[/\\]FuzzFloat[/\\]'
91 stdout 'this input caused a crash!'
92 go run check_testdata.go FuzzFloat
93
94 # Running the fuzzer should find a crashing input quickly for a byte.
95 ! go test -run=FuzzByte -fuzz=FuzzByte -fuzztime=100x -fuzzminimizetime=1000x
96 stdout 'testdata[/\\]fuzz[/\\]FuzzByte[/\\]'
97 stdout 'this input caused a crash!'
98 go run check_testdata.go FuzzByte
99
100 # Running the fuzzer should find a crashing input quickly for a rune.
101 ! go test -run=FuzzRune -fuzz=FuzzRune -fuzztime=100x -fuzzminimizetime=1000x
102 stdout 'testdata[/\\]fuzz[/\\]FuzzRune[/\\]'
103 stdout 'this input caused a crash!'
104 go run check_testdata.go FuzzRune
105
106 # Running the fuzzer should find a crashing input quickly for a string.
107 ! go test -run=FuzzString -fuzz=FuzzString -fuzztime=100x -fuzzminimizetime=1000x
108 stdout 'testdata[/\\]fuzz[/\\]FuzzString[/\\]'
109 stdout 'this input caused a crash!'
110 go run check_testdata.go FuzzString
111
112 -- go.mod --
113 module m
114
115 go 1.16
116 -- fuzz_crash_test.go --
117 package fuzz_crash
118
119 import (
120 "os"
121 "runtime"
122 "testing"
123 )
124
125 func FuzzWithBug(f *testing.F) {
126 f.Add([]byte("aa"))
127 f.Fuzz(func(t *testing.T, b []byte) {
128 if string(b) != "aa" {
129 panic("this input caused a crash!")
130 }
131 })
132 }
133
134 func FuzzWithNilPanic(f *testing.F) {
135 f.Add([]byte("aa"))
136 f.Fuzz(func(t *testing.T, b []byte) {
137 if string(b) != "aa" {
138 panic(nil)
139 }
140 })
141 }
142
143 func FuzzWithGoexit(f *testing.F) {
144 f.Add([]byte("aa"))
145 f.Fuzz(func(t *testing.T, b []byte) {
146 if string(b) != "aa" {
147 runtime.Goexit()
148 }
149 })
150 }
151
152 func FuzzWithFail(f *testing.F) {
153 f.Add([]byte("aa"))
154 f.Fuzz(func(t *testing.T, b []byte) {
155 if string(b) != "aa" {
156 t.Fail()
157 }
158 })
159 }
160
161 func FuzzWithLogFail(f *testing.F) {
162 f.Add([]byte("aa"))
163 f.Fuzz(func(t *testing.T, b []byte) {
164 if string(b) != "aa" {
165 t.Log("logged something")
166 t.Fail()
167 }
168 })
169 }
170
171 func FuzzWithErrorf(f *testing.F) {
172 f.Add([]byte("aa"))
173 f.Fuzz(func(t *testing.T, b []byte) {
174 if string(b) != "aa" {
175 t.Errorf("errorf was called here")
176 }
177 })
178 }
179
180 func FuzzWithFatalf(f *testing.F) {
181 f.Add([]byte("aa"))
182 f.Fuzz(func(t *testing.T, b []byte) {
183 if string(b) != "aa" {
184 t.Fatalf("fatalf was called here")
185 }
186 })
187 }
188
189 func FuzzWithBadExit(f *testing.F) {
190 f.Add([]byte("aa"))
191 f.Fuzz(func(t *testing.T, b []byte) {
192 if string(b) != "aa" {
193 os.Exit(1)
194 }
195 })
196 }
197
198 func FuzzDeadlock(f *testing.F) {
199 f.Add(int(0))
200 f.Fuzz(func(t *testing.T, n int) {
201 if n != 0 {
202 select {}
203 }
204 })
205 }
206
207 func FuzzWithTwoTypes(f *testing.F) {
208 f.Fuzz(func(t *testing.T, a, b []byte) {
209 if len(a) > 0 && len(b) > 0 {
210 panic("these inputs caused a crash!")
211 }
212 })
213 }
214
215 func FuzzInt(f *testing.F) {
216 f.Add(0)
217 f.Fuzz(func(t *testing.T, a int) {
218 if a != 0 {
219 panic("this input caused a crash!")
220 }
221 })
222 }
223
224 func FuzzUint(f *testing.F) {
225 f.Add(uint(0))
226 f.Fuzz(func(t *testing.T, a uint) {
227 if a != 0 {
228 panic("this input caused a crash!")
229 }
230 })
231 }
232
233 func FuzzBool(f *testing.F) {
234 f.Add(false)
235 f.Fuzz(func(t *testing.T, a bool) {
236 if a {
237 panic("this input caused a crash!")
238 }
239 })
240 }
241
242 func FuzzFloat(f *testing.F) {
243 f.Fuzz(func(t *testing.T, a float64) {
244 if a != float64(int64(a)) {
245 // It has a decimal, so it was mutated by division
246 panic("this input caused a crash!")
247 }
248 })
249 }
250
251 func FuzzByte(f *testing.F) {
252 f.Add(byte(0))
253 f.Fuzz(func(t *testing.T, a byte) {
254 if a != 0 {
255 panic("this input caused a crash!")
256 }
257 })
258 }
259
260 func FuzzRune(f *testing.F) {
261 f.Add(rune(0))
262 f.Fuzz(func(t *testing.T, a rune) {
263 if a != 0 {
264 panic("this input caused a crash!")
265 }
266 })
267 }
268
269 func FuzzString(f *testing.F) {
270 f.Add("")
271 f.Fuzz(func(t *testing.T, a string) {
272 if a != "" {
273 panic("this input caused a crash!")
274 }
275 })
276 }
277
278 -- check_testdata.go --
279 // +build ignore
280
281 package main
282
283 import (
284 "bytes"
285 "crypto/sha256"
286 "fmt"
287 "io/ioutil"
288 "os"
289 "path/filepath"
290 )
291
292 func main() {
293 target := os.Args[1]
294 dir := filepath.Join("testdata/fuzz", target)
295
296 files, err := ioutil.ReadDir(dir)
297 if err != nil {
298 fmt.Fprintln(os.Stderr, err)
299 os.Exit(1)
300 }
301
302 if len(files) == 0 {
303 fmt.Fprintf(os.Stderr, "expect at least one new mutation to be written to testdata\n")
304 os.Exit(1)
305 }
306
307 fname := files[0].Name()
308 contents, err := ioutil.ReadFile(filepath.Join(dir, fname))
309 if err != nil {
310 fmt.Fprintln(os.Stderr, err)
311 os.Exit(1)
312 }
313 if bytes.Equal(contents, []byte("aa")) {
314 fmt.Fprintf(os.Stderr, "newly written testdata entry was not mutated\n")
315 os.Exit(1)
316 }
317 // The hash of the bytes in the file should match the filename.
318 h := []byte(fmt.Sprintf("%x", sha256.Sum256(contents)))
319 if !bytes.Equal([]byte(fname), h) {
320 fmt.Fprintf(os.Stderr, "hash of bytes %q does not match filename %q\n", h, fname)
321 os.Exit(1)
322 }
323 }
324
View as plain text