aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt')
-rw-r--r--src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt b/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
new file mode 100644
index 0000000000..5e1d90d8d9
--- /dev/null
+++ b/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
@@ -0,0 +1,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\(([^)]+)\)$`)