| # TODO(jayconrod): support shared memory on more platforms. |
| [!darwin] [!linux] [!windows] 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 |
| |
| # 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 'testdata[/\\]fuzz[/\\]FuzzWithBug[/\\][a-f0-9]{64}' |
| stdout 'this input caused a crash!' |
| |
| ! go test -run=FuzzWithNilPanic -fuzz=FuzzWithNilPanic -fuzztime=100x -fuzzminimizetime=1000x |
| stdout 'testdata[/\\]fuzz[/\\]FuzzWithNilPanic[/\\]' |
| stdout 'runtime.Goexit' |
| go run check_testdata.go FuzzWithNilPanic |
| |
| ! 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 'unexpectedly' |
| go run check_testdata.go FuzzWithBadExit |
| |
| # 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" |
| "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 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 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 != float64(int64(a)) { |
| // It has a decimal, so it was mutated by division |
| 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.Equal([]byte(fname), h) { |
| fmt.Fprintf(os.Stderr, "hash of bytes %q does not match filename %q\n", h, fname) |
| os.Exit(1) |
| } |
| } |