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 fuzz provides common fuzzing functionality for tests built with
6// "go test" and for programs that use fuzzing functionality in the testing
7// package.
8package fuzz
9
10import (
11	"bytes"
12	"context"
13	"crypto/sha256"
14	"errors"
15	"fmt"
16	"internal/godebug"
17	"io"
18	"math/bits"
19	"os"
20	"path/filepath"
21	"reflect"
22	"runtime"
23	"strings"
24	"time"
25)
26
27// CoordinateFuzzingOpts is a set of arguments for CoordinateFuzzing.
28// The zero value is valid for each field unless specified otherwise.
29type CoordinateFuzzingOpts struct {
30	// Log is a writer for logging progress messages and warnings.
31	// If nil, io.Discard will be used instead.
32	Log io.Writer
33
34	// Timeout is the amount of wall clock time to spend fuzzing after the corpus
35	// has loaded. If zero, there will be no time limit.
36	Timeout time.Duration
37
38	// Limit is the number of random values to generate and test. If zero,
39	// there will be no limit on the number of generated values.
40	Limit int64
41
42	// MinimizeTimeout is the amount of wall clock time to spend minimizing
43	// after discovering a crasher. If zero, there will be no time limit. If
44	// MinimizeTimeout and MinimizeLimit are both zero, then minimization will
45	// be disabled.
46	MinimizeTimeout time.Duration
47
48	// MinimizeLimit is the maximum number of calls to the fuzz function to be
49	// made while minimizing after finding a crash. If zero, there will be no
50	// limit. Calls to the fuzz function made when minimizing also count toward
51	// Limit. If MinimizeTimeout and MinimizeLimit are both zero, then
52	// minimization will be disabled.
53	MinimizeLimit int64
54
55	// parallel is the number of worker processes to run in parallel. If zero,
56	// CoordinateFuzzing will run GOMAXPROCS workers.
57	Parallel int
58
59	// Seed is a list of seed values added by the fuzz target with testing.F.Add
60	// and in testdata.
61	Seed []CorpusEntry
62
63	// Types is the list of types which make up a corpus entry.
64	// Types must be set and must match values in Seed.
65	Types []reflect.Type
66
67	// CorpusDir is a directory where files containing values that crash the
68	// code being tested may be written. CorpusDir must be set.
69	CorpusDir string
70
71	// CacheDir is a directory containing additional "interesting" values.
72	// The fuzzer may derive new values from these, and may write new values here.
73	CacheDir string
74}
75
76// CoordinateFuzzing creates several worker processes and communicates with
77// them to test random inputs that could trigger crashes and expose bugs.
78// The worker processes run the same binary in the same directory with the
79// same environment variables as the coordinator process. Workers also run
80// with the same arguments as the coordinator, except with the -test.fuzzworker
81// flag prepended to the argument list.
82//
83// If a crash occurs, the function will return an error containing information
84// about the crash, which can be reported to the user.
85func CoordinateFuzzing(ctx context.Context, opts CoordinateFuzzingOpts) (err error) {
86	if err := ctx.Err(); err != nil {
87		return err
88	}
89	if opts.Log == nil {
90		opts.Log = io.Discard
91	}
92	if opts.Parallel == 0 {
93		opts.Parallel = runtime.GOMAXPROCS(0)
94	}
95	if opts.Limit > 0 && int64(opts.Parallel) > opts.Limit {
96		// Don't start more workers than we need.
97		opts.Parallel = int(opts.Limit)
98	}
99
100	c, err := newCoordinator(opts)
101	if err != nil {
102		return err
103	}
104
105	if opts.Timeout > 0 {
106		var cancel func()
107		ctx, cancel = context.WithTimeout(ctx, opts.Timeout)
108		defer cancel()
109	}
110
111	// fuzzCtx is used to stop workers, for example, after finding a crasher.
112	fuzzCtx, cancelWorkers := context.WithCancel(ctx)
113	defer cancelWorkers()
114	doneC := ctx.Done()
115
116	// stop is called when a worker encounters a fatal error.
117	var fuzzErr error
118	stopping := false
119	stop := func(err error) {
120		if shouldPrintDebugInfo() {
121			_, file, line, ok := runtime.Caller(1)
122			if ok {
123				c.debugLogf("stop called at %s:%d. stopping: %t", file, line, stopping)
124			} else {
125				c.debugLogf("stop called at unknown. stopping: %t", stopping)
126			}
127		}
128
129		if err == fuzzCtx.Err() || isInterruptError(err) {
130			// Suppress cancellation errors and terminations due to SIGINT.
131			// The messages are not helpful since either the user triggered the error
132			// (with ^C) or another more helpful message will be printed (a crasher).
133			err = nil
134		}
135		if err != nil && (fuzzErr == nil || fuzzErr == ctx.Err()) {
136			fuzzErr = err
137		}
138		if stopping {
139			return
140		}
141		stopping = true
142		cancelWorkers()
143		doneC = nil
144	}
145
146	// Ensure that any crash we find is written to the corpus, even if an error
147	// or interruption occurs while minimizing it.
148	crashWritten := false
149	defer func() {
150		if c.crashMinimizing == nil || crashWritten {
151			return
152		}
153		werr := writeToCorpus(&c.crashMinimizing.entry, opts.CorpusDir)
154		if werr != nil {
155			err = fmt.Errorf("%w\n%v", err, werr)
156			return
157		}
158		if err == nil {
159			err = &crashError{
160				path: c.crashMinimizing.entry.Path,
161				err:  errors.New(c.crashMinimizing.crasherMsg),
162			}
163		}
164	}()
165
166	// Start workers.
167	// TODO(jayconrod): do we want to support fuzzing different binaries?
168	dir := "" // same as self
169	binPath := os.Args[0]
170	args := append([]string{"-test.fuzzworker"}, os.Args[1:]...)
171	env := os.Environ() // same as self
172
173	errC := make(chan error)
174	workers := make([]*worker, opts.Parallel)
175	for i := range workers {
176		var err error
177		workers[i], err = newWorker(c, dir, binPath, args, env)
178		if err != nil {
179			return err
180		}
181	}
182	for i := range workers {
183		w := workers[i]
184		go func() {
185			err := w.coordinate(fuzzCtx)
186			if fuzzCtx.Err() != nil || isInterruptError(err) {
187				err = nil
188			}
189			cleanErr := w.cleanup()
190			if err == nil {
191				err = cleanErr
192			}
193			errC <- err
194		}()
195	}
196
197	// Main event loop.
198	// Do not return until all workers have terminated. We avoid a deadlock by
199	// receiving messages from workers even after ctx is canceled.
200	activeWorkers := len(workers)
201	statTicker := time.NewTicker(3 * time.Second)
202	defer statTicker.Stop()
203	defer c.logStats()
204
205	c.logStats()
206	for {
207		// If there is an execution limit, and we've reached it, stop.
208		if c.opts.Limit > 0 && c.count >= c.opts.Limit {
209			stop(nil)
210		}
211
212		var inputC chan fuzzInput
213		input, ok := c.peekInput()
214		if ok && c.crashMinimizing == nil && !stopping {
215			inputC = c.inputC
216		}
217
218		var minimizeC chan fuzzMinimizeInput
219		minimizeInput, ok := c.peekMinimizeInput()
220		if ok && !stopping {
221			minimizeC = c.minimizeC
222		}
223
224		select {
225		case <-doneC:
226			// Interrupted, canceled, or timed out.
227			// stop sets doneC to nil, so we don't busy wait here.
228			stop(ctx.Err())
229
230		case err := <-errC:
231			// A worker terminated, possibly after encountering a fatal error.
232			stop(err)
233			activeWorkers--
234			if activeWorkers == 0 {
235				return fuzzErr
236			}
237
238		case result := <-c.resultC:
239			// Received response from worker.
240			if stopping {
241				break
242			}
243			c.updateStats(result)
244
245			if result.crasherMsg != "" {
246				if c.warmupRun() && result.entry.IsSeed {
247					target := filepath.Base(c.opts.CorpusDir)
248					fmt.Fprintf(c.opts.Log, "failure while testing seed corpus entry: %s/%s\n", target, testName(result.entry.Parent))
249					stop(errors.New(result.crasherMsg))
250					break
251				}
252				if c.canMinimize() && result.canMinimize {
253					if c.crashMinimizing != nil {
254						// This crash is not minimized, and another crash is being minimized.
255						// Ignore this one and wait for the other one to finish.
256						if shouldPrintDebugInfo() {
257							c.debugLogf("found unminimized crasher, skipping in favor of minimizable crasher")
258						}
259						break
260					}
261					// Found a crasher but haven't yet attempted to minimize it.
262					// Send it back to a worker for minimization. Disable inputC so
263					// other workers don't continue fuzzing.
264					c.crashMinimizing = &result
265					fmt.Fprintf(c.opts.Log, "fuzz: minimizing %d-byte failing input file\n", len(result.entry.Data))
266					c.queueForMinimization(result, nil)
267				} else if !crashWritten {
268					// Found a crasher that's either minimized or not minimizable.
269					// Write to corpus and stop.
270					err := writeToCorpus(&result.entry, opts.CorpusDir)
271					if err == nil {
272						crashWritten = true
273						err = &crashError{
274							path: result.entry.Path,
275							err:  errors.New(result.crasherMsg),
276						}
277					}
278					if shouldPrintDebugInfo() {
279						c.debugLogf(
280							"found crasher, id: %s, parent: %s, gen: %d, size: %d, exec time: %s",
281							result.entry.Path,
282							result.entry.Parent,
283							result.entry.Generation,
284							len(result.entry.Data),
285							result.entryDuration,
286						)
287					}
288					stop(err)
289				}
290			} else if result.coverageData != nil {
291				if c.warmupRun() {
292					if shouldPrintDebugInfo() {
293						c.debugLogf(
294							"processed an initial input, id: %s, new bits: %d, size: %d, exec time: %s",
295							result.entry.Parent,
296							countBits(diffCoverage(c.coverageMask, result.coverageData)),
297							len(result.entry.Data),
298							result.entryDuration,
299						)
300					}
301					c.updateCoverage(result.coverageData)
302					c.warmupInputLeft--
303					if c.warmupInputLeft == 0 {
304						fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, gathering baseline coverage: %d/%d completed, now fuzzing with %d workers\n", c.elapsed(), c.warmupInputCount, c.warmupInputCount, c.opts.Parallel)
305						if shouldPrintDebugInfo() {
306							c.debugLogf(
307								"finished processing input corpus, entries: %d, initial coverage bits: %d",
308								len(c.corpus.entries),
309								countBits(c.coverageMask),
310							)
311						}
312					}
313				} else if keepCoverage := diffCoverage(c.coverageMask, result.coverageData); keepCoverage != nil {
314					// Found a value that expanded coverage.
315					// It's not a crasher, but we may want to add it to the on-disk
316					// corpus and prioritize it for future fuzzing.
317					// TODO(jayconrod, katiehockman): Prioritize fuzzing these
318					// values which expanded coverage, perhaps based on the
319					// number of new edges that this result expanded.
320					// TODO(jayconrod, katiehockman): Don't write a value that's already
321					// in the corpus.
322					if c.canMinimize() && result.canMinimize && c.crashMinimizing == nil {
323						// Send back to workers to find a smaller value that preserves
324						// at least one new coverage bit.
325						c.queueForMinimization(result, keepCoverage)
326					} else {
327						// Update the coordinator's coverage mask and save the value.
328						inputSize := len(result.entry.Data)
329						entryNew, err := c.addCorpusEntries(true, result.entry)
330						if err != nil {
331							stop(err)
332							break
333						}
334						if !entryNew {
335							if shouldPrintDebugInfo() {
336								c.debugLogf(
337									"ignoring duplicate input which increased coverage, id: %s",
338									result.entry.Path,
339								)
340							}
341							break
342						}
343						c.updateCoverage(keepCoverage)
344						c.inputQueue.enqueue(result.entry)
345						c.interestingCount++
346						if shouldPrintDebugInfo() {
347							c.debugLogf(
348								"new interesting input, id: %s, parent: %s, gen: %d, new bits: %d, total bits: %d, size: %d, exec time: %s",
349								result.entry.Path,
350								result.entry.Parent,
351								result.entry.Generation,
352								countBits(keepCoverage),
353								countBits(c.coverageMask),
354								inputSize,
355								result.entryDuration,
356							)
357						}
358					}
359				} else {
360					if shouldPrintDebugInfo() {
361						c.debugLogf(
362							"worker reported interesting input that doesn't expand coverage, id: %s, parent: %s, canMinimize: %t",
363							result.entry.Path,
364							result.entry.Parent,
365							result.canMinimize,
366						)
367					}
368				}
369			} else if c.warmupRun() {
370				// No error or coverage data was reported for this input during
371				// warmup, so continue processing results.
372				c.warmupInputLeft--
373				if c.warmupInputLeft == 0 {
374					fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, testing seed corpus: %d/%d completed, now fuzzing with %d workers\n", c.elapsed(), c.warmupInputCount, c.warmupInputCount, c.opts.Parallel)
375					if shouldPrintDebugInfo() {
376						c.debugLogf(
377							"finished testing-only phase, entries: %d",
378							len(c.corpus.entries),
379						)
380					}
381				}
382			}
383
384		case inputC <- input:
385			// Sent the next input to a worker.
386			c.sentInput(input)
387
388		case minimizeC <- minimizeInput:
389			// Sent the next input for minimization to a worker.
390			c.sentMinimizeInput(minimizeInput)
391
392		case <-statTicker.C:
393			c.logStats()
394		}
395	}
396
397	// TODO(jayconrod,katiehockman): if a crasher can't be written to the corpus,
398	// write to the cache instead.
399}
400
401// crashError wraps a crasher written to the seed corpus. It saves the name
402// of the file where the input causing the crasher was saved. The testing
403// framework uses this to report a command to re-run that specific input.
404type crashError struct {
405	path string
406	err  error
407}
408
409func (e *crashError) Error() string {
410	return e.err.Error()
411}
412
413func (e *crashError) Unwrap() error {
414	return e.err
415}
416
417func (e *crashError) CrashPath() string {
418	return e.path
419}
420
421type corpus struct {
422	entries []CorpusEntry
423	hashes  map[[sha256.Size]byte]bool
424}
425
426// addCorpusEntries adds entries to the corpus, and optionally writes the entries
427// to the cache directory. If an entry is already in the corpus it is skipped. If
428// all of the entries are unique, addCorpusEntries returns true and a nil error,
429// if at least one of the entries was a duplicate, it returns false and a nil error.
430func (c *coordinator) addCorpusEntries(addToCache bool, entries ...CorpusEntry) (bool, error) {
431	noDupes := true
432	for _, e := range entries {
433		data, err := corpusEntryData(e)
434		if err != nil {
435			return false, err
436		}
437		h := sha256.Sum256(data)
438		if c.corpus.hashes[h] {
439			noDupes = false
440			continue
441		}
442		if addToCache {
443			if err := writeToCorpus(&e, c.opts.CacheDir); err != nil {
444				return false, err
445			}
446			// For entries written to disk, we don't hold onto the bytes,
447			// since the corpus would consume a significant amount of
448			// memory.
449			e.Data = nil
450		}
451		c.corpus.hashes[h] = true
452		c.corpus.entries = append(c.corpus.entries, e)
453	}
454	return noDupes, nil
455}
456
457// CorpusEntry represents an individual input for fuzzing.
458//
459// We must use an equivalent type in the testing and testing/internal/testdeps
460// packages, but testing can't import this package directly, and we don't want
461// to export this type from testing. Instead, we use the same struct type and
462// use a type alias (not a defined type) for convenience.
463type CorpusEntry = struct {
464	Parent string
465
466	// Path is the path of the corpus file, if the entry was loaded from disk.
467	// For other entries, including seed values provided by f.Add, Path is the
468	// name of the test, e.g. seed#0 or its hash.
469	Path string
470
471	// Data is the raw input data. Data should only be populated for seed
472	// values. For on-disk corpus files, Data will be nil, as it will be loaded
473	// from disk using Path.
474	Data []byte
475
476	// Values is the unmarshaled values from a corpus file.
477	Values []any
478
479	Generation int
480
481	// IsSeed indicates whether this entry is part of the seed corpus.
482	IsSeed bool
483}
484
485// corpusEntryData returns the raw input bytes, either from the data struct
486// field, or from disk.
487func corpusEntryData(ce CorpusEntry) ([]byte, error) {
488	if ce.Data != nil {
489		return ce.Data, nil
490	}
491
492	return os.ReadFile(ce.Path)
493}
494
495type fuzzInput struct {
496	// entry is the value to test initially. The worker will randomly mutate
497	// values from this starting point.
498	entry CorpusEntry
499
500	// timeout is the time to spend fuzzing variations of this input,
501	// not including starting or cleaning up.
502	timeout time.Duration
503
504	// limit is the maximum number of calls to the fuzz function the worker may
505	// make. The worker may make fewer calls, for example, if it finds an
506	// error early. If limit is zero, there is no limit on calls to the
507	// fuzz function.
508	limit int64
509
510	// warmup indicates whether this is a warmup input before fuzzing begins. If
511	// true, the input should not be fuzzed.
512	warmup bool
513
514	// coverageData reflects the coordinator's current coverageMask.
515	coverageData []byte
516}
517
518type fuzzResult struct {
519	// entry is an interesting value or a crasher.
520	entry CorpusEntry
521
522	// crasherMsg is an error message from a crash. It's "" if no crash was found.
523	crasherMsg string
524
525	// canMinimize is true if the worker should attempt to minimize this result.
526	// It may be false because an attempt has already been made.
527	canMinimize bool
528
529	// coverageData is set if the worker found new coverage.
530	coverageData []byte
531
532	// limit is the number of values the coordinator asked the worker
533	// to test. 0 if there was no limit.
534	limit int64
535
536	// count is the number of values the worker actually tested.
537	count int64
538
539	// totalDuration is the time the worker spent testing inputs.
540	totalDuration time.Duration
541
542	// entryDuration is the time the worker spent execution an interesting result
543	entryDuration time.Duration
544}
545
546type fuzzMinimizeInput struct {
547	// entry is an interesting value or crasher to minimize.
548	entry CorpusEntry
549
550	// crasherMsg is an error message from a crash. It's "" if no crash was found.
551	// If set, the worker will attempt to find a smaller input that also produces
552	// an error, though not necessarily the same error.
553	crasherMsg string
554
555	// limit is the maximum number of calls to the fuzz function the worker may
556	// make. The worker may make fewer calls, for example, if it can't reproduce
557	// an error. If limit is zero, there is no limit on calls to the fuzz function.
558	limit int64
559
560	// timeout is the time to spend minimizing this input.
561	// A zero timeout means no limit.
562	timeout time.Duration
563
564	// keepCoverage is a set of coverage bits that entry found that were not in
565	// the coordinator's combined set. When minimizing, the worker should find an
566	// input that preserves at least one of these bits. keepCoverage is nil for
567	// crashing inputs.
568	keepCoverage []byte
569}
570
571// coordinator holds channels that workers can use to communicate with
572// the coordinator.
573type coordinator struct {
574	opts CoordinateFuzzingOpts
575
576	// startTime is the time we started the workers after loading the corpus.
577	// Used for logging.
578	startTime time.Time
579
580	// inputC is sent values to fuzz by the coordinator. Any worker may receive
581	// values from this channel. Workers send results to resultC.
582	inputC chan fuzzInput
583
584	// minimizeC is sent values to minimize by the coordinator. Any worker may
585	// receive values from this channel. Workers send results to resultC.
586	minimizeC chan fuzzMinimizeInput
587
588	// resultC is sent results of fuzzing by workers. The coordinator
589	// receives these. Multiple types of messages are allowed.
590	resultC chan fuzzResult
591
592	// count is the number of values fuzzed so far.
593	count int64
594
595	// countLastLog is the number of values fuzzed when the output was last
596	// logged.
597	countLastLog int64
598
599	// timeLastLog is the time at which the output was last logged.
600	timeLastLog time.Time
601
602	// interestingCount is the number of unique interesting values which have
603	// been found this execution.
604	interestingCount int
605
606	// warmupInputCount is the count of all entries in the corpus which will
607	// need to be received from workers to run once during warmup, but not fuzz.
608	// This could be for coverage data, or only for the purposes of verifying
609	// that the seed corpus doesn't have any crashers. See warmupRun.
610	warmupInputCount int
611
612	// warmupInputLeft is the number of entries in the corpus which still need
613	// to be received from workers to run once during warmup, but not fuzz.
614	// See warmupInputLeft.
615	warmupInputLeft int
616
617	// duration is the time spent fuzzing inside workers, not counting time
618	// starting up or tearing down.
619	duration time.Duration
620
621	// countWaiting is the number of fuzzing executions the coordinator is
622	// waiting on workers to complete.
623	countWaiting int64
624
625	// corpus is a set of interesting values, including the seed corpus and
626	// generated values that workers reported as interesting.
627	corpus corpus
628
629	// minimizationAllowed is true if one or more of the types of fuzz
630	// function's parameters can be minimized.
631	minimizationAllowed bool
632
633	// inputQueue is a queue of inputs that workers should try fuzzing. This is
634	// initially populated from the seed corpus and cached inputs. More inputs
635	// may be added as new coverage is discovered.
636	inputQueue queue
637
638	// minimizeQueue is a queue of inputs that caused errors or exposed new
639	// coverage. Workers should attempt to find smaller inputs that do the
640	// same thing.
641	minimizeQueue queue
642
643	// crashMinimizing is the crash that is currently being minimized.
644	crashMinimizing *fuzzResult
645
646	// coverageMask aggregates coverage that was found for all inputs in the
647	// corpus. Each byte represents a single basic execution block. Each set bit
648	// within the byte indicates that an input has triggered that block at least
649	// 1 << n times, where n is the position of the bit in the byte. For example, a
650	// value of 12 indicates that separate inputs have triggered this block
651	// between 4-7 times and 8-15 times.
652	coverageMask []byte
653}
654
655func newCoordinator(opts CoordinateFuzzingOpts) (*coordinator, error) {
656	// Make sure all the seed corpus has marshaled data.
657	for i := range opts.Seed {
658		if opts.Seed[i].Data == nil && opts.Seed[i].Values != nil {
659			opts.Seed[i].Data = marshalCorpusFile(opts.Seed[i].Values...)
660		}
661	}
662	c := &coordinator{
663		opts:        opts,
664		startTime:   time.Now(),
665		inputC:      make(chan fuzzInput),
666		minimizeC:   make(chan fuzzMinimizeInput),
667		resultC:     make(chan fuzzResult),
668		timeLastLog: time.Now(),
669		corpus:      corpus{hashes: make(map[[sha256.Size]byte]bool)},
670	}
671	if err := c.readCache(); err != nil {
672		return nil, err
673	}
674	if opts.MinimizeLimit > 0 || opts.MinimizeTimeout > 0 {
675		for _, t := range opts.Types {
676			if isMinimizable(t) {
677				c.minimizationAllowed = true
678				break
679			}
680		}
681	}
682
683	covSize := len(coverage())
684	if covSize == 0 {
685		fmt.Fprintf(c.opts.Log, "warning: the test binary was not built with coverage instrumentation, so fuzzing will run without coverage guidance and may be inefficient\n")
686		// Even though a coverage-only run won't occur, we should still run all
687		// of the seed corpus to make sure there are no existing failures before
688		// we start fuzzing.
689		c.warmupInputCount = len(c.opts.Seed)
690		for _, e := range c.opts.Seed {
691			c.inputQueue.enqueue(e)
692		}
693	} else {
694		c.warmupInputCount = len(c.corpus.entries)
695		for _, e := range c.corpus.entries {
696			c.inputQueue.enqueue(e)
697		}
698		// Set c.coverageMask to a clean []byte full of zeros.
699		c.coverageMask = make([]byte, covSize)
700	}
701	c.warmupInputLeft = c.warmupInputCount
702
703	if len(c.corpus.entries) == 0 {
704		fmt.Fprintf(c.opts.Log, "warning: starting with empty corpus\n")
705		var vals []any
706		for _, t := range opts.Types {
707			vals = append(vals, zeroValue(t))
708		}
709		data := marshalCorpusFile(vals...)
710		h := sha256.Sum256(data)
711		name := fmt.Sprintf("%x", h[:4])
712		c.addCorpusEntries(false, CorpusEntry{Path: name, Data: data})
713	}
714
715	return c, nil
716}
717
718func (c *coordinator) updateStats(result fuzzResult) {
719	c.count += result.count
720	c.countWaiting -= result.limit
721	c.duration += result.totalDuration
722}
723
724func (c *coordinator) logStats() {
725	now := time.Now()
726	if c.warmupRun() {
727		runSoFar := c.warmupInputCount - c.warmupInputLeft
728		if coverageEnabled {
729			fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, gathering baseline coverage: %d/%d completed\n", c.elapsed(), runSoFar, c.warmupInputCount)
730		} else {
731			fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, testing seed corpus: %d/%d completed\n", c.elapsed(), runSoFar, c.warmupInputCount)
732		}
733	} else if c.crashMinimizing != nil {
734		fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, minimizing\n", c.elapsed())
735	} else {
736		rate := float64(c.count-c.countLastLog) / now.Sub(c.timeLastLog).Seconds()
737		if coverageEnabled {
738			total := c.warmupInputCount + c.interestingCount
739			fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, execs: %d (%.0f/sec), new interesting: %d (total: %d)\n", c.elapsed(), c.count, rate, c.interestingCount, total)
740		} else {
741			fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, execs: %d (%.0f/sec)\n", c.elapsed(), c.count, rate)
742		}
743	}
744	c.countLastLog = c.count
745	c.timeLastLog = now
746}
747
748// peekInput returns the next value that should be sent to workers.
749// If the number of executions is limited, the returned value includes
750// a limit for one worker. If there are no executions left, peekInput returns
751// a zero value and false.
752//
753// peekInput doesn't actually remove the input from the queue. The caller
754// must call sentInput after sending the input.
755//
756// If the input queue is empty and the coverage/testing-only run has completed,
757// queue refills it from the corpus.
758func (c *coordinator) peekInput() (fuzzInput, bool) {
759	if c.opts.Limit > 0 && c.count+c.countWaiting >= c.opts.Limit {
760		// Already making the maximum number of calls to the fuzz function.
761		// Don't send more inputs right now.
762		return fuzzInput{}, false
763	}
764	if c.inputQueue.len == 0 {
765		if c.warmupRun() {
766			// Wait for coverage/testing-only run to finish before sending more
767			// inputs.
768			return fuzzInput{}, false
769		}
770		c.refillInputQueue()
771	}
772
773	entry, ok := c.inputQueue.peek()
774	if !ok {
775		panic("input queue empty after refill")
776	}
777	input := fuzzInput{
778		entry:   entry.(CorpusEntry),
779		timeout: workerFuzzDuration,
780		warmup:  c.warmupRun(),
781	}
782	if c.coverageMask != nil {
783		input.coverageData = bytes.Clone(c.coverageMask)
784	}
785	if input.warmup {
786		// No fuzzing will occur, but it should count toward the limit set by
787		// -fuzztime.
788		input.limit = 1
789		return input, true
790	}
791
792	if c.opts.Limit > 0 {
793		input.limit = c.opts.Limit / int64(c.opts.Parallel)
794		if c.opts.Limit%int64(c.opts.Parallel) > 0 {
795			input.limit++
796		}
797		remaining := c.opts.Limit - c.count - c.countWaiting
798		if input.limit > remaining {
799			input.limit = remaining
800		}
801	}
802	return input, true
803}
804
805// sentInput updates internal counters after an input is sent to c.inputC.
806func (c *coordinator) sentInput(input fuzzInput) {
807	c.inputQueue.dequeue()
808	c.countWaiting += input.limit
809}
810
811// refillInputQueue refills the input queue from the corpus after it becomes
812// empty.
813func (c *coordinator) refillInputQueue() {
814	for _, e := range c.corpus.entries {
815		c.inputQueue.enqueue(e)
816	}
817}
818
819// queueForMinimization creates a fuzzMinimizeInput from result and adds it
820// to the minimization queue to be sent to workers.
821func (c *coordinator) queueForMinimization(result fuzzResult, keepCoverage []byte) {
822	if shouldPrintDebugInfo() {
823		c.debugLogf(
824			"queueing input for minimization, id: %s, parent: %s, keepCoverage: %t, crasher: %t",
825			result.entry.Path,
826			result.entry.Parent,
827			keepCoverage != nil,
828			result.crasherMsg != "",
829		)
830	}
831	if result.crasherMsg != "" {
832		c.minimizeQueue.clear()
833	}
834
835	input := fuzzMinimizeInput{
836		entry:        result.entry,
837		crasherMsg:   result.crasherMsg,
838		keepCoverage: keepCoverage,
839	}
840	c.minimizeQueue.enqueue(input)
841}
842
843// peekMinimizeInput returns the next input that should be sent to workers for
844// minimization.
845func (c *coordinator) peekMinimizeInput() (fuzzMinimizeInput, bool) {
846	if !c.canMinimize() {
847		// Already making the maximum number of calls to the fuzz function.
848		// Don't send more inputs right now.
849		return fuzzMinimizeInput{}, false
850	}
851	v, ok := c.minimizeQueue.peek()
852	if !ok {
853		return fuzzMinimizeInput{}, false
854	}
855	input := v.(fuzzMinimizeInput)
856
857	if c.opts.MinimizeTimeout > 0 {
858		input.timeout = c.opts.MinimizeTimeout
859	}
860	if c.opts.MinimizeLimit > 0 {
861		input.limit = c.opts.MinimizeLimit
862	} else if c.opts.Limit > 0 {
863		if input.crasherMsg != "" {
864			input.limit = c.opts.Limit
865		} else {
866			input.limit = c.opts.Limit / int64(c.opts.Parallel)
867			if c.opts.Limit%int64(c.opts.Parallel) > 0 {
868				input.limit++
869			}
870		}
871	}
872	if c.opts.Limit > 0 {
873		remaining := c.opts.Limit - c.count - c.countWaiting
874		if input.limit > remaining {
875			input.limit = remaining
876		}
877	}
878	return input, true
879}
880
881// sentMinimizeInput removes an input from the minimization queue after it's
882// sent to minimizeC.
883func (c *coordinator) sentMinimizeInput(input fuzzMinimizeInput) {
884	c.minimizeQueue.dequeue()
885	c.countWaiting += input.limit
886}
887
888// warmupRun returns true while the coordinator is running inputs without
889// mutating them as a warmup before fuzzing. This could be to gather baseline
890// coverage data for entries in the corpus, or to test all of the seed corpus
891// for errors before fuzzing begins.
892//
893// The coordinator doesn't store coverage data in the cache with each input
894// because that data would be invalid when counter offsets in the test binary
895// change.
896//
897// When gathering coverage, the coordinator sends each entry to a worker to
898// gather coverage for that entry only, without fuzzing or minimizing. This
899// phase ends when all workers have finished, and the coordinator has a combined
900// coverage map.
901func (c *coordinator) warmupRun() bool {
902	return c.warmupInputLeft > 0
903}
904
905// updateCoverage sets bits in c.coverageMask that are set in newCoverage.
906// updateCoverage returns the number of newly set bits. See the comment on
907// coverageMask for the format.
908func (c *coordinator) updateCoverage(newCoverage []byte) int {
909	if len(newCoverage) != len(c.coverageMask) {
910		panic(fmt.Sprintf("number of coverage counters changed at runtime: %d, expected %d", len(newCoverage), len(c.coverageMask)))
911	}
912	newBitCount := 0
913	for i := range newCoverage {
914		diff := newCoverage[i] &^ c.coverageMask[i]
915		newBitCount += bits.OnesCount8(diff)
916		c.coverageMask[i] |= newCoverage[i]
917	}
918	return newBitCount
919}
920
921// canMinimize returns whether the coordinator should attempt to find smaller
922// inputs that reproduce a crash or new coverage.
923func (c *coordinator) canMinimize() bool {
924	return c.minimizationAllowed &&
925		(c.opts.Limit == 0 || c.count+c.countWaiting < c.opts.Limit)
926}
927
928func (c *coordinator) elapsed() time.Duration {
929	return time.Since(c.startTime).Round(1 * time.Second)
930}
931
932// readCache creates a combined corpus from seed values and values in the cache
933// (in GOCACHE/fuzz).
934//
935// TODO(fuzzing): need a mechanism that can remove values that
936// aren't useful anymore, for example, because they have the wrong type.
937func (c *coordinator) readCache() error {
938	if _, err := c.addCorpusEntries(false, c.opts.Seed...); err != nil {
939		return err
940	}
941	entries, err := ReadCorpus(c.opts.CacheDir, c.opts.Types)
942	if err != nil {
943		if _, ok := err.(*MalformedCorpusError); !ok {
944			// It's okay if some files in the cache directory are malformed and
945			// are not included in the corpus, but fail if it's an I/O error.
946			return err
947		}
948		// TODO(jayconrod,katiehockman): consider printing some kind of warning
949		// indicating the number of files which were skipped because they are
950		// malformed.
951	}
952	if _, err := c.addCorpusEntries(false, entries...); err != nil {
953		return err
954	}
955	return nil
956}
957
958// MalformedCorpusError is an error found while reading the corpus from the
959// filesystem. All of the errors are stored in the errs list. The testing
960// framework uses this to report malformed files in testdata.
961type MalformedCorpusError struct {
962	errs []error
963}
964
965func (e *MalformedCorpusError) Error() string {
966	var msgs []string
967	for _, s := range e.errs {
968		msgs = append(msgs, s.Error())
969	}
970	return strings.Join(msgs, "\n")
971}
972
973// ReadCorpus reads the corpus from the provided dir. The returned corpus
974// entries are guaranteed to match the given types. Any malformed files will
975// be saved in a MalformedCorpusError and returned, along with the most recent
976// error.
977func ReadCorpus(dir string, types []reflect.Type) ([]CorpusEntry, error) {
978	files, err := os.ReadDir(dir)
979	if os.IsNotExist(err) {
980		return nil, nil // No corpus to read
981	} else if err != nil {
982		return nil, fmt.Errorf("reading seed corpus from testdata: %v", err)
983	}
984	var corpus []CorpusEntry
985	var errs []error
986	for _, file := range files {
987		// TODO(jayconrod,katiehockman): determine when a file is a fuzzing input
988		// based on its name. We should only read files created by writeToCorpus.
989		// If we read ALL files, we won't be able to change the file format by
990		// changing the extension. We also won't be able to add files like
991		// README.txt explaining why the directory exists.
992		if file.IsDir() {
993			continue
994		}
995		filename := filepath.Join(dir, file.Name())
996		data, err := os.ReadFile(filename)
997		if err != nil {
998			return nil, fmt.Errorf("failed to read corpus file: %v", err)
999		}
1000		var vals []any
1001		vals, err = readCorpusData(data, types)
1002		if err != nil {
1003			errs = append(errs, fmt.Errorf("%q: %v", filename, err))
1004			continue
1005		}
1006		corpus = append(corpus, CorpusEntry{Path: filename, Values: vals})
1007	}
1008	if len(errs) > 0 {
1009		return corpus, &MalformedCorpusError{errs: errs}
1010	}
1011	return corpus, nil
1012}
1013
1014func readCorpusData(data []byte, types []reflect.Type) ([]any, error) {
1015	vals, err := unmarshalCorpusFile(data)
1016	if err != nil {
1017		return nil, fmt.Errorf("unmarshal: %v", err)
1018	}
1019	if err = CheckCorpus(vals, types); err != nil {
1020		return nil, err
1021	}
1022	return vals, nil
1023}
1024
1025// CheckCorpus verifies that the types in vals match the expected types
1026// provided.
1027func CheckCorpus(vals []any, types []reflect.Type) error {
1028	if len(vals) != len(types) {
1029		return fmt.Errorf("wrong number of values in corpus entry: %d, want %d", len(vals), len(types))
1030	}
1031	valsT := make([]reflect.Type, len(vals))
1032	for valsI, v := range vals {
1033		valsT[valsI] = reflect.TypeOf(v)
1034	}
1035	for i := range types {
1036		if valsT[i] != types[i] {
1037			return fmt.Errorf("mismatched types in corpus entry: %v, want %v", valsT, types)
1038		}
1039	}
1040	return nil
1041}
1042
1043// writeToCorpus atomically writes the given bytes to a new file in testdata. If
1044// the directory does not exist, it will create one. If the file already exists,
1045// writeToCorpus will not rewrite it. writeToCorpus sets entry.Path to the new
1046// file that was just written or an error if it failed.
1047func writeToCorpus(entry *CorpusEntry, dir string) (err error) {
1048	sum := fmt.Sprintf("%x", sha256.Sum256(entry.Data))[:16]
1049	entry.Path = filepath.Join(dir, sum)
1050	if err := os.MkdirAll(dir, 0777); err != nil {
1051		return err
1052	}
1053	if err := os.WriteFile(entry.Path, entry.Data, 0666); err != nil {
1054		os.Remove(entry.Path) // remove partially written file
1055		return err
1056	}
1057	return nil
1058}
1059
1060func testName(path string) string {
1061	return filepath.Base(path)
1062}
1063
1064func zeroValue(t reflect.Type) any {
1065	for _, v := range zeroVals {
1066		if reflect.TypeOf(v) == t {
1067			return v
1068		}
1069	}
1070	panic(fmt.Sprintf("unsupported type: %v", t))
1071}
1072
1073var zeroVals []any = []any{
1074	[]byte(""),
1075	string(""),
1076	false,
1077	byte(0),
1078	rune(0),
1079	float32(0),
1080	float64(0),
1081	int(0),
1082	int8(0),
1083	int16(0),
1084	int32(0),
1085	int64(0),
1086	uint(0),
1087	uint8(0),
1088	uint16(0),
1089	uint32(0),
1090	uint64(0),
1091}
1092
1093var debugInfo = godebug.New("#fuzzdebug").Value() == "1"
1094
1095func shouldPrintDebugInfo() bool {
1096	return debugInfo
1097}
1098
1099func (c *coordinator) debugLogf(format string, args ...any) {
1100	t := time.Now().Format("2006-01-02 15:04:05.999999999")
1101	fmt.Fprintf(c.opts.Log, t+" DEBUG "+format+"\n", args...)
1102}
1103