[!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) } }