...
1[!fuzz] skip
2
3# Test basic fuzzing mutator behavior.
4#
5# fuzz_test.go has two fuzz targets (FuzzA, FuzzB) which both add a seed value.
6# Each fuzz function writes the input to a log file. The coordinator and worker
7# use separate log files. check_logs.go verifies that the coordinator only
8# tests seed values and the worker tests mutated values on the fuzz target.
9
10[short] skip
11env GOCACHE=$WORK/cache
12
13go test -fuzz=FuzzA -fuzztime=100x -parallel=1 -log=fuzz
14go run check_logs.go fuzz fuzz.worker
15
16# TODO(b/181800488): remove -parallel=1, here and below. For now, when a
17# crash is found, all workers keep running, wasting resources and reducing
18# the number of executions available to the minimizer, increasing flakiness.
19
20# Test that the mutator is good enough to find several unique mutations.
21! go test -fuzz=FuzzMutator -parallel=1 -fuzztime=100x mutator_test.go
22! stdout '^ok'
23stdout FAIL
24stdout 'mutator found enough unique mutations'
25
26-- go.mod --
27module m
28
29go 1.16
30-- fuzz_test.go --
31package fuzz_test
32
33import (
34 "flag"
35 "fmt"
36 "os"
37 "testing"
38)
39
40var (
41 logPath = flag.String("log", "", "path to log file")
42 logFile *os.File
43)
44
45func TestMain(m *testing.M) {
46 flag.Parse()
47 var err error
48 logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
49 if os.IsExist(err) {
50 *logPath += ".worker"
51 logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
52 }
53 if err != nil {
54 fmt.Fprintln(os.Stderr, err)
55 os.Exit(1)
56 }
57 os.Exit(m.Run())
58}
59
60func FuzzA(f *testing.F) {
61 f.Add([]byte("seed"))
62 f.Fuzz(func(t *testing.T, b []byte) {
63 fmt.Fprintf(logFile, "FuzzA %q\n", b)
64 })
65}
66
67func FuzzB(f *testing.F) {
68 f.Add([]byte("seed"))
69 f.Fuzz(func(t *testing.T, b []byte) {
70 fmt.Fprintf(logFile, "FuzzB %q\n", b)
71 })
72}
73
74-- check_logs.go --
75// +build ignore
76
77package main
78
79import (
80 "bufio"
81 "bytes"
82 "fmt"
83 "io"
84 "os"
85 "strings"
86)
87
88func main() {
89 coordPath, workerPath := os.Args[1], os.Args[2]
90
91 coordLog, err := os.Open(coordPath)
92 if err != nil {
93 fmt.Fprintln(os.Stderr, err)
94 os.Exit(1)
95 }
96 defer coordLog.Close()
97 if err := checkCoordLog(coordLog); err != nil {
98 fmt.Fprintln(os.Stderr, err)
99 os.Exit(1)
100 }
101
102 workerLog, err := os.Open(workerPath)
103 if err != nil {
104 fmt.Fprintln(os.Stderr, err)
105 os.Exit(1)
106 }
107 defer workerLog.Close()
108 if err := checkWorkerLog(workerLog); err != nil {
109 fmt.Fprintln(os.Stderr, err)
110 os.Exit(1)
111 }
112}
113
114func checkCoordLog(r io.Reader) error {
115 b, err := io.ReadAll(r)
116 if err != nil {
117 return err
118 }
119 if string(bytes.TrimSpace(b)) != `FuzzB "seed"` {
120 return fmt.Errorf("coordinator: did not test FuzzB seed")
121 }
122 return nil
123}
124
125func checkWorkerLog(r io.Reader) error {
126 scan := bufio.NewScanner(r)
127 var sawAMutant bool
128 for scan.Scan() {
129 line := scan.Text()
130 if !strings.HasPrefix(line, "FuzzA ") {
131 return fmt.Errorf("worker: tested something other than target: %s", line)
132 }
133 if strings.TrimPrefix(line, "FuzzA ") != `"seed"` {
134 sawAMutant = true
135 }
136 }
137 if err := scan.Err(); err != nil && err != bufio.ErrTooLong {
138 return err
139 }
140 if !sawAMutant {
141 return fmt.Errorf("worker: did not test any mutants")
142 }
143 return nil
144}
145-- mutator_test.go --
146package fuzz_test
147
148import (
149 "testing"
150)
151
152// TODO(katiehockman): re-work this test once we have a better fuzzing engine
153// (ie. more mutations, and compiler instrumentation)
154func FuzzMutator(f *testing.F) {
155 // TODO(katiehockman): simplify this once we can dedupe crashes (e.g.
156 // replace map with calls to panic, and simply count the number of crashes
157 // that were added to testdata)
158 crashes := make(map[string]bool)
159 // No seed corpus initiated
160 f.Fuzz(func(t *testing.T, b []byte) {
161 crashes[string(b)] = true
162 if len(crashes) >= 10 {
163 panic("mutator found enough unique mutations")
164 }
165 })
166}
View as plain text