# 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\(([^)]+)\)$`)