Warmup: input.warmup,
CoverageData: input.coverageData,
}
- entry, resp, err := w.client.fuzz(ctx, input.entry, args)
+ entry, resp, isInternalError, err := w.client.fuzz(ctx, input.entry, args)
canMinimize := true
if err != nil {
// Error communicating with worker.
// Report an error, but don't record a crasher.
return fmt.Errorf("communicating with fuzzing process: %v", err)
}
- if w.waitErr == nil || isInterruptError(w.waitErr) {
- // Worker stopped, either by exiting with status 0 or after being
- // interrupted with a signal (not sent by coordinator). See comment in
- // termC case above.
- //
- // Since we expect I/O errors around interrupts, ignore this error.
- return nil
- }
if sig, ok := terminationSignal(w.waitErr); ok && !isCrashSignal(sig) {
// Worker terminated by a signal that probably wasn't caused by a
// specific input to the fuzz function. For example, on Linux,
// is closed. Don't record a crasher.
return fmt.Errorf("fuzzing process terminated by unexpected signal; no crash will be recorded: %v", w.waitErr)
}
+ if isInternalError {
+ // An internal error occurred which shouldn't be considered
+ // a crash.
+ return err
+ }
// Unexpected termination. Set error message and fall through.
// We'll restart the worker on the next iteration.
// Don't attempt to minimize this since it crashed the worker.
// Err is the error string caused by the value in shared memory, which is
// non-empty if the value in shared memory caused a crash.
Err string
+
+ // InternalErr is the error string caused by an internal error in the
+ // worker. This shouldn't be considered a crasher.
+ InternalErr string
}
// pingArgs contains arguments to workerServer.ping.
func (ws *workerServer) fuzz(ctx context.Context, args fuzzArgs) (resp fuzzResponse) {
if args.CoverageData != nil {
if ws.coverageMask != nil && len(args.CoverageData) != len(ws.coverageMask) {
- panic(fmt.Sprintf("unexpected size for CoverageData: got %d, expected %d", len(args.CoverageData), len(ws.coverageMask)))
+ resp.InternalErr = fmt.Sprintf("unexpected size for CoverageData: got %d, expected %d", len(args.CoverageData), len(ws.coverageMask))
+ return resp
}
ws.coverageMask = args.CoverageData
}
ws.memMu <- mem
}()
if args.Limit > 0 && mem.header().count >= args.Limit {
- panic(fmt.Sprintf("mem.header().count %d already exceeds args.Limit %d", mem.header().count, args.Limit))
+ resp.InternalErr = fmt.Sprintf("mem.header().count %d already exceeds args.Limit %d", mem.header().count, args.Limit)
+ return resp
}
vals, err := unmarshalCorpusFile(mem.valueCopy())
if err != nil {
- panic(err)
+ resp.InternalErr = err.Error()
+ return resp
}
shouldStop := func() bool {
entryOut.Parent = entryIn.Parent
entryOut.Generation = entryIn.Generation
if err != nil {
- panic(fmt.Sprintf("workerClient.minimize unmarshaling minimized value: %v", err))
+ return CorpusEntry{}, minimizeResponse{}, fmt.Errorf("workerClient.minimize unmarshaling minimized value: %v", err)
}
} else {
// Did not minimize, but the original input may still be interesting,
}
// 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, err error) {
+func (wc *workerClient) fuzz(ctx context.Context, entryIn CorpusEntry, args fuzzArgs) (entryOut CorpusEntry, resp fuzzResponse, isInternalError bool, err error) {
wc.mu.Lock()
defer wc.mu.Unlock()
mem, ok := <-wc.memMu
if !ok {
- return CorpusEntry{}, fuzzResponse{}, errSharedMemClosed
+ return CorpusEntry{}, fuzzResponse{}, true, errSharedMemClosed
}
mem.header().count = 0
inp, err := CorpusEntryData(entryIn)
if err != nil {
- return CorpusEntry{}, fuzzResponse{}, err
+ return CorpusEntry{}, fuzzResponse{}, true, err
}
mem.setValue(inp)
wc.memMu <- mem
c := call{Fuzz: &args}
callErr := wc.callLocked(ctx, c, &resp)
+ if resp.InternalErr != "" {
+ return CorpusEntry{}, fuzzResponse{}, true, errors.New(resp.InternalErr)
+ }
mem, ok = <-wc.memMu
if !ok {
- return CorpusEntry{}, fuzzResponse{}, errSharedMemClosed
+ return CorpusEntry{}, fuzzResponse{}, true, errSharedMemClosed
}
defer func() { wc.memMu <- mem }()
resp.Count = mem.header().count
if !bytes.Equal(inp, mem.valueRef()) {
- panic("workerServer.fuzz modified input")
+ return CorpusEntry{}, fuzzResponse{}, true, errors.New("workerServer.fuzz modified input")
}
needEntryOut := callErr != nil || resp.Err != "" ||
(!args.Warmup && resp.CoverageData != nil)
if needEntryOut {
valuesOut, err := unmarshalCorpusFile(inp)
if err != nil {
- panic(fmt.Sprintf("unmarshaling fuzz input value after call: %v", err))
+ return CorpusEntry{}, fuzzResponse{}, true, fmt.Errorf("unmarshaling fuzz input value after call: %v", err)
}
wc.m.r.restore(mem.header().randState, mem.header().randInc)
if !args.Warmup {
}
}
- return entryOut, resp, callErr
+ return entryOut, resp, false, callErr
}
// ping tells the worker to call the ping method. See workerServer.ping.