Structs
CoordinateFuzzingOpts
struct
#
CoordinateFuzzingOpts is a set of arguments for CoordinateFuzzing.
The zero value is valid for each field unless specified otherwise.
type CoordinateFuzzingOpts struct {
Log io.Writer
Timeout time.Duration
Limit int64
MinimizeTimeout time.Duration
MinimizeLimit int64
Parallel int
Seed []CorpusEntry
Types []reflect.Type
CorpusDir string
CacheDir string
}
CorpusEntry
struct
#
CorpusEntry represents an individual input for fuzzing.
We must use an equivalent type in the testing and testing/internal/testdeps
packages, but testing can't import this package directly, and we don't want
to export this type from testing. Instead, we use the same struct type and
use a type alias (not a defined type) for convenience.
type CorpusEntry struct {
Parent string
Path string
Data []byte
Values []any
Generation int
IsSeed bool
}
MalformedCorpusError
struct
#
MalformedCorpusError is an error found while reading the corpus from the
filesystem. All of the errors are stored in the errs list. The testing
framework uses this to report malformed files in testdata.
type MalformedCorpusError struct {
errs []error
}
call
struct
#
call is serialized and sent from the coordinator on fuzz_in. It acts as
a minimalist RPC mechanism. Exactly one of its fields must be set to indicate
which method to call.
type call struct {
Ping *pingArgs
Fuzz *fuzzArgs
Minimize *minimizeArgs
}
contextReader
struct
#
contextReader wraps a Reader with a Context. If the context is canceled
while the underlying reader is blocked, Read returns immediately.
This is useful for reading from a pipe. Closing a pipe file descriptor does
not unblock pending Reads on that file descriptor. All copies of the pipe's
other file descriptor (the write end) must be closed in all processes that
inherit it. This is difficult to do correctly in the situation we care about
(process group termination).
type contextReader struct {
ctx context.Context
r io.Reader
}
coordinator
struct
#
coordinator holds channels that workers can use to communicate with
the coordinator.
type coordinator struct {
opts CoordinateFuzzingOpts
startTime time.Time
inputC chan fuzzInput
minimizeC chan fuzzMinimizeInput
resultC chan fuzzResult
count int64
countLastLog int64
timeLastLog time.Time
interestingCount int
warmupInputCount int
warmupInputLeft int
duration time.Duration
countWaiting int64
corpus corpus
minimizationAllowed bool
inputQueue queue
minimizeQueue queue
crashMinimizing *fuzzResult
coverageMask []byte
}
corpus
struct
#
type corpus struct {
entries []CorpusEntry
hashes map[[sha256.Size]byte]bool
}
crashError
struct
#
crashError wraps a crasher written to the seed corpus. It saves the name
of the file where the input causing the crasher was saved. The testing
framework uses this to report a command to re-run that specific input.
type crashError struct {
path string
err error
}
fuzzArgs
struct
#
fuzzArgs contains arguments to workerServer.fuzz. The value to fuzz is
passed in shared memory.
type fuzzArgs struct {
Timeout time.Duration
Limit int64
Warmup bool
CoverageData []byte
}
fuzzInput
struct
#
type fuzzInput struct {
entry CorpusEntry
timeout time.Duration
limit int64
warmup bool
coverageData []byte
}
fuzzMinimizeInput
struct
#
type fuzzMinimizeInput struct {
entry CorpusEntry
crasherMsg string
limit int64
timeout time.Duration
keepCoverage []byte
}
fuzzResponse
struct
#
fuzzResponse contains results from workerServer.fuzz.
type fuzzResponse struct {
TotalDuration time.Duration
InterestingDuration time.Duration
Count int64
CoverageData []byte
Err string
InternalErr string
}
fuzzResult
struct
#
type fuzzResult struct {
entry CorpusEntry
crasherMsg string
canMinimize bool
coverageData []byte
limit int64
count int64
totalDuration time.Duration
entryDuration time.Duration
}
minimizeArgs
struct
#
minimizeArgs contains arguments to workerServer.minimize. The value to
minimize is already in shared memory.
type minimizeArgs struct {
Timeout time.Duration
Limit int64
KeepCoverage []byte
Index int
}
minimizeResponse
struct
#
minimizeResponse contains results from workerServer.minimize.
type minimizeResponse struct {
WroteToMem bool
Err string
CoverageData []byte
Duration time.Duration
Count int64
}
mutator
struct
#
type mutator struct {
r mutatorRand
scratch []byte
}
noCopy
struct
#
noCopy may be embedded into structs which must not be copied
after the first use.
See https://golang.org/issues/8005#issuecomment-190753527
for details.
type noCopy struct {
}
pcgRand
struct
#
pcgRand is a PRNG. It should not be copied or shared. No Rand methods are
concurrency safe.
type pcgRand struct {
noCopy noCopy
state uint64
inc uint64
}
pingArgs
struct
#
pingArgs contains arguments to workerServer.ping.
type pingArgs struct {
}
pingResponse
struct
#
pingResponse contains results from workerServer.ping.
type pingResponse struct {
}
queue
struct
#
queue holds a growable sequence of inputs for fuzzing and minimization.
For now, this is a simple ring buffer
(https://en.wikipedia.org/wiki/Circular_buffer).
TODO(golang.org/issue/46224): use a prioritization algorithm based on input
size, previous duration, coverage, and any other metrics that seem useful.
type queue struct {
elems []any
head int
len int
}
sharedMem
struct
#
sharedMem manages access to a region of virtual memory mapped from a file,
shared between multiple processes. The region includes space for a header and
a value of variable length.
When fuzzing, the coordinator creates a sharedMem from a temporary file for
each worker. This buffer is used to pass values to fuzz between processes.
Care must be taken to manage access to shared memory across processes;
sharedMem provides no synchronization on its own. See workerComm for an
explanation.
type sharedMem struct {
f *os.File
region []byte
removeOnClose bool
sys sharedMemSys
}
sharedMemSys
struct
#
type sharedMemSys struct {
mapObj syscall.Handle
}
sharedMemSys
struct
#
type sharedMemSys struct {
}
sharedMemSys
struct
#
type sharedMemSys struct {
}
worker
struct
#
worker manages a worker process running a test binary. The worker object
exists only in the coordinator (the process started by 'go test -fuzz').
workerClient is used by the coordinator to send RPCs to the worker process,
which handles them with workerServer.
type worker struct {
dir string
binPath string
args []string
env []string
coordinator *coordinator
memMu chan *sharedMem
cmd *exec.Cmd
client *workerClient
waitErr error
interrupted bool
termC chan struct{...}
}
workerClient
struct
#
workerClient is a minimalist RPC client. The coordinator process uses a
workerClient to call methods in each worker process (handled by
workerServer).
type workerClient struct {
workerComm
m *mutator
mu sync.Mutex
}
workerComm
struct
#
workerComm holds pipes and shared memory used for communication
between the coordinator process (client) and a worker process (server).
These values are unique to each worker; they are shared only with the
coordinator, not with other workers.
Access to shared memory is synchronized implicitly over the RPC protocol
implemented in workerServer and workerClient. During a call, the client
(worker) has exclusive access to shared memory; at other times, the server
(coordinator) has exclusive access.
type workerComm struct {
fuzzIn *os.File
fuzzOut *os.File
memMu chan *sharedMem
}
workerServer
struct
#
workerServer is a minimalist RPC server, run by fuzz worker processes.
It allows the coordinator process (using workerClient) to call methods in a
worker process. This system allows the coordinator to run multiple worker
processes in parallel and to collect inputs that caused crashes from shared
memory after a worker process terminates unexpectedly.
type workerServer struct {
workerComm
m *mutator
coverageMask []byte
fuzzFn func(CorpusEntry) (time.Duration, error)
}
Functions
CheckCorpus
function
#
CheckCorpus verifies that the types in vals match the expected types
provided.
func CheckCorpus(vals []any, types []reflect.Type) error
Close
method
#
func (m *sharedMem) Close() error
Close
method
#
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
Close
method
#
Close unmaps the shared memory and closes the temporary file. If this
sharedMem was created with sharedMemTempFile, Close also removes the file.
func (m *sharedMem) Close() error
Close
method
#
Close unmaps the shared memory and closes the temporary file. If this
sharedMem was created with sharedMemTempFile, Close also removes the file.
func (m *sharedMem) Close() error
CoordinateFuzzing
function
#
CoordinateFuzzing creates several worker processes and communicates with
them to test random inputs that could trigger crashes and expose bugs.
The worker processes run the same binary in the same directory with the
same environment variables as the coordinator process. Workers also run
with the same arguments as the coordinator, except with the -test.fuzzworker
flag prepended to the argument list.
If a crash occurs, the function will return an error containing information
about the crash, which can be reported to the user.
func CoordinateFuzzing(ctx context.Context, opts CoordinateFuzzingOpts) (err error)
CrashPath
method
#
func (e *crashError) CrashPath() string
Error
method
#
func (e *crashError) Error() string
Error
method
#
func (e *MalformedCorpusError) Error() string
Lock
method
#
Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock()
Read
method
#
func (cr *contextReader) Read(b []byte) (int, error)
ReadCorpus
function
#
ReadCorpus reads the corpus from the provided dir. The returned corpus
entries are guaranteed to match the given types. Any malformed files will
be saved in a MalformedCorpusError and returned, along with the most recent
error.
func ReadCorpus(dir string, types []reflect.Type) ([]CorpusEntry, error)
ResetCoverage
function
#
ResetCoverage sets all of the counters for each edge of the instrumented
source code to 0.
func ResetCoverage()
RunFuzzWorker
function
#
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
SnapshotCoverage
function
#
SnapshotCoverage copies the current counter values into coverageSnapshot,
preserving them for later inspection. SnapshotCoverage also rounds each
counter down to the nearest power of two. This lets the coordinator store
multiple values for each counter by OR'ing them together.
func SnapshotCoverage()
Unlock
method
#
func (*noCopy) Unlock()
Unwrap
method
#
func (e *crashError) Unwrap() error
addCorpusEntries
method
#
addCorpusEntries adds entries to the corpus, and optionally writes the entries
to the cache directory. If an entry is already in the corpus it is skipped. If
all of the entries are unique, addCorpusEntries returns true and a nil error,
if at least one of the entries was a duplicate, it returns false and a nil error.
func (c *coordinator) addCorpusEntries(addToCache bool, entries ...CorpusEntry) (bool, error)
bool
method
#
bool generates a random bool.
func (r *pcgRand) bool() bool
byteSliceArithmeticUint16
function
#
byteSliceArithmeticUint16 adds/subtracts from a random uint16 in b.
func byteSliceArithmeticUint16(m *mutator, b []byte) []byte
byteSliceArithmeticUint32
function
#
byteSliceArithmeticUint32 adds/subtracts from a random uint32 in b.
func byteSliceArithmeticUint32(m *mutator, b []byte) []byte
byteSliceArithmeticUint64
function
#
byteSliceArithmeticUint64 adds/subtracts from a random uint64 in b.
func byteSliceArithmeticUint64(m *mutator, b []byte) []byte
byteSliceArithmeticUint8
function
#
byteSliceArithmeticUint8 adds/subtracts from a random byte in b.
func byteSliceArithmeticUint8(m *mutator, b []byte) []byte
byteSliceBitFlip
function
#
byteSliceBitFlip flips a random bit in a random byte in b.
func byteSliceBitFlip(m *mutator, b []byte) []byte
byteSliceDuplicateBytes
function
#
byteSliceDuplicateBytes duplicates a chunk of bytes in b and inserts it into
a random position.
func byteSliceDuplicateBytes(m *mutator, b []byte) []byte
byteSliceInsertConstantBytes
function
#
byteSliceInsertConstantBytes inserts a chunk of constant bytes into a random position in b.
func byteSliceInsertConstantBytes(m *mutator, b []byte) []byte
byteSliceInsertRandomBytes
function
#
byteSliceInsertRandomBytes inserts a chunk of random bytes into b at a random
position.
func byteSliceInsertRandomBytes(m *mutator, b []byte) []byte
byteSliceOverwriteBytes
function
#
byteSliceOverwriteBytes overwrites a chunk of b with another chunk of b.
func byteSliceOverwriteBytes(m *mutator, b []byte) []byte
byteSliceOverwriteConstantBytes
function
#
byteSliceOverwriteConstantBytes overwrites a chunk of b with constant bytes.
func byteSliceOverwriteConstantBytes(m *mutator, b []byte) []byte
byteSliceOverwriteInterestingUint16
function
#
byteSliceOverwriteInterestingUint16 overwrites a random uint16 in b with an interesting
value.
func byteSliceOverwriteInterestingUint16(m *mutator, b []byte) []byte
byteSliceOverwriteInterestingUint32
function
#
byteSliceOverwriteInterestingUint32 overwrites a random uint16 in b with an interesting
value.
func byteSliceOverwriteInterestingUint32(m *mutator, b []byte) []byte
byteSliceOverwriteInterestingUint8
function
#
byteSliceOverwriteInterestingUint8 overwrites a random byte in b with an interesting
value.
func byteSliceOverwriteInterestingUint8(m *mutator, b []byte) []byte
byteSliceRemoveBytes
function
#
byteSliceRemoveBytes removes a random chunk of bytes from b.
func byteSliceRemoveBytes(m *mutator, b []byte) []byte
byteSliceShuffleBytes
function
#
byteSliceShuffleBytes shuffles a chunk of bytes in b.
func byteSliceShuffleBytes(m *mutator, b []byte) []byte
byteSliceSwapByte
function
#
byteSliceSwapByte swaps two random bytes in b.
func byteSliceSwapByte(m *mutator, b []byte) []byte
byteSliceSwapBytes
function
#
byteSliceSwapBytes swaps two chunks of bytes in b.
func byteSliceSwapBytes(m *mutator, b []byte) []byte
byteSliceXORByte
function
#
byteSliceXORByte XORs a random byte in b with a random value.
func byteSliceXORByte(m *mutator, b []byte) []byte
callLocked
method
#
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)
canMinimize
method
#
canMinimize returns whether the coordinator should attempt to find smaller
inputs that reproduce a crash or new coverage.
func (c *coordinator) canMinimize() bool
cap
method
#
func (q *queue) cap() int
chooseLen
method
#
chooseLen chooses length of range mutation in range [1,n]. It gives
preference to shorter ranges.
func (m *mutator) chooseLen(n int) int
cleanup
method
#
cleanup releases persistent resources associated with the worker.
func (w *worker) cleanup() error
clear
method
#
func (q *queue) clear()
coordinate
method
#
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
corpusEntryData
function
#
corpusEntryData returns the raw input bytes, either from the data struct
field, or from disk.
func corpusEntryData(ce CorpusEntry) ([]byte, error)
countBits
function
#
func countBits(cov []byte) int
countNewCoverageBits
function
#
countNewCoverageBits returns the number of bits set in snapshot that are not
set in base.
func countNewCoverageBits(base []byte, snapshot []byte) int
coverage
function
#
coverage returns a []byte containing unique 8-bit counters for each edge of
the instrumented source code. This coverage data will only be generated if
`-d=libfuzzer` is set at build time. This can be used to understand the code
coverage of a test execution.
func coverage() []byte
coverage
function
#
coverage returns a []byte containing unique 8-bit counters for each edge of
the instrumented source code. This coverage data will only be generated if
`-d=libfuzzer` is set at build time. This can be used to understand the code
coverage of a test execution.
func coverage() []byte
debugLogf
method
#
func (c *coordinator) debugLogf(format string, args ...any)
dequeue
method
#
func (q *queue) dequeue() (any, bool)
diffCoverage
function
#
diffCoverage returns a set of bits set in snapshot but not in base.
If there are no new bits set, diffCoverage returns nil.
func diffCoverage(base []byte, snapshot []byte) []byte
elapsed
method
#
func (c *coordinator) elapsed() time.Duration
enqueue
method
#
func (q *queue) enqueue(e any)
fuzz
method
#
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)
fuzz
method
#
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)
getWorkerComm
function
#
func getWorkerComm() (comm workerComm, err error)
getWorkerComm
function
#
getWorkerComm returns communication channels in the worker process.
func getWorkerComm() (comm workerComm, err error)
getWorkerComm
function
#
getWorkerComm returns communication channels in the worker process.
func getWorkerComm() (comm workerComm, err error)
godebugSeed
function
#
func godebugSeed() *int
grow
method
#
func (q *queue) grow()
hasCoverageBit
function
#
hasCoverageBit returns true if snapshot has at least one bit set that is
also set in base.
func hasCoverageBit(base []byte, snapshot []byte) bool
init
function
#
func init()
intn
method
#
intn returns a pseudo-random number in [0, n).
n must fit in a uint32.
func (r *pcgRand) intn(n int) int
isCoverageSubset
function
#
isCoverageSubset returns true if all the base coverage bits are set in
snapshot.
func isCoverageSubset(base []byte, snapshot []byte) bool
isCrashSignal
function
#
isCrashSignal returns whether a signal was likely to have been caused by an
error in the program that received it, triggered by a fuzz input. For
example, SIGSEGV would be received after a nil pointer dereference.
Other signals like SIGKILL or SIGHUP are more likely to have been sent by
another process, and we shouldn't record a crasher if the worker process
receives one of these.
Note that Go installs its own signal handlers on startup, so some of these
signals may only be received if signal handlers are changed. For example,
SIGSEGV is normally transformed into a panic that causes the process to exit
with status 2 if not recovered, which we handle as a crash.
func isCrashSignal(signal os.Signal) bool
isCrashSignal
function
#
isCrashSignal is not implemented because Windows doesn't have signals.
func isCrashSignal(signal os.Signal) bool
isCrashSignal
function
#
func isCrashSignal(signal os.Signal) bool
isInterruptError
function
#
func isInterruptError(err error) bool
isInterruptError
function
#
isInterruptError returns whether an error was returned by a process that
was terminated by an interrupt signal (SIGINT).
func isInterruptError(err error) bool
isInterruptError
function
#
func isInterruptError(err error) bool
isMinimizable
function
#
func isMinimizable(t reflect.Type) bool
isRunning
method
#
func (w *worker) isRunning() bool
libfuzzerHookEqualFold
function
#
func libfuzzerHookEqualFold(arg0 string, arg1 string, fakePC uint)
libfuzzerHookStrCmp
function
#
func libfuzzerHookStrCmp(arg0 string, arg1 string, fakePC uint)
libfuzzerTraceCmp1
function
#
func libfuzzerTraceCmp1(arg0 uint8, arg1 uint8, fakePC uint)
libfuzzerTraceCmp2
function
#
func libfuzzerTraceCmp2(arg0 uint16, arg1 uint16, fakePC uint)
libfuzzerTraceCmp4
function
#
func libfuzzerTraceCmp4(arg0 uint32, arg1 uint32, fakePC uint)
libfuzzerTraceCmp8
function
#
func libfuzzerTraceCmp8(arg0 uint64, arg1 uint64, fakePC uint)
libfuzzerTraceConstCmp1
function
#
func libfuzzerTraceConstCmp1(arg0 uint8, arg1 uint8, fakePC uint)
libfuzzerTraceConstCmp2
function
#
func libfuzzerTraceConstCmp2(arg0 uint16, arg1 uint16, fakePC uint)
libfuzzerTraceConstCmp4
function
#
func libfuzzerTraceConstCmp4(arg0 uint32, arg1 uint32, fakePC uint)
libfuzzerTraceConstCmp8
function
#
func libfuzzerTraceConstCmp8(arg0 uint64, arg1 uint64, fakePC uint)
logStats
method
#
func (c *coordinator) logStats()
marshalCorpusFile
function
#
marshalCorpusFile encodes an arbitrary number of arguments into the file format for the
corpus.
func marshalCorpusFile(vals ...any) []byte
minimize
method
#
func (ws *workerServer) minimize(ctx context.Context, args minimizeArgs) (resp minimizeResponse)
minimize
method
#
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)
minimize
method
#
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)
minimizeBytes
function
#
func minimizeBytes(v []byte, try func([]byte) bool, shouldStop func() bool)
minimizeInput
method
#
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)
mutate
method
#
mutate performs several mutations on the provided values.
func (m *mutator) mutate(vals []any, maxBytes int)
mutateBytes
method
#
func (m *mutator) mutateBytes(ptrB *[]byte)
mutateFloat
method
#
func (m *mutator) mutateFloat(v float64, maxValue float64) float64
mutateInt
method
#
func (m *mutator) mutateInt(v int64, maxValue int64) int64
mutateUInt
method
#
func (m *mutator) mutateUInt(v uint64, maxValue uint64) uint64
newCoordinator
function
#
func newCoordinator(opts CoordinateFuzzingOpts) (*coordinator, error)
newMutator
function
#
func newMutator() *mutator
newPcgRand
function
#
newPcgRand generates a new, seeded Rand, ready for use.
func newPcgRand() *pcgRand
newWorker
function
#
func newWorker(c *coordinator, dir string, binPath string, args []string, env []string) (*worker, error)
newWorkerClient
function
#
func newWorkerClient(comm workerComm, m *mutator) *workerClient
parseCorpusValue
function
#
func parseCorpusValue(line []byte) (any, error)
parseInt
function
#
parseInt returns an integer of value val and type typ.
func parseInt(val string, typ string) (any, error)
parseUint
function
#
parseUint returns an unsigned integer of value val and type typ.
func parseUint(val string, typ string) (any, error)
peek
method
#
func (q *queue) peek() (any, bool)
peekInput
method
#
peekInput returns the next value that should be sent to workers.
If the number of executions is limited, the returned value includes
a limit for one worker. If there are no executions left, peekInput returns
a zero value and false.
peekInput doesn't actually remove the input from the queue. The caller
must call sentInput after sending the input.
If the input queue is empty and the coverage/testing-only run has completed,
queue refills it from the corpus.
func (c *coordinator) peekInput() (fuzzInput, bool)
peekMinimizeInput
method
#
peekMinimizeInput returns the next input that should be sent to workers for
minimization.
func (c *coordinator) peekMinimizeInput() (fuzzMinimizeInput, bool)
ping
method
#
ping tells the worker to call the ping method. See workerServer.ping.
func (wc *workerClient) ping(ctx context.Context) error
ping
method
#
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
queueForMinimization
method
#
queueForMinimization creates a fuzzMinimizeInput from result and adds it
to the minimization queue to be sent to workers.
func (c *coordinator) queueForMinimization(result fuzzResult, keepCoverage []byte)
rand
method
#
func (m *mutator) rand(n int) int
randByteOrder
method
#
func (m *mutator) randByteOrder() binary.ByteOrder
readCache
method
#
readCache creates a combined corpus from seed values and values in the cache
(in GOCACHE/fuzz).
TODO(fuzzing): need a mechanism that can remove values that
aren't useful anymore, for example, because they have the wrong type.
func (c *coordinator) readCache() error
readCorpusData
function
#
func readCorpusData(data []byte, types []reflect.Type) ([]any, error)
refillInputQueue
method
#
refillInputQueue refills the input queue from the corpus after it becomes
empty.
func (c *coordinator) refillInputQueue()
restore
method
#
func (r *pcgRand) restore(randState uint64, randInc uint64)
save
method
#
func (r *pcgRand) save(randState *uint64, randInc *uint64)
sentInput
method
#
sentInput updates internal counters after an input is sent to c.inputC.
func (c *coordinator) sentInput(input fuzzInput)
sentMinimizeInput
method
#
sentMinimizeInput removes an input from the minimization queue after it's
sent to minimizeC.
func (c *coordinator) sentMinimizeInput(input fuzzMinimizeInput)
serve
method
#
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
setValue
method
#
setValue copies the data in b into the shared memory buffer and sets
the length. len(b) must be less than or equal to the capacity of the buffer
(as returned by cap(m.value())).
func (m *sharedMem) setValue(b []byte)
setValueLen
method
#
setValueLen sets the length of the shared memory buffer returned by valueRef
to n, which may be at most the cap of that slice.
Note that we can only store the length in the shared memory header. The full
slice header contains a pointer, which is likely only valid for one process,
since each process can map shared memory at a different virtual address.
func (m *sharedMem) setValueLen(n int)
setWorkerComm
function
#
setWorkerComm configures communication channels on the cmd that will
run a worker process.
func setWorkerComm(cmd *exec.Cmd, comm workerComm)
setWorkerComm
function
#
func setWorkerComm(cmd *exec.Cmd, comm workerComm)
setWorkerComm
function
#
setWorkerComm configures communication channels on the cmd that will
run a worker process.
func setWorkerComm(cmd *exec.Cmd, comm workerComm)
sharedMemMapFile
function
#
func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (mem *sharedMem, err error)
sharedMemMapFile
function
#
func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (*sharedMem, error)
sharedMemMapFile
function
#
func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (*sharedMem, error)
sharedMemSize
function
#
sharedMemSize returns the size needed for a shared memory buffer that can
contain values of the given size.
func sharedMemSize(valueSize int) int
sharedMemTempFile
function
#
sharedMemTempFile creates a new temporary file of the given size, then maps
it into memory. The file will be removed when the Close method is called.
func sharedMemTempFile(size int) (m *sharedMem, err error)
shouldPrintDebugInfo
function
#
func shouldPrintDebugInfo() bool
start
method
#
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)
startAndPing
method
#
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
step
method
#
func (r *pcgRand) step()
stop
method
#
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
terminationSignal
function
#
terminationSignal returns -1 and false because Windows doesn't have signals.
func terminationSignal(err error) (os.Signal, bool)
terminationSignal
function
#
terminationSignal checks if err is an exec.ExitError with a signal status.
If it is, terminationSignal returns the signal and true.
If not, -1 and false.
func terminationSignal(err error) (os.Signal, bool)
terminationSignal
function
#
func terminationSignal(err error) (os.Signal, bool)
testName
function
#
func testName(path string) string
uint32
method
#
uint32 returns a pseudo-random uint32.
func (r *pcgRand) uint32() uint32
uint32n
method
#
uint32n returns a pseudo-random number in [0, n).
For implementation details, see:
https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction
https://lemire.me/blog/2016/06/30/fast-random-shuffling
func (r *pcgRand) uint32n(n uint32) uint32
unmarshalCorpusFile
function
#
unmarshalCorpusFile decodes corpus bytes into their respective values.
func unmarshalCorpusFile(b []byte) ([]any, error)
updateCoverage
method
#
updateCoverage sets bits in c.coverageMask that are set in newCoverage.
updateCoverage returns the number of newly set bits. See the comment on
coverageMask for the format.
func (c *coordinator) updateCoverage(newCoverage []byte) int
updateStats
method
#
func (c *coordinator) updateStats(result fuzzResult)
valueCopy
method
#
valueCopy returns a copy of the value stored in shared memory.
func (m *sharedMem) valueCopy() []byte
valueRef
method
#
valueRef returns the value currently stored in shared memory. The returned
slice points to shared memory; it is not a copy.
func (m *sharedMem) valueRef() []byte
warmupRun
method
#
warmupRun returns true while the coordinator is running inputs without
mutating them as a warmup before fuzzing. This could be to gather baseline
coverage data for entries in the corpus, or to test all of the seed corpus
for errors before fuzzing begins.
The coordinator doesn't store coverage data in the cache with each input
because that data would be invalid when counter offsets in the test binary
change.
When gathering coverage, the coordinator sends each entry to a worker to
gather coverage for that entry only, without fuzzing or minimizing. This
phase ends when all workers have finished, and the coordinator has a combined
coverage map.
func (c *coordinator) warmupRun() bool
writeToCorpus
function
#
writeToCorpus atomically writes the given bytes to a new file in testdata. If
the directory does not exist, it will create one. If the file already exists,
writeToCorpus will not rewrite it. writeToCorpus sets entry.Path to the new
file that was just written or an error if it failed.
func writeToCorpus(entry *CorpusEntry, dir string) (err error)
writeToMem
function
#
func writeToMem(vals []any, mem *sharedMem)
zeroValue
function
#
func zeroValue(t reflect.Type) any