blob: 4b9b36dc7531dd86fd13aa800987f03780481afc [file] [log] [blame]
[!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)
}
}