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 env GOCACHE=$WORK/cache
10
11 # Running the seed corpus for all of the targets should pass the first
12 # time, since nothing in the seed corpus will cause a crash.
13 go test
14
15 # Running the fuzzer should find a crashing input quickly.
16 ! go test -fuzz=FuzzWithBug -fuzztime=100x -fuzzminimizetime=1000x
17 stdout 'testdata[/\\]fuzz[/\\]FuzzWithBug[/\\]'
18 stdout 'this input caused a crash!'
19 go run check_testdata.go FuzzWithBug
20
21 # Now, the failing bytes should have been added to the seed corpus for
22 # the target, and should fail when run without fuzzing.
23 ! go test
24 stdout 'FuzzWithBug/[a-f0-9]{16}'
25 stdout 'this input caused a crash!'
26
27 ! go test -run=FuzzWithNilPanic -fuzz=FuzzWithNilPanic -fuzztime=100x -fuzzminimizetime=1000x
28 stdout 'testdata[/\\]fuzz[/\\]FuzzWithNilPanic[/\\]'
29 stdout 'panic called with nil argument|test executed panic.nil. or runtime.Goexit'
30 go run check_testdata.go FuzzWithNilPanic
31
32 ! go test -run=FuzzWithGoexit -fuzz=FuzzWithGoexit -fuzztime=100x -fuzzminimizetime=1000x
33 stdout 'testdata[/\\]fuzz[/\\]FuzzWithGoexit[/\\]'
34 stdout 'runtime.Goexit'
35 go run check_testdata.go FuzzWithGoexit
36
37 ! go test -run=FuzzWithFail -fuzz=FuzzWithFail -fuzztime=100x -fuzzminimizetime=1000x
38 stdout 'testdata[/\\]fuzz[/\\]FuzzWithFail[/\\]'
39 go run check_testdata.go FuzzWithFail
40
41 ! go test -run=FuzzWithLogFail -fuzz=FuzzWithLogFail -fuzztime=100x -fuzzminimizetime=1000x
42 stdout 'testdata[/\\]fuzz[/\\]FuzzWithLogFail[/\\]'
43 stdout 'logged something'
44 go run check_testdata.go FuzzWithLogFail
45
46 ! go test -run=FuzzWithErrorf -fuzz=FuzzWithErrorf -fuzztime=100x -fuzzminimizetime=1000x
47 stdout 'testdata[/\\]fuzz[/\\]FuzzWithErrorf[/\\]'
48 stdout 'errorf was called here'
49 go run check_testdata.go FuzzWithErrorf
50
51 ! go test -run=FuzzWithFatalf -fuzz=FuzzWithFatalf -fuzztime=100x -fuzzminimizetime=1000x
52 stdout 'testdata[/\\]fuzz[/\\]FuzzWithFatalf[/\\]'
53 stdout 'fatalf was called here'
54 go run check_testdata.go FuzzWithFatalf
55
56 ! go test -run=FuzzWithBadExit -fuzz=FuzzWithBadExit -fuzztime=100x -fuzzminimizetime=1000x
57 stdout 'testdata[/\\]fuzz[/\\]FuzzWithBadExit[/\\]'
58 stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status'
59 go run check_testdata.go FuzzWithBadExit
60
61 ! go test -run=FuzzDeadlock -fuzz=FuzzDeadlock -fuzztime=100x -fuzzminimizetime=0x
62 stdout 'testdata[/\\]fuzz[/\\]FuzzDeadlock[/\\]'
63 stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status'
64 go run check_testdata.go FuzzDeadlock
65
66 # Running the fuzzer should find a crashing input quickly for fuzzing two types.
67 ! go test -run=FuzzWithTwoTypes -fuzz=FuzzWithTwoTypes -fuzztime=100x -fuzzminimizetime=1000x
68 stdout 'testdata[/\\]fuzz[/\\]FuzzWithTwoTypes[/\\]'
69 stdout 'these inputs caused a crash!'
70 go run check_testdata.go FuzzWithTwoTypes
71
72 # Running the fuzzer should find a crashing input quickly for an integer.
73 ! go test -run=FuzzInt -fuzz=FuzzInt -fuzztime=100x -fuzzminimizetime=1000x
74 stdout 'testdata[/\\]fuzz[/\\]FuzzInt[/\\]'
75 stdout 'this input caused a crash!'
76 go run check_testdata.go FuzzInt
77
78 ! go test -run=FuzzUint -fuzz=FuzzUint -fuzztime=100x -fuzzminimizetime=1000x
79 stdout 'testdata[/\\]fuzz[/\\]FuzzUint[/\\]'
80 stdout 'this input caused a crash!'
81 go run check_testdata.go FuzzUint
82
83 # Running the fuzzer should find a crashing input quickly for a bool.
84 ! go test -run=FuzzBool -fuzz=FuzzBool -fuzztime=100x -fuzzminimizetime=1000x
85 stdout 'testdata[/\\]fuzz[/\\]FuzzBool[/\\]'
86 stdout 'this input caused a crash!'
87 go run check_testdata.go FuzzBool
88
89 # Running the fuzzer should find a crashing input quickly for a float.
90 ! go test -run=FuzzFloat -fuzz=FuzzFloat -fuzztime=100x -fuzzminimizetime=1000x
91 stdout 'testdata[/\\]fuzz[/\\]FuzzFloat[/\\]'
92 stdout 'this input caused a crash!'
93 go run check_testdata.go FuzzFloat
94
95 # Running the fuzzer should find a crashing input quickly for a byte.
96 ! go test -run=FuzzByte -fuzz=FuzzByte -fuzztime=100x -fuzzminimizetime=1000x
97 stdout 'testdata[/\\]fuzz[/\\]FuzzByte[/\\]'
98 stdout 'this input caused a crash!'
99 go run check_testdata.go FuzzByte
100
101 # Running the fuzzer should find a crashing input quickly for a rune.
102 ! go test -run=FuzzRune -fuzz=FuzzRune -fuzztime=100x -fuzzminimizetime=1000x
103 stdout 'testdata[/\\]fuzz[/\\]FuzzRune[/\\]'
104 stdout 'this input caused a crash!'
105 go run check_testdata.go FuzzRune
106
107 # Running the fuzzer should find a crashing input quickly for a string.
108 ! go test -run=FuzzString -fuzz=FuzzString -fuzztime=100x -fuzzminimizetime=1000x
109 stdout 'testdata[/\\]fuzz[/\\]FuzzString[/\\]'
110 stdout 'this input caused a crash!'
111 go run check_testdata.go FuzzString
112
113 -- go.mod --
114 module m
115
116 go 1.16
117 -- fuzz_crash_test.go --
118 package fuzz_crash
119
120 import (
121 "os"
122 "runtime"
123 "testing"
124 )
125
126 func FuzzWithBug(f *testing.F) {
127 f.Add([]byte("aa"))
128 f.Fuzz(func(t *testing.T, b []byte) {
129 if string(b) != "aa" {
130 panic("this input caused a crash!")
131 }
132 })
133 }
134
135 func FuzzWithNilPanic(f *testing.F) {
136 f.Add([]byte("aa"))
137 f.Fuzz(func(t *testing.T, b []byte) {
138 if string(b) != "aa" {
139 panic(nil)
140 }
141 })
142 }
143
144 func FuzzWithGoexit(f *testing.F) {
145 f.Add([]byte("aa"))
146 f.Fuzz(func(t *testing.T, b []byte) {
147 if string(b) != "aa" {
148 runtime.Goexit()
149 }
150 })
151 }
152
153 func FuzzWithFail(f *testing.F) {
154 f.Add([]byte("aa"))
155 f.Fuzz(func(t *testing.T, b []byte) {
156 if string(b) != "aa" {
157 t.Fail()
158 }
159 })
160 }
161
162 func FuzzWithLogFail(f *testing.F) {
163 f.Add([]byte("aa"))
164 f.Fuzz(func(t *testing.T, b []byte) {
165 if string(b) != "aa" {
166 t.Log("logged something")
167 t.Fail()
168 }
169 })
170 }
171
172 func FuzzWithErrorf(f *testing.F) {
173 f.Add([]byte("aa"))
174 f.Fuzz(func(t *testing.T, b []byte) {
175 if string(b) != "aa" {
176 t.Errorf("errorf was called here")
177 }
178 })
179 }
180
181 func FuzzWithFatalf(f *testing.F) {
182 f.Add([]byte("aa"))
183 f.Fuzz(func(t *testing.T, b []byte) {
184 if string(b) != "aa" {
185 t.Fatalf("fatalf was called here")
186 }
187 })
188 }
189
190 func FuzzWithBadExit(f *testing.F) {
191 f.Add([]byte("aa"))
192 f.Fuzz(func(t *testing.T, b []byte) {
193 if string(b) != "aa" {
194 os.Exit(1)
195 }
196 })
197 }
198
199 func FuzzDeadlock(f *testing.F) {
200 f.Add(int(0))
201 f.Fuzz(func(t *testing.T, n int) {
202 if n != 0 {
203 select {}
204 }
205 })
206 }
207
208 func FuzzWithTwoTypes(f *testing.F) {
209 f.Fuzz(func(t *testing.T, a, b []byte) {
210 if len(a) > 0 && len(b) > 0 {
211 panic("these inputs caused a crash!")
212 }
213 })
214 }
215
216 func FuzzInt(f *testing.F) {
217 f.Add(0)
218 f.Fuzz(func(t *testing.T, a int) {
219 if a != 0 {
220 panic("this input caused a crash!")
221 }
222 })
223 }
224
225 func FuzzUint(f *testing.F) {
226 f.Add(uint(0))
227 f.Fuzz(func(t *testing.T, a uint) {
228 if a != 0 {
229 panic("this input caused a crash!")
230 }
231 })
232 }
233
234 func FuzzBool(f *testing.F) {
235 f.Add(false)
236 f.Fuzz(func(t *testing.T, a bool) {
237 if a {
238 panic("this input caused a crash!")
239 }
240 })
241 }
242
243 func FuzzFloat(f *testing.F) {
244 f.Fuzz(func(t *testing.T, a float64) {
245 if a != 0 {
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.HasPrefix(h, []byte(fname)) {
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