Text file src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt

     1  [short] skip
     2  [!fuzz-instrumented] skip
     3  
     4  # Test that when an interesting value is discovered (one that expands coverage),
     5  # the fuzzing engine minimizes it before writing it to the cache.
     6  #
     7  # The program below starts with a seed value of length 100, but more coverage
     8  # will be found for any value other than the seed. We should end with a value
     9  # in the cache of length 1 (the minimizer currently does not produce empty
    10  # strings). check_cache.go confirms that.
    11  #
    12  # We would like to verify that ALL values in the cache were minimized to a
    13  # length of 1, but this isn't always possible when new coverage is found in
    14  # functions called by testing or internal/fuzz in the background.
    15  
    16  go test -c -fuzz=.  # Build using shared build cache for speed.
    17  env GOCACHE=$WORK/gocache
    18  exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinCache -test.fuzztime=1000x
    19  go run check_cache/check_cache.go $GOCACHE/fuzz/FuzzMinCache
    20  
    21  go test -c -fuzz=.  # Build using shared build cache for speed.
    22  env GOCACHE=$WORK/gocache
    23  
    24  # Test that minimization occurs for a crash that appears while minimizing a
    25  # newly found interesting input. There must be only one worker for this test to
    26  # be flaky like we want.
    27  ! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerCrashInMinimization -test.run=FuzzMinimizerCrashInMinimization -test.fuzztime=10000x -test.parallel=1
    28  ! stdout '^ok'
    29  stdout -count=1 'got the minimum size!'
    30  stdout -count=1 'flaky failure'
    31  stdout FAIL
    32  # Check that the input written to testdata will reproduce the error, and is the
    33  # smallest possible.
    34  go run check_testdata/check_testdata.go FuzzMinimizerCrashInMinimization 50
    35  
    36  # Test that a nonrecoverable error that occurs while minimizing an interesting
    37  # input is reported correctly.
    38  ! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerNonrecoverableCrashInMinimization -test.run=FuzzMinimizerNonrecoverableCrashInMinimization -test.fuzztime=10000x -test.parallel=1
    39  ! stdout '^ok'
    40  stdout -count=1 'fuzzing process hung or terminated unexpectedly while minimizing'
    41  stdout -count=1 'EOF'
    42  stdout FAIL
    43  # Check that the input written to testdata will reproduce the error.
    44  go run check_testdata/check_testdata.go FuzzMinimizerNonrecoverableCrashInMinimization 100
    45  
    46  -- go.mod --
    47  module fuzz
    48  
    49  go 1.17
    50  -- y.go --
    51  package fuzz
    52  
    53  import (
    54  	"bytes"
    55  	"io"
    56  )
    57  
    58  func Y(w io.Writer, s string) {
    59  	if !bytes.Equal([]byte(s), []byte("y")) {
    60  		w.Write([]byte("not equal"))
    61  	}
    62  }
    63  -- fuzz_test.go --
    64  package fuzz
    65  
    66  import (
    67  	"bytes"
    68  	"io"
    69  	"os"
    70  	"strings"
    71  	"testing"
    72  	"unicode/utf8"
    73  )
    74  
    75  func FuzzMinimizerCrashInMinimization(f *testing.F) {
    76  	seed := strings.Repeat("A", 1000)
    77  	f.Add(seed)
    78  	i := 3
    79  	f.Fuzz(func(t *testing.T, s string) {
    80  		if len(s) < 50 || len(s) > 1100 {
    81  			// Make sure that b is large enough that it can be minimized
    82  			return
    83  		}
    84  		if s != seed {
    85  			// This should hit a new edge, and the interesting input
    86  			// should attempt minimization
    87  			Y(io.Discard, s)
    88  		}
    89  		if i > 0 {
    90  			// Don't let it fail right away.
    91  			i--
    92  		} else if utf8.RuneCountInString(s) == len(s) && len(s) <= 100 {
    93  			// Make sure this only fails if the number of bytes in the
    94  			// marshaled string is the same as the unmarshaled string,
    95  			// so that we can check the length of the testdata file.
    96  			t.Error("flaky failure")
    97  			if len(s) == 50 {
    98  				t.Error("got the minimum size!")
    99  			}
   100  		}
   101  	})
   102  }
   103  
   104  func FuzzMinimizerNonrecoverableCrashInMinimization(f *testing.F) {
   105  	seed := strings.Repeat("A", 1000)
   106  	f.Add(seed)
   107  	i := 3
   108  	f.Fuzz(func(t *testing.T, s string) {
   109  		if len(s) < 50 || len(s) > 1100 {
   110  			return
   111  		}
   112  		if s != seed {
   113  			Y(io.Discard, s)
   114  		}
   115  		if i > 0 {
   116  			i--
   117  		} else if utf8.RuneCountInString(s) == len(s) && len(s) <= 100 {
   118  			os.Exit(19)
   119  		}
   120  	})
   121  }
   122  
   123  func FuzzMinCache(f *testing.F) {
   124  	seed := bytes.Repeat([]byte("a"), 20)
   125  	f.Add(seed)
   126  	f.Fuzz(func(t *testing.T, buf []byte) {
   127  		if bytes.Equal(buf, seed) {
   128  			return
   129  		}
   130  	})
   131  }
   132  -- check_testdata/check_testdata.go --
   133  //go:build ignore
   134  // +build ignore
   135  
   136  // check_testdata.go checks that the string written
   137  // is not longer than the provided length.
   138  package main
   139  
   140  import (
   141  	"fmt"
   142  	"io/ioutil"
   143  	"os"
   144  	"path/filepath"
   145  	"strconv"
   146  )
   147  
   148  func main() {
   149  	wantLen, err := strconv.Atoi(os.Args[2])
   150  	if err != nil {
   151  		fmt.Fprintln(os.Stderr, err)
   152  		os.Exit(1)
   153  	}
   154  	testName := os.Args[1]
   155  	dir := filepath.Join("testdata/fuzz", testName)
   156  
   157  	files, err := ioutil.ReadDir(dir)
   158  	if err != nil {
   159  		fmt.Fprintln(os.Stderr, err)
   160  		os.Exit(1)
   161  	}
   162  
   163  	if len(files) == 0 {
   164  		fmt.Fprintf(os.Stderr, "expect at least one failure to be written to testdata\n")
   165  		os.Exit(1)
   166  	}
   167  
   168  	fname := files[0].Name()
   169  	contents, err := ioutil.ReadFile(filepath.Join(dir, fname))
   170  	if err != nil {
   171  		fmt.Fprintln(os.Stderr, err)
   172  		os.Exit(1)
   173  	}
   174  	contentsLen := len(contents) - len(`go test fuzz v1
   175  string("")
   176  `)
   177  	if got, want := contentsLen, wantLen; got > want {
   178  		fmt.Fprintf(os.Stderr, "expect length <= %d, got %d\n", want, got)
   179  		os.Exit(1)
   180  	}
   181  	fmt.Fprintf(os.Stderr, "%s\n", contents)
   182  }
   183  
   184  -- check_cache/check_cache.go --
   185  //go:build ignore
   186  // +build ignore
   187  
   188  // check_cache.go checks that each file in the cached corpus has a []byte
   189  // of length at most 1. This verifies that at least one cached input is minimized.
   190  package main
   191  
   192  import (
   193  	"bytes"
   194  	"fmt"
   195  	"os"
   196  	"path/filepath"
   197  	"regexp"
   198  	"strconv"
   199  )
   200  
   201  func main() {
   202  	dir := os.Args[1]
   203  	ents, err := os.ReadDir(dir)
   204  	if err != nil {
   205  		fmt.Fprintln(os.Stderr, err)
   206  		os.Exit(1)
   207  	}
   208  	for _, ent := range ents {
   209  		name := filepath.Join(dir, ent.Name())
   210  		if good, err := checkCacheFile(name); err != nil {
   211  			fmt.Fprintln(os.Stderr, err)
   212  			os.Exit(1)
   213  		} else if good {
   214  			os.Exit(0)
   215  		}
   216  	}
   217  	fmt.Fprintln(os.Stderr, "no cached inputs were minimized")
   218  	os.Exit(1)
   219  }
   220  
   221  func checkCacheFile(name string) (good bool, err error) {
   222  	data, err := os.ReadFile(name)
   223  	if err != nil {
   224  		return false, err
   225  	}
   226  	for _, line := range bytes.Split(data, []byte("\n")) {
   227  		m := valRe.FindSubmatch(line)
   228  		if m == nil {
   229  			continue
   230  		}
   231  		if s, err := strconv.Unquote(string(m[1])); err != nil {
   232  			return false, err
   233  		} else if len(s) <= 1 {
   234  			return true, nil
   235  		}
   236  	}
   237  	return false, nil
   238  }
   239  
   240  var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
   241  

View as plain text