github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/testing/fuzz.go (about)

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