Text file
src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
1 [short] skip
2 [!fuzz-instrumented] skip
3
4 # Test that when an interesting value is discovered (one that expands coverage),
5 # the fuzzing engine minimizes it before writing it to the cache.
6 #
7 # The program below starts with a seed value of length 100, but more coverage
8 # will be found for any value other than the seed. We should end with a value
9 # in the cache of length 1 (the minimizer currently does not produce empty
10 # strings). check_cache.go confirms that.
11 #
12 # We would like to verify that ALL values in the cache were minimized to a
13 # length of 1, but this isn't always possible when new coverage is found in
14 # functions called by testing or internal/fuzz in the background.
15
16 go test -c -fuzz=. # Build using shared build cache for speed.
17 env GOCACHE=$WORK/gocache
18 exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinCache -test.fuzztime=1000x
19 go run check_cache/check_cache.go $GOCACHE/fuzz/FuzzMinCache
20
21 # Test that minimization occurs for a crash that appears while minimizing a
22 # newly found interesting input. There must be only one worker for this test to
23 # be flaky like we want.
24 ! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerCrashInMinimization -test.run=^$ -test.fuzztime=10000x -test.parallel=1
25 ! stdout '^ok'
26 stdout -count=1 'got the minimum size!'
27 stdout -count=1 'bad input'
28 stdout FAIL
29 # Check that the input written to testdata will reproduce the error, and is the
30 # smallest possible.
31 go run check_testdata/check_testdata.go FuzzMinimizerCrashInMinimization 1
32
33 # Test that a nonrecoverable error that occurs while minimizing an interesting
34 # input is reported correctly.
35 ! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerNonrecoverableCrashInMinimization -test.run=^$ -test.fuzztime=10000x -test.parallel=1
36 ! stdout '^ok'
37 stdout -count=1 'fuzzing process hung or terminated unexpectedly while minimizing'
38 stdout -count=1 'EOF'
39 stdout FAIL
40 # Check that the input written to testdata will reproduce the error.
41 go run check_testdata/check_testdata.go FuzzMinimizerNonrecoverableCrashInMinimization 1
42
43 -- go.mod --
44 module fuzz
45
46 go 1.17
47 -- y.go --
48 package fuzz
49
50 import (
51 "bytes"
52 "io"
53 )
54
55 func Y(w io.Writer, s string) {
56 if !bytes.Equal([]byte(s), []byte("y")) {
57 w.Write([]byte("not equal"))
58 }
59 }
60 -- fuzz_test.go --
61 package fuzz
62
63 import (
64 "bytes"
65 "os"
66 "testing"
67 )
68
69 func FuzzMinimizerCrashInMinimization(f *testing.F) {
70 seed := bytes.Repeat([]byte{255}, 100)
71 f.Add(seed)
72 f.Fuzz(func(t *testing.T, b []byte) {
73 if bytes.Equal(seed, b) {
74 return
75 }
76 t.Error("bad input")
77 if len(b) == 1 {
78 t.Error("got the minimum size!")
79 }
80 })
81 }
82
83 var fuzzing bool
84
85 func FuzzMinimizerNonrecoverableCrashInMinimization(f *testing.F) {
86 seed := bytes.Repeat([]byte{255}, 100)
87 f.Add(seed)
88 f.Fuzz(func(t *testing.T, b []byte) {
89 if bytes.Equal(seed, b) {
90 return
91 } else if len(b) == 1 {
92 os.Exit(1)
93 }
94 })
95 }
96
97 func FuzzMinCache(f *testing.F) {
98 seed := bytes.Repeat([]byte("a"), 20)
99 f.Add(seed)
100 f.Fuzz(func(t *testing.T, buf []byte) {
101 if bytes.Equal(buf, seed) {
102 return
103 }
104 })
105 }
106 -- check_testdata/check_testdata.go --
107 //go:build ignore
108 // +build ignore
109
110 // check_testdata.go checks that the string written
111 // is not longer than the provided length.
112 package main
113
114 import (
115 "bytes"
116 "fmt"
117 "io/ioutil"
118 "os"
119 "path/filepath"
120 "regexp"
121 "strconv"
122 )
123
124 func main() {
125 wantLen, err := strconv.Atoi(os.Args[2])
126 if err != nil {
127 fmt.Fprintln(os.Stderr, err)
128 os.Exit(1)
129 }
130 testName := os.Args[1]
131 dir := filepath.Join("testdata/fuzz", testName)
132
133 files, err := ioutil.ReadDir(dir)
134 if err != nil {
135 fmt.Fprintln(os.Stderr, err)
136 os.Exit(1)
137 }
138
139 if len(files) == 0 {
140 fmt.Fprintf(os.Stderr, "expect at least one failure to be written to testdata\n")
141 os.Exit(1)
142 }
143
144 for _, f := range files {
145 data, err := ioutil.ReadFile(filepath.Join(dir, f.Name()))
146 if err != nil {
147 panic(err)
148 }
149 var containsVal bool
150 for _, line := range bytes.Split(data, []byte("\n")) {
151 m := valRe.FindSubmatch(line)
152 if m == nil {
153 continue
154 }
155 containsVal = true
156 s, err := strconv.Unquote(string(m[1]))
157 if err != nil {
158 panic(err)
159 }
160 if len(s) != wantLen {
161 fmt.Fprintf(os.Stderr, "expect length %d, got %d (%q)\n", wantLen, len(s), line)
162 os.Exit(1)
163 }
164 }
165 if !containsVal {
166 fmt.Fprintln(os.Stderr, "corpus file contained no values")
167 os.Exit(1)
168 }
169 }
170 }
171
172 var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
173
174 -- check_cache/check_cache.go --
175 //go:build ignore
176 // +build ignore
177
178 // check_cache.go checks that each file in the cached corpus has a []byte
179 // of length at most 1. This verifies that at least one cached input is minimized.
180 package main
181
182 import (
183 "bytes"
184 "fmt"
185 "os"
186 "path/filepath"
187 "regexp"
188 "strconv"
189 )
190
191 func main() {
192 dir := os.Args[1]
193 ents, err := os.ReadDir(dir)
194 if err != nil {
195 fmt.Fprintln(os.Stderr, err)
196 os.Exit(1)
197 }
198 for _, ent := range ents {
199 name := filepath.Join(dir, ent.Name())
200 if good, err := checkCacheFile(name); err != nil {
201 fmt.Fprintln(os.Stderr, err)
202 os.Exit(1)
203 } else if good {
204 os.Exit(0)
205 }
206 }
207 fmt.Fprintln(os.Stderr, "no cached inputs were minimized")
208 os.Exit(1)
209 }
210
211 func checkCacheFile(name string) (good bool, err error) {
212 data, err := os.ReadFile(name)
213 if err != nil {
214 return false, err
215 }
216 for _, line := range bytes.Split(data, []byte("\n")) {
217 m := valRe.FindSubmatch(line)
218 if m == nil {
219 continue
220 }
221 if s, err := strconv.Unquote(string(m[1])); err != nil {
222 return false, err
223 } else if len(s) <= 1 {
224 return true, nil
225 }
226 }
227 return false, nil
228 }
229
230 var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
231
View as plain text