| # TODO(jayconrod): support shared memory on more platforms. |
| [!darwin] [!linux] [!windows] skip |
| |
| [short] skip |
| |
| # We clean the fuzz cache during this test. Don't clean the user's cache. |
| env GOCACHE=$WORK/gocache |
| |
| # Test that fuzzminimizetime cannot be negative seconds |
| ! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x -fuzzminimizetime=-1ms minimizer_test.go |
| ! stdout '^ok' |
| ! stdout 'contains a non-zero byte' |
| stdout 'invalid duration' |
| stdout FAIL |
| |
| # Test that fuzzminimizetime cannot be negative times |
| ! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x -fuzzminimizetime=-1x minimizer_test.go |
| ! stdout '^ok' |
| ! stdout 'contains a non-zero byte' |
| stdout 'invalid count' |
| stdout FAIL |
| |
| # Test that fuzzminimizetime can be zero seconds, and minimization is disabled |
| ! go test -fuzz=FuzzMinimizeZeroDurationSet -run=FuzzMinimizeZeroDurationSet -fuzztime=10000x -fuzzminimizetime=0s minimizer_test.go |
| ! stdout '^ok' |
| ! stdout 'minimizing' |
| stdout 'there was an Error' |
| stdout FAIL |
| |
| # Test that fuzzminimizetime can be zero times, and minimization is disabled |
| ! go test -fuzz=FuzzMinimizeZeroLimitSet -run=FuzzMinimizeZeroLimitSet -fuzztime=10000x -fuzzminimizetime=0x minimizer_test.go |
| ! stdout '^ok' |
| ! stdout 'minimizing' |
| stdout 'there was an Error' |
| stdout FAIL |
| |
| # Test that minimization is working for recoverable errors. |
| ! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x minimizer_test.go |
| ! stdout '^ok' |
| stdout 'got the minimum size!' |
| stdout 'contains a non-zero byte' |
| stdout FAIL |
| |
| # Check that the bytes written to testdata are of length 50 (the minimum size) |
| go run check_testdata.go FuzzMinimizerRecoverable 50 |
| |
| # Test that re-running the minimized value causes a crash. |
| ! go test -run=FuzzMinimizerRecoverable minimizer_test.go |
| rm testdata |
| |
| # Test that minimization doesn't run for non-recoverable errors. |
| ! go test -fuzz=FuzzMinimizerNonrecoverable -run=FuzzMinimizerNonrecoverable -fuzztime=10000x minimizer_test.go |
| ! stdout '^ok' |
| ! stdout 'minimizing' |
| stdout 'fuzzing process terminated unexpectedly: exit status 99' |
| stdout FAIL |
| |
| # Check that re-running the value causes a crash. |
| ! go test -run=FuzzMinimizerNonrecoverable minimizer_test.go |
| rm testdata |
| |
| # Clear the fuzzing cache. There may already be minimized inputs that would |
| # interfere with the next stage of the test. |
| go clean -fuzzcache |
| |
| # Test that minimization can be cancelled by fuzzminimizetime and the latest |
| # crash will still be logged and written to testdata. |
| ! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=100x -fuzzminimizetime=1x minimizer_test.go |
| ! stdout '^ok' |
| stdout 'testdata[/\\]fuzz[/\\]FuzzMinimizerRecoverable[/\\]' |
| ! stdout 'got the minimum size!' # it shouldn't have had enough time to minimize it |
| stdout FAIL |
| |
| # Test that re-running the unminimized value causes a crash. |
| ! go test -run=FuzzMinimizerRecoverable minimizer_test.go |
| |
| ! go test -fuzz=FuzzMinimizerTooSlow -run=FuzzMinimizerTooSlow -fuzzminimizetime=3s minimizer_test.go |
| stdout 'fuzz: minimizing' |
| stdout 'fuzz: elapsed: \d+s, minimizing' |
| stdout 'testdata[/\\]fuzz[/\\]FuzzMinimizerTooSlow[/\\]' |
| stdout FAIL |
| |
| # TODO(jayconrod,katiehockman): add a test which verifies that the right bytes |
| # are written to testdata in the case of an interrupt during minimization. |
| |
| -- go.mod -- |
| module m |
| |
| go 1.16 |
| -- minimizer_test.go -- |
| package fuzz_test |
| |
| import ( |
| "os" |
| "testing" |
| "time" |
| ) |
| |
| func FuzzMinimizeZeroDurationSet(f *testing.F) { |
| f.Fuzz(func(t *testing.T, b []byte) { |
| if len(b) > 5 { |
| t.Errorf("there was an Error") |
| } |
| }) |
| } |
| |
| func FuzzMinimizeZeroLimitSet(f *testing.F) { |
| f.Fuzz(func(t *testing.T, b []byte) { |
| if len(b) > 5 { |
| t.Errorf("there was an Error") |
| } |
| }) |
| } |
| |
| func FuzzMinimizerRecoverable(f *testing.F) { |
| f.Add(make([]byte, 100)) |
| f.Fuzz(func(t *testing.T, b []byte) { |
| if len(b) < 50 { |
| // Make sure that b is large enough that it can be minimized |
| return |
| } |
| // Given the randomness of the mutations, this should allow the |
| // minimizer to trim down the value a bit. |
| for _, n := range b { |
| if n != 0 { |
| if len(b) == 50 { |
| t.Log("got the minimum size!") |
| } |
| t.Fatal("contains a non-zero byte") |
| } |
| } |
| }) |
| } |
| |
| func FuzzMinimizerNonrecoverable(f *testing.F) { |
| f.Add(make([]byte, 100)) |
| f.Fuzz(func(t *testing.T, b []byte) { |
| if len(b) < 50 { |
| // Make sure that b is large enough that it can be minimized |
| return |
| } |
| // Given the randomness of the mutations, this should allow the |
| // minimizer to trim down the value a bit. |
| for _, n := range b { |
| if n != 0 { |
| t.Log("contains a non-zero byte") |
| os.Exit(99) |
| } |
| } |
| }) |
| } |
| |
| func FuzzMinimizerTooSlow(f *testing.F) { |
| f.Fuzz(func(t *testing.T, b []byte) { |
| if len(b) > 50 { |
| t.Error("error here") |
| time.Sleep(2 * time.Second) |
| } |
| }) |
| } |
| |
| -- check_testdata.go -- |
| // +build ignore |
| |
| package main |
| |
| import ( |
| "bytes" |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "strconv" |
| ) |
| |
| func main() { |
| target := os.Args[1] |
| numBytes, err := strconv.Atoi(os.Args[2]) |
| if err != nil { |
| fmt.Fprintln(os.Stderr, err) |
| os.Exit(1) |
| } |
| |
| // Open the file in testdata (there should only be one) |
| dir := fmt.Sprintf("testdata/fuzz/%s", target) |
| files, err := ioutil.ReadDir(dir) |
| if err != nil { |
| fmt.Fprintln(os.Stderr, err) |
| os.Exit(1) |
| } |
| if len(files) != 1 { |
| fmt.Fprintf(os.Stderr, "expected one file, got %d", len(files)) |
| os.Exit(1) |
| } |
| got, err := ioutil.ReadFile(filepath.Join(dir, files[0].Name())) |
| if err != nil { |
| fmt.Fprintln(os.Stderr, err) |
| os.Exit(1) |
| } |
| |
| // Trim the newline at the end of the file |
| got = bytes.TrimSpace(got) |
| |
| // Make sure that there were exactly 100 bytes written to the corpus entry |
| prefix := []byte("[]byte(") |
| i := bytes.Index(got, prefix) |
| gotBytes := got[i+len(prefix) : len(got)-1] |
| s, err := strconv.Unquote(string(gotBytes)) |
| if err != nil { |
| fmt.Fprintln(os.Stderr, err) |
| os.Exit(1) |
| } |
| if want, got := numBytes, len(s); want != got { |
| fmt.Fprintf(os.Stderr, "want %d bytes, got %d\n", want, got) |
| os.Exit(1) |
| } |
| } |