| [!fuzz] skip | |
| # Tests that a crash caused by a mutator-discovered input writes the bad input | |
| # to testdata, and fails+reports correctly. This tests the end-to-end behavior | |
| # of the mutator finding a crash while fuzzing, adding it as a regression test | |
| # to the seed corpus in testdata, and failing the next time the test is run. | |
| [short] skip | |
| env GOCACHE=$WORK/cache | |
| # Running the seed corpus for all of the targets should pass the first | |
| # time, since nothing in the seed corpus will cause a crash. | |
| go test | |
| # Running the fuzzer should find a crashing input quickly. | |
| ! go test -fuzz=FuzzWithBug -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzWithBug[/\\]' | |
| stdout 'this input caused a crash!' | |
| go run check_testdata.go FuzzWithBug | |
| # Now, the failing bytes should have been added to the seed corpus for | |
| # the target, and should fail when run without fuzzing. | |
| ! go test | |
| stdout 'FuzzWithBug/[a-f0-9]{16}' | |
| stdout 'this input caused a crash!' | |
| ! go test -run=FuzzWithNilPanic -fuzz=FuzzWithNilPanic -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzWithNilPanic[/\\]' | |
| stdout 'panic called with nil argument|test executed panic.nil. or runtime.Goexit' | |
| go run check_testdata.go FuzzWithNilPanic | |
| ! go test -run=FuzzWithGoexit -fuzz=FuzzWithGoexit -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzWithGoexit[/\\]' | |
| stdout 'runtime.Goexit' | |
| go run check_testdata.go FuzzWithGoexit | |
| ! go test -run=FuzzWithFail -fuzz=FuzzWithFail -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzWithFail[/\\]' | |
| go run check_testdata.go FuzzWithFail | |
| ! go test -run=FuzzWithLogFail -fuzz=FuzzWithLogFail -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzWithLogFail[/\\]' | |
| stdout 'logged something' | |
| go run check_testdata.go FuzzWithLogFail | |
| ! go test -run=FuzzWithErrorf -fuzz=FuzzWithErrorf -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzWithErrorf[/\\]' | |
| stdout 'errorf was called here' | |
| go run check_testdata.go FuzzWithErrorf | |
| ! go test -run=FuzzWithFatalf -fuzz=FuzzWithFatalf -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzWithFatalf[/\\]' | |
| stdout 'fatalf was called here' | |
| go run check_testdata.go FuzzWithFatalf | |
| ! go test -run=FuzzWithBadExit -fuzz=FuzzWithBadExit -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzWithBadExit[/\\]' | |
| stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status' | |
| go run check_testdata.go FuzzWithBadExit | |
| ! go test -run=FuzzDeadlock -fuzz=FuzzDeadlock -fuzztime=100x -fuzzminimizetime=0x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzDeadlock[/\\]' | |
| stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status' | |
| go run check_testdata.go FuzzDeadlock | |
| # Running the fuzzer should find a crashing input quickly for fuzzing two types. | |
| ! go test -run=FuzzWithTwoTypes -fuzz=FuzzWithTwoTypes -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzWithTwoTypes[/\\]' | |
| stdout 'these inputs caused a crash!' | |
| go run check_testdata.go FuzzWithTwoTypes | |
| # Running the fuzzer should find a crashing input quickly for an integer. | |
| ! go test -run=FuzzInt -fuzz=FuzzInt -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzInt[/\\]' | |
| stdout 'this input caused a crash!' | |
| go run check_testdata.go FuzzInt | |
| ! go test -run=FuzzUint -fuzz=FuzzUint -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzUint[/\\]' | |
| stdout 'this input caused a crash!' | |
| go run check_testdata.go FuzzUint | |
| # Running the fuzzer should find a crashing input quickly for a bool. | |
| ! go test -run=FuzzBool -fuzz=FuzzBool -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzBool[/\\]' | |
| stdout 'this input caused a crash!' | |
| go run check_testdata.go FuzzBool | |
| # Running the fuzzer should find a crashing input quickly for a float. | |
| ! go test -run=FuzzFloat -fuzz=FuzzFloat -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzFloat[/\\]' | |
| stdout 'this input caused a crash!' | |
| go run check_testdata.go FuzzFloat | |
| # Running the fuzzer should find a crashing input quickly for a byte. | |
| ! go test -run=FuzzByte -fuzz=FuzzByte -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzByte[/\\]' | |
| stdout 'this input caused a crash!' | |
| go run check_testdata.go FuzzByte | |
| # Running the fuzzer should find a crashing input quickly for a rune. | |
| ! go test -run=FuzzRune -fuzz=FuzzRune -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzRune[/\\]' | |
| stdout 'this input caused a crash!' | |
| go run check_testdata.go FuzzRune | |
| # Running the fuzzer should find a crashing input quickly for a string. | |
| ! go test -run=FuzzString -fuzz=FuzzString -fuzztime=100x -fuzzminimizetime=1000x | |
| stdout 'testdata[/\\]fuzz[/\\]FuzzString[/\\]' | |
| stdout 'this input caused a crash!' | |
| go run check_testdata.go FuzzString | |
| -- go.mod -- | |
| module m | |
| go 1.16 | |
| -- fuzz_crash_test.go -- | |
| package fuzz_crash | |
| import ( | |
| "os" | |
| "runtime" | |
| "testing" | |
| ) | |
| func FuzzWithBug(f *testing.F) { | |
| f.Add([]byte("aa")) | |
| f.Fuzz(func(t *testing.T, b []byte) { | |
| if string(b) != "aa" { | |
| panic("this input caused a crash!") | |
| } | |
| }) | |
| } | |
| func FuzzWithNilPanic(f *testing.F) { | |
| f.Add([]byte("aa")) | |
| f.Fuzz(func(t *testing.T, b []byte) { | |
| if string(b) != "aa" { | |
| panic(nil) | |
| } | |
| }) | |
| } | |
| func FuzzWithGoexit(f *testing.F) { | |
| f.Add([]byte("aa")) | |
| f.Fuzz(func(t *testing.T, b []byte) { | |
| if string(b) != "aa" { | |
| runtime.Goexit() | |
| } | |
| }) | |
| } | |
| func FuzzWithFail(f *testing.F) { | |
| f.Add([]byte("aa")) | |
| f.Fuzz(func(t *testing.T, b []byte) { | |
| if string(b) != "aa" { | |
| t.Fail() | |
| } | |
| }) | |
| } | |
| func FuzzWithLogFail(f *testing.F) { | |
| f.Add([]byte("aa")) | |
| f.Fuzz(func(t *testing.T, b []byte) { | |
| if string(b) != "aa" { | |
| t.Log("logged something") | |
| t.Fail() | |
| } | |
| }) | |
| } | |
| func FuzzWithErrorf(f *testing.F) { | |
| f.Add([]byte("aa")) | |
| f.Fuzz(func(t *testing.T, b []byte) { | |
| if string(b) != "aa" { | |
| t.Errorf("errorf was called here") | |
| } | |
| }) | |
| } | |
| func FuzzWithFatalf(f *testing.F) { | |
| f.Add([]byte("aa")) | |
| f.Fuzz(func(t *testing.T, b []byte) { | |
| if string(b) != "aa" { | |
| t.Fatalf("fatalf was called here") | |
| } | |
| }) | |
| } | |
| func FuzzWithBadExit(f *testing.F) { | |
| f.Add([]byte("aa")) | |
| f.Fuzz(func(t *testing.T, b []byte) { | |
| if string(b) != "aa" { | |
| os.Exit(1) | |
| } | |
| }) | |
| } | |
| func FuzzDeadlock(f *testing.F) { | |
| f.Add(int(0)) | |
| f.Fuzz(func(t *testing.T, n int) { | |
| if n != 0 { | |
| select {} | |
| } | |
| }) | |
| } | |
| func FuzzWithTwoTypes(f *testing.F) { | |
| f.Fuzz(func(t *testing.T, a, b []byte) { | |
| if len(a) > 0 && len(b) > 0 { | |
| panic("these inputs caused a crash!") | |
| } | |
| }) | |
| } | |
| func FuzzInt(f *testing.F) { | |
| f.Add(0) | |
| f.Fuzz(func(t *testing.T, a int) { | |
| if a != 0 { | |
| panic("this input caused a crash!") | |
| } | |
| }) | |
| } | |
| func FuzzUint(f *testing.F) { | |
| f.Add(uint(0)) | |
| f.Fuzz(func(t *testing.T, a uint) { | |
| if a != 0 { | |
| panic("this input caused a crash!") | |
| } | |
| }) | |
| } | |
| func FuzzBool(f *testing.F) { | |
| f.Add(false) | |
| f.Fuzz(func(t *testing.T, a bool) { | |
| if a { | |
| panic("this input caused a crash!") | |
| } | |
| }) | |
| } | |
| func FuzzFloat(f *testing.F) { | |
| f.Fuzz(func(t *testing.T, a float64) { | |
| if a != 0 { | |
| panic("this input caused a crash!") | |
| } | |
| }) | |
| } | |
| func FuzzByte(f *testing.F) { | |
| f.Add(byte(0)) | |
| f.Fuzz(func(t *testing.T, a byte) { | |
| if a != 0 { | |
| panic("this input caused a crash!") | |
| } | |
| }) | |
| } | |
| func FuzzRune(f *testing.F) { | |
| f.Add(rune(0)) | |
| f.Fuzz(func(t *testing.T, a rune) { | |
| if a != 0 { | |
| panic("this input caused a crash!") | |
| } | |
| }) | |
| } | |
| func FuzzString(f *testing.F) { | |
| f.Add("") | |
| f.Fuzz(func(t *testing.T, a string) { | |
| if a != "" { | |
| panic("this input caused a crash!") | |
| } | |
| }) | |
| } | |
| -- check_testdata.go -- | |
| // +build ignore | |
| package main | |
| import ( | |
| "bytes" | |
| "crypto/sha256" | |
| "fmt" | |
| "io/ioutil" | |
| "os" | |
| "path/filepath" | |
| ) | |
| func main() { | |
| target := os.Args[1] | |
| dir := filepath.Join("testdata/fuzz", target) | |
| files, err := ioutil.ReadDir(dir) | |
| if err != nil { | |
| fmt.Fprintln(os.Stderr, err) | |
| os.Exit(1) | |
| } | |
| if len(files) == 0 { | |
| fmt.Fprintf(os.Stderr, "expect at least one new mutation to be written to testdata\n") | |
| os.Exit(1) | |
| } | |
| fname := files[0].Name() | |
| contents, err := ioutil.ReadFile(filepath.Join(dir, fname)) | |
| if err != nil { | |
| fmt.Fprintln(os.Stderr, err) | |
| os.Exit(1) | |
| } | |
| if bytes.Equal(contents, []byte("aa")) { | |
| fmt.Fprintf(os.Stderr, "newly written testdata entry was not mutated\n") | |
| os.Exit(1) | |
| } | |
| // The hash of the bytes in the file should match the filename. | |
| h := []byte(fmt.Sprintf("%x", sha256.Sum256(contents))) | |
| if !bytes.HasPrefix(h, []byte(fname)) { | |
| fmt.Fprintf(os.Stderr, "hash of bytes %q does not match filename %q\n", h, fname) | |
| os.Exit(1) | |
| } | |
| } | |