File size: 3,746 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 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | [!fuzz] skip
# Test basic fuzzing mutator behavior.
#
# fuzz_test.go has two fuzz targets (FuzzA, FuzzB) which both add a seed value.
# Each fuzz function writes the input to a log file. The coordinator and worker
# use separate log files. check_logs.go verifies that the coordinator only
# tests seed values and the worker tests mutated values on the fuzz target.
[short] skip
env GOCACHE=$WORK/cache
go test -fuzz=FuzzA -fuzztime=100x -parallel=1 -log=fuzz
go run check_logs.go fuzz fuzz.worker
# TODO(b/181800488): remove -parallel=1, here and below. For now, when a
# crash is found, all workers keep running, wasting resources and reducing
# the number of executions available to the minimizer, increasing flakiness.
# Test that the mutator is good enough to find several unique mutations.
! go test -fuzz=FuzzMutator -parallel=1 -fuzztime=100x mutator_test.go
! stdout '^ok'
stdout FAIL
stdout 'mutator found enough unique mutations'
-- go.mod --
module m
go 1.16
-- fuzz_test.go --
package fuzz_test
import (
"flag"
"fmt"
"os"
"testing"
)
var (
logPath = flag.String("log", "", "path to log file")
logFile *os.File
)
func TestMain(m *testing.M) {
flag.Parse()
var err error
logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
if os.IsExist(err) {
*logPath += ".worker"
logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
}
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Exit(m.Run())
}
func FuzzA(f *testing.F) {
f.Add([]byte("seed"))
f.Fuzz(func(t *testing.T, b []byte) {
fmt.Fprintf(logFile, "FuzzA %q\n", b)
})
}
func FuzzB(f *testing.F) {
f.Add([]byte("seed"))
f.Fuzz(func(t *testing.T, b []byte) {
fmt.Fprintf(logFile, "FuzzB %q\n", b)
})
}
-- check_logs.go --
// +build ignore
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"strings"
)
func main() {
coordPath, workerPath := os.Args[1], os.Args[2]
coordLog, err := os.Open(coordPath)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer coordLog.Close()
if err := checkCoordLog(coordLog); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
workerLog, err := os.Open(workerPath)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer workerLog.Close()
if err := checkWorkerLog(workerLog); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func checkCoordLog(r io.Reader) error {
b, err := io.ReadAll(r)
if err != nil {
return err
}
if string(bytes.TrimSpace(b)) != `FuzzB "seed"` {
return fmt.Errorf("coordinator: did not test FuzzB seed")
}
return nil
}
func checkWorkerLog(r io.Reader) error {
scan := bufio.NewScanner(r)
var sawAMutant bool
for scan.Scan() {
line := scan.Text()
if !strings.HasPrefix(line, "FuzzA ") {
return fmt.Errorf("worker: tested something other than target: %s", line)
}
if strings.TrimPrefix(line, "FuzzA ") != `"seed"` {
sawAMutant = true
}
}
if err := scan.Err(); err != nil && err != bufio.ErrTooLong {
return err
}
if !sawAMutant {
return fmt.Errorf("worker: did not test any mutants")
}
return nil
}
-- mutator_test.go --
package fuzz_test
import (
"testing"
)
// TODO(katiehockman): re-work this test once we have a better fuzzing engine
// (ie. more mutations, and compiler instrumentation)
func FuzzMutator(f *testing.F) {
// TODO(katiehockman): simplify this once we can dedupe crashes (e.g.
// replace map with calls to panic, and simply count the number of crashes
// that were added to testdata)
crashes := make(map[string]bool)
// No seed corpus initiated
f.Fuzz(func(t *testing.T, b []byte) {
crashes[string(b)] = true
if len(crashes) >= 10 {
panic("mutator found enough unique mutations")
}
})
}
|