Source file src/testing/fuzz.go

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package testing
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"flag"
    11  	"fmt"
    12  	"io"
    13  	"os"
    14  	"path/filepath"
    15  	"reflect"
    16  	"runtime"
    17  	"strings"
    18  	"time"
    19  )
    20  
    21  func initFuzzFlags() {
    22  	matchFuzz = flag.String("test.fuzz", "", "run the fuzz test matching `regexp`")
    23  	flag.Var(&fuzzDuration, "test.fuzztime", "time to spend fuzzing; default is to run indefinitely")
    24  	flag.Var(&minimizeDuration, "test.fuzzminimizetime", "time to spend minimizing a value after finding a failing input")
    25  
    26  	fuzzCacheDir = flag.String("test.fuzzcachedir", "", "directory where interesting fuzzing inputs are stored (for use only by cmd/go)")
    27  	isFuzzWorker = flag.Bool("test.fuzzworker", false, "coordinate with the parent process to fuzz random values (for use only by cmd/go)")
    28  }
    29  
    30  var (
    31  	matchFuzz        *string
    32  	fuzzDuration     durationOrCountFlag
    33  	minimizeDuration = durationOrCountFlag{d: 60 * time.Second, allowZero: true}
    34  	fuzzCacheDir     *string
    35  	isFuzzWorker     *bool
    36  
    37  	// corpusDir is the parent directory of the fuzz test's seed corpus within
    38  	// the package.
    39  	corpusDir = "testdata/fuzz"
    40  )
    41  
    42  // fuzzWorkerExitCode is used as an exit code by fuzz worker processes after an
    43  // internal error. This distinguishes internal errors from uncontrolled panics
    44  // and other failures. Keep in sync with internal/fuzz.workerExitCode.
    45  const fuzzWorkerExitCode = 70
    46  
    47  // InternalFuzzTarget is an internal type but exported because it is
    48  // cross-package; it is part of the implementation of the "go test" command.
    49  type InternalFuzzTarget struct {
    50  	Name string
    51  	Fn   func(f *F)
    52  }
    53  
    54  // F is a type passed to fuzz tests.
    55  //
    56  // Fuzz tests run generated inputs against a provided fuzz target, which can
    57  // find and report potential bugs in the code being tested.
    58  //
    59  // A fuzz test runs the seed corpus by default, which includes entries provided
    60  // by [F.Add] and entries in the testdata/fuzz/<FuzzTestName> directory. After
    61  // any necessary setup and calls to [F.Add], the fuzz test must then call
    62  // [F.Fuzz] to provide the fuzz target. See the testing package documentation
    63  // for an example, and see the [F.Fuzz] and [F.Add] method documentation for
    64  // details.
    65  //
    66  // *F methods can only be called before [F.Fuzz]. Once the test is
    67  // executing the fuzz target, only [*T] methods can be used. The only *F methods
    68  // that are allowed in the [F.Fuzz] function are [F.Failed] and [F.Name].
    69  type F struct {
    70  	common
    71  	fstate *fuzzState
    72  	tstate *testState
    73  
    74  	// inFuzzFn is true when the fuzz function is running. Most F methods cannot
    75  	// be called when inFuzzFn is true.
    76  	inFuzzFn bool
    77  
    78  	// corpus is a set of seed corpus entries, added with F.Add and loaded
    79  	// from testdata.
    80  	corpus []corpusEntry
    81  
    82  	result     fuzzResult
    83  	fuzzCalled bool
    84  }
    85  
    86  var _ TB = (*F)(nil)
    87  
    88  // corpusEntry is an alias to the same type as internal/fuzz.CorpusEntry.
    89  // We use a type alias because we don't want to export this type, and we can't
    90  // import internal/fuzz from testing.
    91  type corpusEntry = struct {
    92  	Parent     string
    93  	Path       string
    94  	Data       []byte
    95  	Values     []any
    96  	Generation int
    97  	IsSeed     bool
    98  }
    99  
   100  // Helper marks the calling function as a test helper function.
   101  // When printing file and line information, that function will be skipped.
   102  // Helper may be called simultaneously from multiple goroutines.
   103  func (f *F) Helper() {
   104  	if f.inFuzzFn {
   105  		panic("testing: f.Helper was called inside the fuzz target, use t.Helper instead")
   106  	}
   107  
   108  	// common.Helper is inlined here.
   109  	// If we called it, it would mark F.Helper as the helper
   110  	// instead of the caller.
   111  	f.mu.Lock()
   112  	defer f.mu.Unlock()
   113  	if f.helperPCs == nil {
   114  		f.helperPCs = make(map[uintptr]struct{})
   115  	}
   116  	// repeating code from callerName here to save walking a stack frame
   117  	var pc [1]uintptr
   118  	n := runtime.Callers(2, pc[:]) // skip runtime.Callers + Helper
   119  	if n == 0 {
   120  		panic("testing: zero callers found")
   121  	}
   122  	if _, found := f.helperPCs[pc[0]]; !found {
   123  		f.helperPCs[pc[0]] = struct{}{}
   124  		f.helperNames = nil // map will be recreated next time it is needed
   125  	}
   126  }
   127  
   128  // Fail marks the function as having failed but continues execution.
   129  func (f *F) Fail() {
   130  	// (*F).Fail may be called by (*T).Fail, which we should allow. However, we
   131  	// shouldn't allow direct (*F).Fail calls from inside the (*F).Fuzz function.
   132  	if f.inFuzzFn {
   133  		panic("testing: f.Fail was called inside the fuzz target, use t.Fail instead")
   134  	}
   135  	f.common.Helper()
   136  	f.common.Fail()
   137  }
   138  
   139  // Skipped reports whether the test was skipped.
   140  func (f *F) Skipped() bool {
   141  	// (*F).Skipped may be called by tRunner, which we should allow. However, we
   142  	// shouldn't allow direct (*F).Skipped calls from inside the (*F).Fuzz function.
   143  	if f.inFuzzFn {
   144  		panic("testing: f.Skipped was called inside the fuzz target, use t.Skipped instead")
   145  	}
   146  	f.common.Helper()
   147  	return f.common.Skipped()
   148  }
   149  
   150  // Add will add the arguments to the seed corpus for the fuzz test. This will be
   151  // a no-op if called after or within the fuzz target, and args must match the
   152  // arguments for the fuzz target.
   153  func (f *F) Add(args ...any) {
   154  	var values []any
   155  	for i := range args {
   156  		if t := reflect.TypeOf(args[i]); !supportedTypes[t] {
   157  			panic(fmt.Sprintf("testing: unsupported type to Add %v", t))
   158  		}
   159  		values = append(values, args[i])
   160  	}
   161  	f.corpus = append(f.corpus, corpusEntry{Values: values, IsSeed: true, Path: fmt.Sprintf("seed#%d", len(f.corpus))})
   162  }
   163  
   164  // supportedTypes represents all of the supported types which can be fuzzed.
   165  var supportedTypes = map[reflect.Type]bool{
   166  	reflect.TypeOf(([]byte)("")):  true,
   167  	reflect.TypeOf((string)("")):  true,
   168  	reflect.TypeOf((bool)(false)): true,
   169  	reflect.TypeOf((byte)(0)):     true,
   170  	reflect.TypeOf((rune)(0)):     true,
   171  	reflect.TypeOf((float32)(0)):  true,
   172  	reflect.TypeOf((float64)(0)):  true,
   173  	reflect.TypeOf((int)(0)):      true,
   174  	reflect.TypeOf((int8)(0)):     true,
   175  	reflect.TypeOf((int16)(0)):    true,
   176  	reflect.TypeOf((int32)(0)):    true,
   177  	reflect.TypeOf((int64)(0)):    true,
   178  	reflect.TypeOf((uint)(0)):     true,
   179  	reflect.TypeOf((uint8)(0)):    true,
   180  	reflect.TypeOf((uint16)(0)):   true,
   181  	reflect.TypeOf((uint32)(0)):   true,
   182  	reflect.TypeOf((uint64)(0)):   true,
   183  }
   184  
   185  // Fuzz runs the fuzz function, ff, for fuzz testing. If ff fails for a set of
   186  // arguments, those arguments will be added to the seed corpus.
   187  //
   188  // ff must be a function with no return value whose first argument is [*T] and
   189  // whose remaining arguments are the types to be fuzzed.
   190  // For example:
   191  //
   192  //	f.Fuzz(func(t *testing.T, b []byte, i int) { ... })
   193  //
   194  // The following types are allowed: []byte, string, bool, byte, rune, float32,
   195  // float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64.
   196  // More types may be supported in the future.
   197  //
   198  // ff must not call any [*F] methods, e.g. [F.Log], [F.Error], [F.Skip]. Use
   199  // the corresponding [*T] method instead. The only [*F] methods that are allowed in
   200  // the F.Fuzz function are [F.Failed] and [F.Name].
   201  //
   202  // This function should be fast and deterministic, and its behavior should not
   203  // depend on shared state. No mutable input arguments, or pointers to them,
   204  // should be retained between executions of the fuzz function, as the memory
   205  // backing them may be mutated during a subsequent invocation. ff must not
   206  // modify the underlying data of the arguments provided by the fuzzing engine.
   207  //
   208  // When fuzzing, F.Fuzz does not return until a problem is found, time runs out
   209  // (set with -fuzztime), or the test process is interrupted by a signal. F.Fuzz
   210  // should be called exactly once, unless [F.Skip] or [F.Fail] is called beforehand.
   211  func (f *F) Fuzz(ff any) {
   212  	if f.fuzzCalled {
   213  		panic("testing: F.Fuzz called more than once")
   214  	}
   215  	f.fuzzCalled = true
   216  	if f.failed {
   217  		return
   218  	}
   219  	f.Helper()
   220  
   221  	// ff should be in the form func(*testing.T, ...interface{})
   222  	fn := reflect.ValueOf(ff)
   223  	fnType := fn.Type()
   224  	if fnType.Kind() != reflect.Func {
   225  		panic("testing: F.Fuzz must receive a function")
   226  	}
   227  	if fnType.NumIn() < 2 || fnType.In(0) != reflect.TypeOf((*T)(nil)) {
   228  		panic("testing: fuzz target must receive at least two arguments, where the first argument is a *T")
   229  	}
   230  	if fnType.NumOut() != 0 {
   231  		panic("testing: fuzz target must not return a value")
   232  	}
   233  
   234  	// Save the types of the function to compare against the corpus.
   235  	var types []reflect.Type
   236  	for i := 1; i < fnType.NumIn(); i++ {
   237  		t := fnType.In(i)
   238  		if !supportedTypes[t] {
   239  			panic(fmt.Sprintf("testing: unsupported type for fuzzing %v", t))
   240  		}
   241  		types = append(types, t)
   242  	}
   243  
   244  	// Load the testdata seed corpus. Check types of entries in the testdata
   245  	// corpus and entries declared with F.Add.
   246  	//
   247  	// Don't load the seed corpus if this is a worker process; we won't use it.
   248  	if f.fstate.mode != fuzzWorker {
   249  		for _, c := range f.corpus {
   250  			if err := f.fstate.deps.CheckCorpus(c.Values, types); err != nil {
   251  				// TODO(#48302): Report the source location of the F.Add call.
   252  				f.Fatal(err)
   253  			}
   254  		}
   255  
   256  		// Load seed corpus
   257  		c, err := f.fstate.deps.ReadCorpus(filepath.Join(corpusDir, f.name), types)
   258  		if err != nil {
   259  			f.Fatal(err)
   260  		}
   261  		for i := range c {
   262  			c[i].IsSeed = true // these are all seed corpus values
   263  			if f.fstate.mode == fuzzCoordinator {
   264  				// If this is the coordinator process, zero the values, since we don't need
   265  				// to hold onto them.
   266  				c[i].Values = nil
   267  			}
   268  		}
   269  
   270  		f.corpus = append(f.corpus, c...)
   271  	}
   272  
   273  	// run calls fn on a given input, as a subtest with its own T.
   274  	// run is analogous to T.Run. The test filtering and cleanup works similarly.
   275  	// fn is called in its own goroutine.
   276  	run := func(captureOut io.Writer, e corpusEntry) (ok bool) {
   277  		if e.Values == nil {
   278  			// The corpusEntry must have non-nil Values in order to run the
   279  			// test. If Values is nil, it is a bug in our code.
   280  			panic(fmt.Sprintf("corpus file %q was not unmarshaled", e.Path))
   281  		}
   282  		if shouldFailFast() {
   283  			return true
   284  		}
   285  		testName := f.name
   286  		if e.Path != "" {
   287  			testName = fmt.Sprintf("%s/%s", testName, filepath.Base(e.Path))
   288  		}
   289  		if f.tstate.isFuzzing {
   290  			// Don't preserve subtest names while fuzzing. If fn calls T.Run,
   291  			// there will be a very large number of subtests with duplicate names,
   292  			// which will use a large amount of memory. The subtest names aren't
   293  			// useful since there's no way to re-run them deterministically.
   294  			f.tstate.match.clearSubNames()
   295  		}
   296  
   297  		ctx, cancelCtx := context.WithCancel(f.ctx)
   298  
   299  		// Record the stack trace at the point of this call so that if the subtest
   300  		// function - which runs in a separate stack - is marked as a helper, we can
   301  		// continue walking the stack into the parent test.
   302  		var pc [maxStackLen]uintptr
   303  		n := runtime.Callers(2, pc[:])
   304  		t := &T{
   305  			common: common{
   306  				barrier:   make(chan bool),
   307  				signal:    make(chan bool),
   308  				name:      testName,
   309  				parent:    &f.common,
   310  				level:     f.level + 1,
   311  				creator:   pc[:n],
   312  				chatty:    f.chatty,
   313  				ctx:       ctx,
   314  				cancelCtx: cancelCtx,
   315  			},
   316  			tstate: f.tstate,
   317  		}
   318  		if captureOut != nil {
   319  			// t.parent aliases f.common.
   320  			t.parent.w = captureOut
   321  		}
   322  		t.w = indenter{&t.common}
   323  		t.setOutputWriter()
   324  		if t.chatty != nil {
   325  			t.chatty.Updatef(t.name, "=== RUN   %s\n", t.name)
   326  		}
   327  		f.common.inFuzzFn, f.inFuzzFn = true, true
   328  		go tRunner(t, func(t *T) {
   329  			args := []reflect.Value{reflect.ValueOf(t)}
   330  			for _, v := range e.Values {
   331  				args = append(args, reflect.ValueOf(v))
   332  			}
   333  			// Before resetting the current coverage, defer the snapshot so that
   334  			// we make sure it is called right before the tRunner function
   335  			// exits, regardless of whether it was executed cleanly, panicked,
   336  			// or if the fuzzFn called t.Fatal.
   337  			if f.tstate.isFuzzing {
   338  				defer f.fstate.deps.SnapshotCoverage()
   339  				f.fstate.deps.ResetCoverage()
   340  			}
   341  			fn.Call(args)
   342  		})
   343  		<-t.signal
   344  		if t.chatty != nil && t.chatty.json {
   345  			t.chatty.Updatef(t.parent.name, "=== NAME  %s\n", t.parent.name)
   346  		}
   347  		f.common.inFuzzFn, f.inFuzzFn = false, false
   348  		return !t.Failed()
   349  	}
   350  
   351  	switch f.fstate.mode {
   352  	case fuzzCoordinator:
   353  		// Fuzzing is enabled, and this is the test process started by 'go test'.
   354  		// Act as the coordinator process, and coordinate workers to perform the
   355  		// actual fuzzing.
   356  		corpusTargetDir := filepath.Join(corpusDir, f.name)
   357  		cacheTargetDir := filepath.Join(*fuzzCacheDir, f.name)
   358  		err := f.fstate.deps.CoordinateFuzzing(
   359  			fuzzDuration.d,
   360  			int64(fuzzDuration.n),
   361  			minimizeDuration.d,
   362  			int64(minimizeDuration.n),
   363  			*parallel,
   364  			f.corpus,
   365  			types,
   366  			corpusTargetDir,
   367  			cacheTargetDir)
   368  		if err != nil {
   369  			f.result = fuzzResult{Error: err}
   370  			f.Fail()
   371  			fmt.Fprintf(f.w, "%v\n", err)
   372  			if crashErr, ok := err.(fuzzCrashError); ok {
   373  				crashPath := crashErr.CrashPath()
   374  				fmt.Fprintf(f.w, "Failing input written to %s\n", crashPath)
   375  				testName := filepath.Base(crashPath)
   376  				fmt.Fprintf(f.w, "To re-run:\ngo test -run=%s/%s\n", f.name, testName)
   377  			}
   378  		}
   379  		// TODO(jayconrod,katiehockman): Aggregate statistics across workers
   380  		// and add to FuzzResult (ie. time taken, num iterations)
   381  
   382  	case fuzzWorker:
   383  		// Fuzzing is enabled, and this is a worker process. Follow instructions
   384  		// from the coordinator.
   385  		if err := f.fstate.deps.RunFuzzWorker(func(e corpusEntry) error {
   386  			// Don't write to f.w (which points to Stdout) if running from a
   387  			// fuzz worker. This would become very verbose, particularly during
   388  			// minimization. Return the error instead, and let the caller deal
   389  			// with the output.
   390  			var buf strings.Builder
   391  			if ok := run(&buf, e); !ok {
   392  				return errors.New(buf.String())
   393  			}
   394  			return nil
   395  		}); err != nil {
   396  			// Internal errors are marked with f.Fail; user code may call this too, before F.Fuzz.
   397  			// The worker will exit with fuzzWorkerExitCode, indicating this is a failure
   398  			// (and 'go test' should exit non-zero) but a failing input should not be recorded.
   399  			f.Errorf("communicating with fuzzing coordinator: %v", err)
   400  		}
   401  
   402  	default:
   403  		// Fuzzing is not enabled, or will be done later. Only run the seed
   404  		// corpus now.
   405  		for _, e := range f.corpus {
   406  			name := fmt.Sprintf("%s/%s", f.name, filepath.Base(e.Path))
   407  			if _, ok, _ := f.tstate.match.fullName(nil, name); ok {
   408  				run(f.w, e)
   409  			}
   410  		}
   411  	}
   412  }
   413  
   414  func (f *F) report() {
   415  	if *isFuzzWorker || f.parent == nil {
   416  		return
   417  	}
   418  	dstr := fmtDuration(f.duration)
   419  	format := "--- %s: %s (%s)\n"
   420  	if f.Failed() {
   421  		f.flushToParent(f.name, format, "FAIL", f.name, dstr)
   422  	} else if f.chatty != nil {
   423  		if f.Skipped() {
   424  			f.flushToParent(f.name, format, "SKIP", f.name, dstr)
   425  		} else {
   426  			f.flushToParent(f.name, format, "PASS", f.name, dstr)
   427  		}
   428  	}
   429  }
   430  
   431  // fuzzResult contains the results of a fuzz run.
   432  type fuzzResult struct {
   433  	N     int           // The number of iterations.
   434  	T     time.Duration // The total time taken.
   435  	Error error         // Error is the error from the failing input
   436  }
   437  
   438  func (r fuzzResult) String() string {
   439  	if r.Error == nil {
   440  		return ""
   441  	}
   442  	return r.Error.Error()
   443  }
   444  
   445  // fuzzCrashError is satisfied by a failing input detected while fuzzing.
   446  // These errors are written to the seed corpus and can be re-run with 'go test'.
   447  // Errors within the fuzzing framework (like I/O errors between coordinator
   448  // and worker processes) don't satisfy this interface.
   449  type fuzzCrashError interface {
   450  	error
   451  	Unwrap() error
   452  
   453  	// CrashPath returns the path of the subtest that corresponds to the saved
   454  	// crash input file in the seed corpus. The test can be re-run with go test
   455  	// -run=$test/$name $test is the fuzz test name, and $name is the
   456  	// filepath.Base of the string returned here.
   457  	CrashPath() string
   458  }
   459  
   460  // fuzzState holds fields common to all fuzz tests.
   461  type fuzzState struct {
   462  	deps testDeps
   463  	mode fuzzMode
   464  }
   465  
   466  type fuzzMode uint8
   467  
   468  const (
   469  	seedCorpusOnly fuzzMode = iota
   470  	fuzzCoordinator
   471  	fuzzWorker
   472  )
   473  
   474  // runFuzzTests runs the fuzz tests matching the pattern for -run. This will
   475  // only run the (*F).Fuzz function for each seed corpus without using the
   476  // fuzzing engine to generate or mutate inputs.
   477  func runFuzzTests(deps testDeps, fuzzTests []InternalFuzzTarget, deadline time.Time) (ran, ok bool) {
   478  	ok = true
   479  	if len(fuzzTests) == 0 || *isFuzzWorker {
   480  		return ran, ok
   481  	}
   482  	m := newMatcher(deps.MatchString, *match, "-test.run", *skip)
   483  	var mFuzz *matcher
   484  	if *matchFuzz != "" {
   485  		mFuzz = newMatcher(deps.MatchString, *matchFuzz, "-test.fuzz", *skip)
   486  	}
   487  
   488  	for _, procs := range cpuList {
   489  		runtime.GOMAXPROCS(procs)
   490  		for i := uint(0); i < *count; i++ {
   491  			if shouldFailFast() {
   492  				break
   493  			}
   494  
   495  			tstate := newTestState(*parallel, m)
   496  			tstate.deadline = deadline
   497  			fstate := &fuzzState{deps: deps, mode: seedCorpusOnly}
   498  			root := common{w: os.Stdout} // gather output in one place
   499  			if Verbose() {
   500  				root.chatty = newChattyPrinter(root.w)
   501  			}
   502  			for _, ft := range fuzzTests {
   503  				if shouldFailFast() {
   504  					break
   505  				}
   506  				testName, matched, _ := tstate.match.fullName(nil, ft.Name)
   507  				if !matched {
   508  					continue
   509  				}
   510  				if mFuzz != nil {
   511  					if _, fuzzMatched, _ := mFuzz.fullName(nil, ft.Name); fuzzMatched {
   512  						// If this will be fuzzed, then don't run the seed corpus
   513  						// right now. That will happen later.
   514  						continue
   515  					}
   516  				}
   517  				ctx, cancelCtx := context.WithCancel(context.Background())
   518  				f := &F{
   519  					common: common{
   520  						signal:    make(chan bool),
   521  						barrier:   make(chan bool),
   522  						name:      testName,
   523  						parent:    &root,
   524  						level:     root.level + 1,
   525  						chatty:    root.chatty,
   526  						ctx:       ctx,
   527  						cancelCtx: cancelCtx,
   528  					},
   529  					tstate: tstate,
   530  					fstate: fstate,
   531  				}
   532  				f.w = indenter{&f.common}
   533  				f.setOutputWriter()
   534  				if f.chatty != nil {
   535  					f.chatty.Updatef(f.name, "=== RUN   %s\n", f.name)
   536  				}
   537  				go fRunner(f, ft.Fn)
   538  				<-f.signal
   539  				if f.chatty != nil && f.chatty.json {
   540  					f.chatty.Updatef(f.parent.name, "=== NAME  %s\n", f.parent.name)
   541  				}
   542  				ok = ok && !f.Failed()
   543  				ran = ran || f.ran
   544  			}
   545  			if !ran {
   546  				// There were no tests to run on this iteration.
   547  				// This won't change, so no reason to keep trying.
   548  				break
   549  			}
   550  		}
   551  	}
   552  
   553  	return ran, ok
   554  }
   555  
   556  // runFuzzing runs the fuzz test matching the pattern for -fuzz. Only one such
   557  // fuzz test must match. This will run the fuzzing engine to generate and
   558  // mutate new inputs against the fuzz target.
   559  //
   560  // If fuzzing is disabled (-test.fuzz is not set), runFuzzing
   561  // returns immediately.
   562  func runFuzzing(deps testDeps, fuzzTests []InternalFuzzTarget) (ok bool) {
   563  	if len(fuzzTests) == 0 || *matchFuzz == "" {
   564  		return true
   565  	}
   566  	m := newMatcher(deps.MatchString, *matchFuzz, "-test.fuzz", *skip)
   567  	tstate := newTestState(1, m)
   568  	tstate.isFuzzing = true
   569  	fstate := &fuzzState{
   570  		deps: deps,
   571  	}
   572  	root := common{w: os.Stdout}
   573  	if *isFuzzWorker {
   574  		root.w = io.Discard
   575  		fstate.mode = fuzzWorker
   576  	} else {
   577  		fstate.mode = fuzzCoordinator
   578  	}
   579  	if Verbose() && !*isFuzzWorker {
   580  		root.chatty = newChattyPrinter(root.w)
   581  	}
   582  	var fuzzTest *InternalFuzzTarget
   583  	var testName string
   584  	var matched []string
   585  	for i := range fuzzTests {
   586  		name, ok, _ := tstate.match.fullName(nil, fuzzTests[i].Name)
   587  		if !ok {
   588  			continue
   589  		}
   590  		matched = append(matched, name)
   591  		fuzzTest = &fuzzTests[i]
   592  		testName = name
   593  	}
   594  	if len(matched) == 0 {
   595  		fmt.Fprintln(os.Stderr, "testing: warning: no fuzz tests to fuzz")
   596  		return true
   597  	}
   598  	if len(matched) > 1 {
   599  		fmt.Fprintf(os.Stderr, "testing: will not fuzz, -fuzz matches more than one fuzz test: %v\n", matched)
   600  		return false
   601  	}
   602  
   603  	ctx, cancelCtx := context.WithCancel(context.Background())
   604  	f := &F{
   605  		common: common{
   606  			signal:    make(chan bool),
   607  			barrier:   nil, // T.Parallel has no effect when fuzzing.
   608  			name:      testName,
   609  			parent:    &root,
   610  			level:     root.level + 1,
   611  			chatty:    root.chatty,
   612  			ctx:       ctx,
   613  			cancelCtx: cancelCtx,
   614  		},
   615  		fstate: fstate,
   616  		tstate: tstate,
   617  	}
   618  	f.w = indenter{&f.common}
   619  	f.setOutputWriter()
   620  	if f.chatty != nil {
   621  		f.chatty.Updatef(f.name, "=== RUN   %s\n", f.name)
   622  	}
   623  	go fRunner(f, fuzzTest.Fn)
   624  	<-f.signal
   625  	if f.chatty != nil {
   626  		f.chatty.Updatef(f.parent.name, "=== NAME  %s\n", f.parent.name)
   627  	}
   628  	return !f.failed
   629  }
   630  
   631  // fRunner wraps a call to a fuzz test and ensures that cleanup functions are
   632  // called and status flags are set. fRunner should be called in its own
   633  // goroutine. To wait for its completion, receive from f.signal.
   634  //
   635  // fRunner is analogous to tRunner, which wraps subtests started with T.Run.
   636  // Unit tests and fuzz tests work a little differently, so for now, these
   637  // functions aren't consolidated. In particular, because there are no F.Run and
   638  // F.Parallel methods, i.e., no fuzz sub-tests or parallel fuzz tests, a few
   639  // simplifications are made. We also require that F.Fuzz, F.Skip, or F.Fail is
   640  // called.
   641  func fRunner(f *F, fn func(*F)) {
   642  	// When this goroutine is done, either because runtime.Goexit was called, a
   643  	// panic started, or fn returned normally, record the duration and send
   644  	// t.signal, indicating the fuzz test is done.
   645  	defer func() {
   646  		// Detect whether the fuzz test panicked or called runtime.Goexit
   647  		// without calling F.Fuzz, F.Fail, or F.Skip. If it did, panic (possibly
   648  		// replacing a nil panic value). Nothing should recover after fRunner
   649  		// unwinds, so this should crash the process and print stack.
   650  		// Unfortunately, recovering here adds stack frames, but the location of
   651  		// the original panic should still be
   652  		// clear.
   653  		f.checkRaces()
   654  		if f.Failed() {
   655  			numFailed.Add(1)
   656  		}
   657  		err := recover()
   658  		if err == nil {
   659  			f.mu.RLock()
   660  			fuzzNotCalled := !f.fuzzCalled && !f.skipped && !f.failed
   661  			if !f.finished && !f.skipped && !f.failed {
   662  				err = errNilPanicOrGoexit
   663  			}
   664  			f.mu.RUnlock()
   665  			if fuzzNotCalled && err == nil {
   666  				f.Error("returned without calling F.Fuzz, F.Fail, or F.Skip")
   667  			}
   668  		}
   669  
   670  		// Use a deferred call to ensure that we report that the test is
   671  		// complete even if a cleanup function calls F.FailNow. See issue 41355.
   672  		didPanic := false
   673  		defer func() {
   674  			if !didPanic {
   675  				// Only report that the test is complete if it doesn't panic,
   676  				// as otherwise the test binary can exit before the panic is
   677  				// reported to the user. See issue 41479.
   678  				f.signal <- true
   679  			}
   680  		}()
   681  
   682  		// If we recovered a panic or inappropriate runtime.Goexit, fail the test,
   683  		// flush the output log up to the root, then panic.
   684  		doPanic := func(err any) {
   685  			f.Fail()
   686  			if r := f.runCleanup(recoverAndReturnPanic); r != nil {
   687  				f.Logf("cleanup panicked with %v", r)
   688  			}
   689  			for root := &f.common; root.parent != nil; root = root.parent {
   690  				root.mu.Lock()
   691  				root.duration += highPrecisionTimeSince(root.start)
   692  				d := root.duration
   693  				root.mu.Unlock()
   694  				root.flushToParent(root.name, "--- FAIL: %s (%s)\n", root.name, fmtDuration(d))
   695  			}
   696  			didPanic = true
   697  			panic(err)
   698  		}
   699  		if err != nil {
   700  			doPanic(err)
   701  		}
   702  
   703  		// No panic or inappropriate Goexit.
   704  		f.duration += highPrecisionTimeSince(f.start)
   705  
   706  		if len(f.sub) > 0 {
   707  			// Unblock inputs that called T.Parallel while running the seed corpus.
   708  			// This only affects fuzz tests run as normal tests.
   709  			// While fuzzing, T.Parallel has no effect, so f.sub is empty, and this
   710  			// branch is not taken. f.barrier is nil in that case.
   711  			f.tstate.release()
   712  			close(f.barrier)
   713  			// Wait for the subtests to complete.
   714  			for _, sub := range f.sub {
   715  				<-sub.signal
   716  			}
   717  			cleanupStart := highPrecisionTimeNow()
   718  			err := f.runCleanup(recoverAndReturnPanic)
   719  			f.duration += highPrecisionTimeSince(cleanupStart)
   720  			if err != nil {
   721  				doPanic(err)
   722  			}
   723  		}
   724  
   725  		// Report after all subtests have finished.
   726  		f.report()
   727  		f.done = true
   728  		f.setRan()
   729  	}()
   730  	defer func() {
   731  		if len(f.sub) == 0 {
   732  			f.runCleanup(normalPanic)
   733  		}
   734  	}()
   735  
   736  	f.start = highPrecisionTimeNow()
   737  	f.resetRaces()
   738  	fn(f)
   739  
   740  	// Code beyond this point will not be executed when FailNow or SkipNow
   741  	// is invoked.
   742  	f.mu.Lock()
   743  	f.finished = true
   744  	f.mu.Unlock()
   745  }
   746  

View as plain text