File size: 3,239 Bytes
e36aeda
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
skip # a 5s timeout is never going to be reliable (go.dev/issue/72140)

[!fuzz] skip
[short] skip
env GOCACHE=$WORK/cache

# There are no seed values, so 'go test' should finish quickly.
go test

# For the fuzzing phase, we reduce GOMAXPROCS to avoid consuming too many
# resources during the test. Ideally this would just free up resources to run
# other parallel tests more quickly, but unfortunately it is actually necessary
# in some 32-bit environments to prevent the fuzzing engine from running out of
# address space (see https://go.dev/issue/65434).
env GOMAXPROCS=2

# Fuzzing should exit 0 after fuzztime, even if timeout is short.
go test -timeout=3s -fuzz=FuzzFast -fuzztime=5s

# We should see the same behavior when invoking the test binary directly.
go test -c
exec ./fuzz.test$GOEXE -test.timeout=3s -test.fuzz=FuzzFast -test.fuzztime=5s -test.parallel=1 -test.fuzzcachedir=$WORK/cache

# Timeout should not cause inputs to be written as crashers.
! exists testdata/fuzz

# When we use fuzztime with an "x" suffix, it runs a specific number of times.
# This fuzz function creates a file with a unique name ($pid.$count) on each
# run. We count the files to find the number of runs.
mkdir count
go test -fuzz=FuzzTestCount -fuzztime=1000x -fuzzminimizetime=1x
go run check_file_count.go count 1000

# When we use fuzzminimizetime with an "x" suffix, it runs a specific number of
# times while minimizing. This fuzz function creates a file with a unique name
# ($pid.$count) on each run once the first crash has been found. That means that
# there should be one file for each execution of the fuzz function during
# minimization, so we count these to determine how many times minimization was
# run.
mkdir minimizecount
! go test -fuzz=FuzzMinimizeCount -fuzzminimizetime=3x -parallel=1
go run check_file_count.go minimizecount 3

-- go.mod --
module fuzz

go 1.16
-- fuzz_fast_test.go --
package fuzz_test

import "testing"

func FuzzFast(f *testing.F) {
	f.Fuzz(func (*testing.T, []byte) {})
}
-- fuzz_count_test.go --
package fuzz

import (
	"fmt"
	"os"
	"testing"
)

func FuzzTestCount(f *testing.F) {
	pid := os.Getpid()
	n := 0
	f.Fuzz(func(t *testing.T, _ []byte) {
		name := fmt.Sprintf("count/%v.%d", pid, n)
		if err := os.WriteFile(name, nil, 0666); err != nil {
			t.Fatal(err)
		}
		n++
	})
}
-- fuzz_minimize_count_test.go --
package fuzz

import (
	"bytes"
	"fmt"
	"os"
	"testing"
)

func FuzzMinimizeCount(f *testing.F) {
	pid := os.Getpid()
	n := 0
	seed := bytes.Repeat([]byte("a"), 357)
	f.Add(seed)
	crashFound := false
	f.Fuzz(func(t *testing.T, b []byte) {
		if crashFound {
			name := fmt.Sprintf("minimizecount/%v.%d", pid, n)
			if err := os.WriteFile(name, nil, 0666); err != nil {
				t.Fatal(err)
			}
			n++
		}
		if !bytes.Equal(b, seed) {  // this should happen right away
			crashFound = true
			t.Error("minimize this!")
		}
	})
}
-- check_file_count.go --
// +build ignore

package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {
	dir, err := os.ReadDir(os.Args[1])
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	got := len(dir)
	want, _ := strconv.Atoi(os.Args[2])
	if got != want {
		fmt.Fprintf(os.Stderr, "got %d files; want %d\n", got, want)
		os.Exit(1)
	}
}