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  # Test that minimization occurs for a crash that appears while minimizing a
    22  # newly found interesting input. There must be only one worker for this test to
    23  # be flaky like we want.
    24  ! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerCrashInMinimization -test.run=^$ -test.fuzztime=10000x -test.parallel=1
    25  ! stdout '^ok'
    26  stdout -count=1 'got the minimum size!'
    27  stdout -count=1 'bad input'
    28  stdout FAIL
    29  # Check that the input written to testdata will reproduce the error, and is the
    30  # smallest possible.
    31  go run check_testdata/check_testdata.go FuzzMinimizerCrashInMinimization 1
    32  
    33  # Test that a nonrecoverable error that occurs while minimizing an interesting
    34  # input is reported correctly.
    35  ! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerNonrecoverableCrashInMinimization -test.run=^$ -test.fuzztime=10000x -test.parallel=1
    36  ! stdout '^ok'
    37  stdout -count=1 'fuzzing process hung or terminated unexpectedly while minimizing'
    38  stdout -count=1 'EOF'
    39  stdout FAIL
    40  # Check that the input written to testdata will reproduce the error.
    41  go run check_testdata/check_testdata.go FuzzMinimizerNonrecoverableCrashInMinimization 1
    42  
    43  -- go.mod --
    44  module fuzz
    45  
    46  go 1.17
    47  -- y.go --
    48  package fuzz
    49  
    50  import (
    51  	"bytes"
    52  	"io"
    53  )
    54  
    55  func Y(w io.Writer, s string) {
    56  	if !bytes.Equal([]byte(s), []byte("y")) {
    57  		w.Write([]byte("not equal"))
    58  	}
    59  }
    60  -- fuzz_test.go --
    61  package fuzz
    62  
    63  import (
    64  	"bytes"
    65  	"os"
    66  	"testing"
    67  )
    68  
    69  func FuzzMinimizerCrashInMinimization(f *testing.F) {
    70  	seed := bytes.Repeat([]byte{255}, 100)
    71  	f.Add(seed)
    72  	f.Fuzz(func(t *testing.T, b []byte) {
    73  		if bytes.Equal(seed, b) {
    74  			return
    75  		}
    76  		t.Error("bad input")
    77  		if len(b) == 1 {
    78  			t.Error("got the minimum size!")
    79  		}
    80  	})
    81  }
    82  
    83  var fuzzing bool
    84  
    85  func FuzzMinimizerNonrecoverableCrashInMinimization(f *testing.F) {
    86  	seed := bytes.Repeat([]byte{255}, 100)
    87  	f.Add(seed)
    88  	f.Fuzz(func(t *testing.T, b []byte) {
    89  		if bytes.Equal(seed, b) {
    90  			return
    91  		} else if len(b) == 1 {
    92  			os.Exit(1)
    93  		}
    94  	})
    95  }
    96  
    97  func FuzzMinCache(f *testing.F) {
    98  	seed := bytes.Repeat([]byte("a"), 20)
    99  	f.Add(seed)
   100  	f.Fuzz(func(t *testing.T, buf []byte) {
   101  		if bytes.Equal(buf, seed) {
   102  			return
   103  		}
   104  	})
   105  }
   106  -- check_testdata/check_testdata.go --
   107  //go:build ignore
   108  // +build ignore
   109  
   110  // check_testdata.go checks that the string written
   111  // is not longer than the provided length.
   112  package main
   113  
   114  import (
   115  	"bytes"
   116  	"fmt"
   117  	"io/ioutil"
   118  	"os"
   119  	"path/filepath"
   120  	"regexp"
   121  	"strconv"
   122  )
   123  
   124  func main() {
   125  	wantLen, err := strconv.Atoi(os.Args[2])
   126  	if err != nil {
   127  		fmt.Fprintln(os.Stderr, err)
   128  		os.Exit(1)
   129  	}
   130  	testName := os.Args[1]
   131  	dir := filepath.Join("testdata/fuzz", testName)
   132  
   133  	files, err := ioutil.ReadDir(dir)
   134  	if err != nil {
   135  		fmt.Fprintln(os.Stderr, err)
   136  		os.Exit(1)
   137  	}
   138  
   139  	if len(files) == 0 {
   140  		fmt.Fprintf(os.Stderr, "expect at least one failure to be written to testdata\n")
   141  		os.Exit(1)
   142  	}
   143  
   144  	for _, f := range files {
   145  		data, err := ioutil.ReadFile(filepath.Join(dir, f.Name()))
   146  		if err != nil {
   147  			panic(err)
   148  		}
   149  		var containsVal bool
   150  		for _, line := range bytes.Split(data, []byte("\n")) {
   151  			m := valRe.FindSubmatch(line)
   152  			if m == nil {
   153  				continue
   154  			}
   155  			containsVal = true
   156  			s, err := strconv.Unquote(string(m[1]))
   157  			if err != nil {
   158  				panic(err)
   159  			}
   160  			if len(s) != wantLen {
   161  				fmt.Fprintf(os.Stderr, "expect length %d, got %d (%q)\n", wantLen, len(s), line)
   162  				os.Exit(1)
   163  			}
   164  		}
   165  		if !containsVal {
   166  			fmt.Fprintln(os.Stderr, "corpus file contained no values")
   167  			os.Exit(1)
   168  		}
   169  	}
   170  }
   171  
   172  var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
   173  
   174  -- check_cache/check_cache.go --
   175  //go:build ignore
   176  // +build ignore
   177  
   178  // check_cache.go checks that each file in the cached corpus has a []byte
   179  // of length at most 1. This verifies that at least one cached input is minimized.
   180  package main
   181  
   182  import (
   183  	"bytes"
   184  	"fmt"
   185  	"os"
   186  	"path/filepath"
   187  	"regexp"
   188  	"strconv"
   189  )
   190  
   191  func main() {
   192  	dir := os.Args[1]
   193  	ents, err := os.ReadDir(dir)
   194  	if err != nil {
   195  		fmt.Fprintln(os.Stderr, err)
   196  		os.Exit(1)
   197  	}
   198  	for _, ent := range ents {
   199  		name := filepath.Join(dir, ent.Name())
   200  		if good, err := checkCacheFile(name); err != nil {
   201  			fmt.Fprintln(os.Stderr, err)
   202  			os.Exit(1)
   203  		} else if good {
   204  			os.Exit(0)
   205  		}
   206  	}
   207  	fmt.Fprintln(os.Stderr, "no cached inputs were minimized")
   208  	os.Exit(1)
   209  }
   210  
   211  func checkCacheFile(name string) (good bool, err error) {
   212  	data, err := os.ReadFile(name)
   213  	if err != nil {
   214  		return false, err
   215  	}
   216  	for _, line := range bytes.Split(data, []byte("\n")) {
   217  		m := valRe.FindSubmatch(line)
   218  		if m == nil {
   219  			continue
   220  		}
   221  		if s, err := strconv.Unquote(string(m[1])); err != nil {
   222  			return false, err
   223  		} else if len(s) <= 1 {
   224  			return true, nil
   225  		}
   226  	}
   227  	return false, nil
   228  }
   229  
   230  var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
   231  

View as plain text