aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
blob: 5e1d90d8d9b3aabe70c6fc3aa3ebd291b07c4626 (plain)
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
# TODO(jayconrod): support shared memory on more platforms.
[!darwin] [!linux] [!windows] skip

# Instrumentation only supported on 64-bit architectures.
[!amd64] [!arm64] skip

# Test that when an interesting value is discovered (one that expands coverage),
# the fuzzing engine minimizes it before writing it to the cache.
#
# The program below starts with a seed value of length 100, but more coverage
# will be found for any value other than the seed. We should end with a value
# in the cache of length 1 (the minimizer currently does not produce empty
# strings). check_cache.go confirms that.
#
# We would like to verify that ALL values in the cache were minimized to a
# length of 1, but this isn't always possible when new coverage is found in
# functions called by testing or internal/fuzz in the background.

go test -c -fuzz=.  # Build using shared build cache for speed.
env GOCACHE=$WORK/gocache
exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=. -test.fuzztime=1000x
go run check_cache.go $GOCACHE/fuzz/FuzzMin

-- go.mod --
module fuzz

go 1.17
-- fuzz_test.go --
package fuzz

import (
	"bytes"
	"testing"
)

func FuzzMin(f *testing.F) {
	seed := bytes.Repeat([]byte("a"), 20)
	f.Add(seed)
	f.Fuzz(func(t *testing.T, buf []byte) {
		if bytes.Equal(buf, seed) {
			return
		}
		if n := sum(buf); n < 0 {
			t.Error("sum cannot be negative")
		}
	})
}

func sum(buf []byte) int {
	n := 0
	for _, b := range buf {
		n += int(b)
	}
	return n
}
-- check_cache.go --
//go:build ignore
// +build ignore

// check_cache.go checks that each file in the cached corpus has a []byte
// of length at most 1. This verifies that at least one cached input is minimized.
package main

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
)

func main() {
	dir := os.Args[1]
	ents, err := os.ReadDir(dir)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	for _, ent := range ents {
		name := filepath.Join(dir, ent.Name())
		if good, err := checkCacheFile(name); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		} else if good {
			os.Exit(0)
		}
	}
	fmt.Fprintln(os.Stderr, "no cached inputs were minimized")
	os.Exit(1)
}

func checkCacheFile(name string) (good bool, err error) {
	data, err := os.ReadFile(name)
	if err != nil {
		return false, err
	}
	for _, line := range bytes.Split(data, []byte("\n")) {
		m := valRe.FindSubmatch(line)
		if m == nil {
			continue
		}
		if s, err := strconv.Unquote(string(m[1])); err != nil {
			return false, err
		} else if len(s) <= 1 {
			return true, nil
		}
	}
	return false, nil
}

var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)