mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.fuzz] internal/fuzz: handle SIGINT races gracefully
A worker process may be terminated by SIGINT if it doesn't install the signal handler before SIGINT is delivered. That's likely when TestMain or the fuzz target setup take a long time. The coordinator now ignores these errors. Also, when testdeps.TestDeps.CoordinateFuzzing and RunFuzzWorker return, they will send a value on the chan passed to signal.Notify instead of closing it. This should have been obvious in hindsight, but the signal handler could still send a value on that channel after those functions return but before the process exits. Change-Id: Iea2589115f1f9bb7415bb5e7911defee423e642e Reviewed-on: https://go-review.googlesource.com/c/go/+/284292 Trust: Jay Conrod <jayconrod@google.com> Reviewed-by: Katie Hockman <katie@golang.org>
This commit is contained in:
parent
d45df5de32
commit
8e0584c327
5 changed files with 41 additions and 8 deletions
|
|
@ -73,3 +73,14 @@ func getWorkerComm() (comm workerComm, err error) {
|
||||||
}
|
}
|
||||||
return workerComm{fuzzIn: fuzzIn, fuzzOut: fuzzOut, mem: mem}, nil
|
return workerComm{fuzzIn: fuzzIn, fuzzOut: fuzzOut, mem: mem}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isInterruptError returns whether an error was returned by a process that
|
||||||
|
// was terminated by an interrupt signal (SIGINT).
|
||||||
|
func isInterruptError(err error) bool {
|
||||||
|
exitErr, ok := err.(*exec.ExitError)
|
||||||
|
if !ok || exitErr.ExitCode() >= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
status := exitErr.Sys().(syscall.WaitStatus)
|
||||||
|
return status.Signal() == syscall.SIGINT
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,3 +29,7 @@ func setWorkerComm(cmd *exec.Cmd, comm workerComm) {
|
||||||
func getWorkerComm() (comm workerComm, err error) {
|
func getWorkerComm() (comm workerComm, err error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isInterruptError(err error) bool {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,3 +131,8 @@ func getWorkerComm() (comm workerComm, err error) {
|
||||||
|
|
||||||
return workerComm{fuzzIn: fuzzIn, fuzzOut: fuzzOut, mem: mem}, nil
|
return workerComm{fuzzIn: fuzzIn, fuzzOut: fuzzOut, mem: mem}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isInterruptError(err error) bool {
|
||||||
|
// TODO(jayconrod): implement
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,19 +80,32 @@ func (w *worker) runFuzzing() error {
|
||||||
select {
|
select {
|
||||||
case <-w.coordinator.doneC:
|
case <-w.coordinator.doneC:
|
||||||
// All workers were told to stop.
|
// All workers were told to stop.
|
||||||
return w.stop()
|
err := w.stop()
|
||||||
|
if isInterruptError(err) {
|
||||||
|
// Worker interrupted by SIGINT. This can happen if the worker receives
|
||||||
|
// SIGINT before installing the signal handler. That's likely if
|
||||||
|
// TestMain or the fuzz target setup takes a long time.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
|
||||||
case <-w.termC:
|
case <-w.termC:
|
||||||
// Worker process terminated unexpectedly, so inform the coordinator
|
// Worker process terminated unexpectedly.
|
||||||
// that a crash occurred.
|
if isInterruptError(w.waitErr) {
|
||||||
|
// Worker interrupted by SIGINT. See comment in doneC case.
|
||||||
|
w.stop()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unexpected termination. Inform the coordinator about the crash.
|
||||||
|
// TODO(jayconrod,katiehockman): if -keepfuzzing, restart worker.
|
||||||
value := w.mem.valueCopy()
|
value := w.mem.valueCopy()
|
||||||
|
message := fmt.Sprintf("fuzzing process terminated unexpectedly: %v", w.waitErr)
|
||||||
crasher := crasherEntry{
|
crasher := crasherEntry{
|
||||||
corpusEntry: corpusEntry{b: value},
|
corpusEntry: corpusEntry{b: value},
|
||||||
errMsg: "fuzzing process crashed unexpectedly",
|
errMsg: message,
|
||||||
}
|
}
|
||||||
w.coordinator.crasherC <- crasher
|
w.coordinator.crasherC <- crasher
|
||||||
|
|
||||||
// TODO(jayconrod,katiehockman): if -keepfuzzing, restart worker.
|
|
||||||
err := w.stop()
|
err := w.stop()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = fmt.Errorf("worker exited unexpectedly")
|
err = fmt.Errorf("worker exited unexpectedly")
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ func (TestDeps) CoordinateFuzzing(timeout time.Duration, parallel int, seed [][]
|
||||||
<-interruptC
|
<-interruptC
|
||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
defer close(interruptC)
|
defer func() { interruptC <- os.Interrupt }()
|
||||||
|
|
||||||
err := fuzz.CoordinateFuzzing(ctx, parallel, seed, corpusDir, cacheDir)
|
err := fuzz.CoordinateFuzzing(ctx, parallel, seed, corpusDir, cacheDir)
|
||||||
if err == ctx.Err() {
|
if err == ctx.Err() {
|
||||||
|
|
@ -169,7 +169,7 @@ func (TestDeps) RunFuzzWorker(fn func([]byte) error) error {
|
||||||
<-interruptC
|
<-interruptC
|
||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
defer close(interruptC)
|
defer func() { interruptC <- os.Interrupt }()
|
||||||
|
|
||||||
err := fuzz.RunFuzzWorker(ctx, fn)
|
err := fuzz.RunFuzzWorker(ctx, fn)
|
||||||
if err == ctx.Err() {
|
if err == ctx.Err() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue