const workerFuzzDuration … const workerTimeoutDuration … const workerExitCode … const workerSharedMemSize … type worker … func newWorker(c *coordinator, dir, binPath string, args, env []string) (*worker, error) { … } // cleanup releases persistent resources associated with the worker. func (w *worker) cleanup() error { … } // coordinate runs the test binary to perform fuzzing. // // coordinate loops until ctx is canceled or a fatal error is encountered. // If a test process terminates unexpectedly while fuzzing, coordinate will // attempt to restart and continue unless the termination can be attributed // to an interruption (from a timer or the user). // // While looping, coordinate receives inputs from the coordinator, passes // those inputs to the worker process, then passes the results back to // the coordinator. func (w *worker) coordinate(ctx context.Context) error { … } // minimize tells a worker process to attempt to find a smaller value that // either causes an error (if we started minimizing because we found an input // that causes an error) or preserves new coverage (if we started minimizing // because we found an input that expands coverage). func (w *worker) minimize(ctx context.Context, input fuzzMinimizeInput) (min fuzzResult, err error) { … } func (w *worker) isRunning() bool { … } // startAndPing starts the worker process and sends it a message to make sure it // can communicate. // // startAndPing returns an error if any part of this didn't work, including if // the context is expired or the worker process was interrupted before it // responded. Errors that happen after start but before the ping response // likely indicate that the worker did not call F.Fuzz or called F.Fail first. // We don't record crashers for these errors. func (w *worker) startAndPing(ctx context.Context) error { … } // start runs a new worker process. // // If the process couldn't be started, start returns an error. Start won't // return later termination errors from the process if they occur. // // If the process starts successfully, start returns nil. stop must be called // once later to clean up, even if the process terminates on its own. // // When the process terminates, w.waitErr is set to the error (if any), and // w.termC is closed. func (w *worker) start() (err error) { … } // stop tells the worker process to exit by closing w.client, then blocks until // it terminates. If the worker doesn't terminate after a short time, stop // signals it with os.Interrupt (where supported), then os.Kill. // // stop returns the error the process terminated with, if any (same as // w.waitErr). // // stop must be called at least once after start returns successfully, even if // the worker process terminates unexpectedly. func (w *worker) stop() error { … } // RunFuzzWorker is called in a worker process to communicate with the // coordinator process in order to fuzz random inputs. RunFuzzWorker loops // until the coordinator tells it to stop. // // fn is a wrapper on the fuzz function. It may return an error to indicate // a given input "crashed". The coordinator will also record a crasher if // the function times out or terminates the process. // // RunFuzzWorker returns an error if it could not communicate with the // coordinator process. func RunFuzzWorker(ctx context.Context, fn func(CorpusEntry) error) error { … } type call … type minimizeArgs … type minimizeResponse … type fuzzArgs … type fuzzResponse … type pingArgs … type pingResponse … type workerComm … type workerServer … // serve reads serialized RPC messages on fuzzIn. When serve receives a message, // it calls the corresponding method, then sends the serialized result back // on fuzzOut. // // serve handles RPC calls synchronously; it will not attempt to read a message // until the previous call has finished. // // serve returns errors that occurred when communicating over pipes. serve // does not return errors from method calls; those are passed through serialized // responses. func (ws *workerServer) serve(ctx context.Context) error { … } const chainedMutations … // fuzz runs the test function on random variations of the input value in shared // memory for a limited duration or number of iterations. // // fuzz returns early if it finds an input that crashes the fuzz function (with // fuzzResponse.Err set) or an input that expands coverage (with // fuzzResponse.InterestingDuration set). // // fuzz does not modify the input in shared memory. Instead, it saves the // initial PRNG state in shared memory and increments a counter in shared // memory before each call to the test function. The caller may reconstruct // the crashing input with this information, since the PRNG is deterministic. func (ws *workerServer) fuzz(ctx context.Context, args fuzzArgs) (resp fuzzResponse) { … } func (ws *workerServer) minimize(ctx context.Context, args minimizeArgs) (resp minimizeResponse) { … } // minimizeInput applies a series of minimizing transformations on the provided // vals, ensuring that each minimization still causes an error, or keeps // coverage, in fuzzFn. It uses the context to determine how long to run, // stopping once closed. It returns a bool indicating whether minimization was // successful and an error if one was found. func (ws *workerServer) minimizeInput(ctx context.Context, vals []any, mem *sharedMem, args minimizeArgs) (success bool, retErr error) { … } func writeToMem(vals []any, mem *sharedMem) { … } // ping does nothing. The coordinator calls this method to ensure the worker // has called F.Fuzz and can communicate. func (ws *workerServer) ping(ctx context.Context, args pingArgs) pingResponse { … } type workerClient … func newWorkerClient(comm workerComm, m *mutator) *workerClient { … } // Close shuts down the connection to the RPC server (the worker process) by // closing fuzz_in. Close drains fuzz_out (avoiding a SIGPIPE in the worker), // and closes it after the worker process closes the other end. func (wc *workerClient) Close() error { … } var errSharedMemClosed … // minimize tells the worker to call the minimize method. See // workerServer.minimize. func (wc *workerClient) minimize(ctx context.Context, entryIn CorpusEntry, args minimizeArgs) (entryOut CorpusEntry, resp minimizeResponse, retErr error) { … } // fuzz tells the worker to call the fuzz method. See workerServer.fuzz. func (wc *workerClient) fuzz(ctx context.Context, entryIn CorpusEntry, args fuzzArgs) (entryOut CorpusEntry, resp fuzzResponse, isInternalError bool, err error) { … } // ping tells the worker to call the ping method. See workerServer.ping. func (wc *workerClient) ping(ctx context.Context) error { … } // callLocked sends an RPC from the coordinator to the worker process and waits // for the response. The callLocked may be canceled with ctx. func (wc *workerClient) callLocked(ctx context.Context, c call, resp any) (err error) { … } type contextReader … func (cr *contextReader) Read(b []byte) (int, error) { … }