fuzz

Imports

Imports #

"unsafe"
"reflect"
"fmt"
"math/bits"
"bytes"
"fmt"
"os"
"unsafe"
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/token"
"math"
"strconv"
"strings"
"unicode/utf8"
"encoding/binary"
"fmt"
"math"
"unsafe"
"fmt"
"os"
"os/exec"
"syscall"
"os"
"os/exec"
"bytes"
"context"
"crypto/sha256"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"os/exec"
"reflect"
"runtime"
"sync"
"time"
"bytes"
"context"
"crypto/sha256"
"errors"
"fmt"
"internal/godebug"
"io"
"math/bits"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"time"
"math/bits"
"os"
"strconv"
"strings"
"sync/atomic"
"time"
"fmt"
"os"
"os/exec"
"syscall"
"unsafe"
_ "unsafe"

Constants & Variables

_counters var #

_counters and _ecounters mark the start and end, respectively, of where the 8-bit coverage counters reside in memory. They're known to cmd/link, which specially assigns their addresses for this purpose.

var _counters [0]byte

_ecounters var #

_counters and _ecounters mark the start and end, respectively, of where the 8-bit coverage counters reside in memory. They're known to cmd/link, which specially assigns their addresses for this purpose.

var _ecounters [0]byte

byteSliceMutators var #

var byteSliceMutators = []byteSliceMutator{...}

chainedMutations const #

chainedMutations is how many mutations are applied before the worker resets the input to its original state. NOTE: this number was picked without much thought. It is low enough that it seems to create a significant diversity in mutated inputs. We may want to consider looking into this more closely once we have a proper performance testing framework. Another option is to randomly pick the number of chained mutations on each invocation of the workerServer.fuzz method (this appears to be what libFuzzer does, although there seems to be no documentation which explains why this choice was made.)

const chainedMutations = 5

coverageEnabled var #

var coverageEnabled = *ast.BinaryExpr

coverageSnapshot var #

var coverageSnapshot = *ast.CallExpr

debugInfo var #

var debugInfo = *ast.BinaryExpr

encVersion1 var #

encVersion1 will be the first line of a file with version 1 encoding.

var encVersion1 = "go test fuzz v1"

errSharedMemClosed var #

errSharedMemClosed is returned by workerClient methods that cannot access shared memory because it was closed and unmapped by another goroutine. That can happen when worker.cleanup is called in the worker goroutine while a workerClient.fuzz call runs concurrently. This error should not be reported. It indicates the operation was interrupted.

var errSharedMemClosed = *ast.CallExpr

globalInc var #

var globalInc atomic.Uint64

interesting16 var #

var interesting16 = []int16{...}

interesting32 var #

var interesting32 = []int32{...}

interesting8 var #

var interesting8 = []int8{...}

maxInt const #

const maxInt = *ast.CallExpr

maxUint const #

const maxUint = *ast.CallExpr

multiplier const #

const multiplier uint64 = 6364136223846793005

workerExitCode const #

workerExitCode is used as an exit code by fuzz worker processes after an internal error. This distinguishes internal errors from uncontrolled panics and other crashes. Keep in sync with internal/fuzz.workerExitCode.

const workerExitCode = 70

workerFuzzDuration const #

workerFuzzDuration is the amount of time a worker can spend testing random variations of an input given by the coordinator.

const workerFuzzDuration = *ast.BinaryExpr

workerSharedMemSize const #

workerSharedMemSize is the maximum size of the shared memory file used to communicate with workers. This limits the size of fuzz inputs.

const workerSharedMemSize = *ast.BinaryExpr

workerTimeoutDuration const #

workerTimeoutDuration is the amount of time a worker can go without responding to the coordinator before being stopped.

const workerTimeoutDuration = *ast.BinaryExpr

zeroVals var #

var zeroVals []any = []any{...}

Type Aliases

byteSliceMutator type #

type byteSliceMutator func(*mutator, []byte) []byte

Interfaces

mutatorRand interface #

type mutatorRand interface {
uint32() uint32
intn(int) int
uint32n(uint32) uint32
bool() bool
save(randState *uint64, randInc *uint64)
restore(randState uint64, randInc uint64)
}

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
}

sharedMemHeader struct #

sharedMemHeader stores metadata in shared memory.

type sharedMemHeader struct {
count int64
valueLen int
randState uint64
randInc uint64
rawInMem bool
}

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

Generated with Arrow