2015-10-12 10:16:57 +02:00
// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
package ipc
import (
2015-10-13 11:55:19 +02:00
"bytes"
2016-01-27 14:11:23 +01:00
"flag"
2015-10-12 10:16:57 +02:00
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
2015-11-23 14:31:33 +01:00
"sync/atomic"
2015-10-12 10:16:57 +02:00
"syscall"
"time"
2016-10-04 16:12:12 +02:00
"unsafe"
2015-10-13 11:34:13 +02:00
2016-01-11 17:28:34 +01:00
"github.com/google/syzkaller/fileutil"
2015-10-13 11:34:13 +02:00
"github.com/google/syzkaller/prog"
2015-10-12 10:16:57 +02:00
)
type Env struct {
In [ ] byte
Out [ ] byte
2015-11-10 20:30:50 +01:00
cmd * command
2015-10-12 10:16:57 +02:00
inFile * os . File
outFile * os . File
bin [ ] string
2016-11-25 20:17:32 +01:00
pid int
2017-05-19 12:00:36 -07:00
config Config
2015-11-19 19:02:30 +01:00
StatExecs uint64
StatRestarts uint64
2015-10-12 10:16:57 +02:00
}
2017-05-19 12:00:36 -07:00
// Configuration flags for Config.Flags.
2015-10-12 10:16:57 +02:00
const (
2016-07-01 22:20:47 +02:00
FlagDebug = uint64 ( 1 ) << iota // debug output from executor
2017-01-25 22:18:42 +01:00
FlagSignal // collect feedback signals (coverage)
2016-07-01 22:20:47 +02:00
FlagThreaded // use multiple threads to mitigate blocked syscalls
FlagCollide // collide syscalls to provoke data races
FlagSandboxSetuid // impersonate nobody user
FlagSandboxNamespace // use namespaces for sandboxing
2016-12-02 13:18:42 +01:00
FlagEnableTun // initialize and use tun in executor
2017-05-19 12:00:36 -07:00
)
2017-01-25 22:18:42 +01:00
2017-05-19 12:00:36 -07:00
const (
2017-01-25 22:18:42 +01:00
outputSize = 16 << 20
signalOffset = 15 << 20
2017-02-27 14:46:32 +01:00
statusFail = 67
statusError = 68
statusRetry = 69
2015-10-12 10:16:57 +02:00
)
2016-01-27 14:11:23 +01:00
var (
flagThreaded = flag . Bool ( "threaded" , true , "use threaded mode in executor" )
flagCollide = flag . Bool ( "collide" , true , "collide syscalls to provoke data races" )
2017-01-25 22:18:42 +01:00
flagSignal = flag . Bool ( "cover" , true , "collect feedback signals (coverage)" )
2016-07-01 22:20:47 +02:00
flagSandbox = flag . String ( "sandbox" , "setuid" , "sandbox for fuzzing (none/setuid/namespace)" )
2016-01-27 14:11:23 +01:00
flagDebug = flag . Bool ( "debug" , false , "debug output from executor" )
2016-03-01 15:12:45 +01:00
// Executor protects against most hangs, so we use quite large timeout here.
// Executor can be slow due to global locks in namespaces and other things,
// so let's better wait than report false misleading crashes.
2017-05-19 12:00:36 -07:00
flagTimeout = flag . Duration ( "timeout" , 1 * time . Minute , "execution timeout" )
flagAbortSignal = flag . Int ( "abort_signal" , int ( syscall . SIGKILL ) , "initial signal to send to executor in error conditions; upgrades to SIGKILL if executor does not exit" )
2017-05-19 15:55:00 -07:00
flagBufferSize = flag . Uint64 ( "buffer_size" , 128 << 10 , "internal buffer size (in bytes) for executor output" )
2016-01-27 14:11:23 +01:00
)
2016-11-29 15:19:29 +01:00
// ExecutorFailure is returned from MakeEnv or from env.Exec when executor terminates by calling fail function.
// This is considered a logical error (a failed assert).
type ExecutorFailure string
func ( err ExecutorFailure ) Error ( ) string {
return string ( err )
}
2017-05-19 12:00:36 -07:00
// Config is the configuration for Env.
type Config struct {
// Flags are configuation flags, defined above.
Flags uint64
// Timeout is the execution timeout for a single program.
Timeout time . Duration
// AbortSignal is the signal to send to the executor in error
// conditions.
AbortSignal syscall . Signal
2017-05-19 15:55:00 -07:00
// BufferSize is the size of the internal buffer for executor output.
BufferSize uint64
2017-05-19 12:00:36 -07:00
}
func DefaultConfig ( ) ( Config , error ) {
var c Config
2016-01-27 14:11:23 +01:00
if * flagThreaded {
2017-05-19 12:00:36 -07:00
c . Flags |= FlagThreaded
2016-01-27 14:11:23 +01:00
}
if * flagCollide {
2017-05-19 12:00:36 -07:00
c . Flags |= FlagCollide
2016-01-27 14:11:23 +01:00
}
2017-01-25 22:18:42 +01:00
if * flagSignal {
2017-05-19 12:00:36 -07:00
c . Flags |= FlagSignal
2016-01-27 14:11:23 +01:00
}
2016-07-01 22:20:47 +02:00
switch * flagSandbox {
case "none" :
case "setuid" :
2017-05-19 12:00:36 -07:00
c . Flags |= FlagSandboxSetuid
2016-07-01 22:20:47 +02:00
case "namespace" :
2017-05-19 12:00:36 -07:00
c . Flags |= FlagSandboxNamespace
2016-07-01 22:20:47 +02:00
default :
2017-05-19 12:00:36 -07:00
return Config { } , fmt . Errorf ( "flag sandbox must contain one of none/setuid/namespace" )
2016-01-27 14:11:23 +01:00
}
if * flagDebug {
2017-05-19 12:00:36 -07:00
c . Flags |= FlagDebug
2016-01-27 14:11:23 +01:00
}
2017-05-19 12:00:36 -07:00
c . Timeout = * flagTimeout
c . AbortSignal = syscall . Signal ( * flagAbortSignal )
2017-05-19 15:55:00 -07:00
c . BufferSize = * flagBufferSize
2017-05-19 12:00:36 -07:00
return c , nil
2016-01-27 14:11:23 +01:00
}
2017-05-19 12:00:36 -07:00
func MakeEnv ( bin string , pid int , config Config ) ( * Env , error ) {
2015-12-28 13:01:54 +01:00
// IPC timeout must be larger then executor timeout.
// Otherwise IPC will kill parent executor but leave child executor alive.
2017-05-19 12:00:36 -07:00
if config . Timeout < 7 * time . Second {
config . Timeout = 7 * time . Second
2015-12-28 13:01:54 +01:00
}
2017-01-20 23:55:25 +01:00
inf , inmem , err := createMapping ( prog . ExecBufferSize )
2015-10-12 10:16:57 +02:00
if err != nil {
return nil , err
}
2015-10-20 17:43:02 +02:00
defer func ( ) {
if inf != nil {
closeMapping ( inf , inmem )
}
} ( )
2017-01-25 22:18:42 +01:00
outf , outmem , err := createMapping ( outputSize )
2015-10-12 10:16:57 +02:00
if err != nil {
return nil , err
}
2015-10-20 17:43:02 +02:00
defer func ( ) {
if outf != nil {
closeMapping ( outf , outmem )
}
} ( )
2015-10-12 10:16:57 +02:00
for i := 0 ; i < 8 ; i ++ {
2017-05-19 12:00:36 -07:00
inmem [ i ] = byte ( config . Flags >> ( 8 * uint ( i ) ) )
2015-10-12 10:16:57 +02:00
}
2016-10-04 16:12:12 +02:00
* ( * uint64 ) ( unsafe . Pointer ( & inmem [ 8 ] ) ) = uint64 ( pid )
inmem = inmem [ 16 : ]
2015-10-12 10:16:57 +02:00
env := & Env {
In : inmem ,
Out : outmem ,
inFile : inf ,
outFile : outf ,
bin : strings . Split ( bin , " " ) ,
2016-11-25 20:17:32 +01:00
pid : pid ,
2017-05-19 12:00:36 -07:00
config : config ,
2015-10-12 10:16:57 +02:00
}
if len ( env . bin ) == 0 {
return nil , fmt . Errorf ( "binary is empty string" )
}
2015-11-10 20:30:50 +01:00
env . bin [ 0 ] , err = filepath . Abs ( env . bin [ 0 ] ) // we are going to chdir
if err != nil {
return nil , fmt . Errorf ( "filepath.Abs failed: %v" , err )
}
2016-11-26 17:04:23 +01:00
// Append pid to binary name.
// E.g. if binary is 'syz-executor' and pid=15,
// we create a link from 'syz-executor15' to 'syz-executor' and use 'syz-executor15' as binary.
// This allows to easily identify program that lead to a crash in the log.
// Log contains pid in "executing program 15" and crashes usually contain "Comm: syz-executor15".
base := filepath . Base ( env . bin [ 0 ] )
pidStr := fmt . Sprint ( pid )
if len ( base ) + len ( pidStr ) >= 16 {
// TASK_COMM_LEN is currently set to 16
base = base [ : 15 - len ( pidStr ) ]
}
binCopy := filepath . Join ( filepath . Dir ( env . bin [ 0 ] ) , base + pidStr )
if err := os . Link ( env . bin [ 0 ] , binCopy ) ; err == nil {
env . bin [ 0 ] = binCopy
}
2015-10-20 17:43:02 +02:00
inf = nil
outf = nil
2015-10-12 10:16:57 +02:00
return env , nil
}
func ( env * Env ) Close ( ) error {
2015-11-10 20:30:50 +01:00
if env . cmd != nil {
env . cmd . close ( )
}
2015-10-12 10:16:57 +02:00
err1 := closeMapping ( env . inFile , env . In )
err2 := closeMapping ( env . outFile , env . Out )
switch {
case err1 != nil :
return err1
case err2 != nil :
return err2
default :
return nil
}
}
2017-01-25 22:18:42 +01:00
type CallInfo struct {
Signal [ ] uint32 // feedback signal, filled if FlagSignal is set
Cover [ ] uint32 // per-call coverage, filled if FlagSignal is set and cover == true,
//if dedup == false, then cov effectively contains a trace, otherwise duplicates are removed
Errno int // call errno (0 if the call was successful)
}
2015-10-13 11:55:19 +02:00
// Exec starts executor binary to execute program p and returns information about the execution:
// output: process output
2017-01-25 22:18:42 +01:00
// info: per-call info
2015-10-13 11:55:19 +02:00
// failed: true if executor has detected a kernel bug
// hanged: program hanged and was killed
// err0: failed to start process, or executor has detected a logical error
2017-01-25 22:18:42 +01:00
func ( env * Env ) Exec ( p * prog . Prog , cover , dedup bool ) ( output [ ] byte , info [ ] CallInfo , failed , hanged bool , err0 error ) {
2015-10-13 11:34:13 +02:00
if p != nil {
2015-10-13 11:55:19 +02:00
// Copy-in serialized program.
2017-01-20 23:55:25 +01:00
if err := p . SerializeForExec ( env . In , env . pid ) ; err != nil {
err0 = fmt . Errorf ( "executor %v: failed to serialize: %v" , env . pid , err )
2016-12-08 15:06:23 +01:00
return
2015-10-13 11:34:13 +02:00
}
}
2017-05-19 12:00:36 -07:00
if env . config . Flags & FlagSignal != 0 {
2017-01-25 22:18:42 +01:00
// Zero out the first two words (ncmd and nsig), so that we don't have garbage there
2015-10-13 11:55:19 +02:00
// if executor crashes before writing non-garbage there.
for i := 0 ; i < 4 ; i ++ {
env . Out [ i ] = 0
}
}
2015-11-23 14:31:33 +01:00
atomic . AddUint64 ( & env . StatExecs , 1 )
2015-11-10 20:30:50 +01:00
if env . cmd == nil {
2015-11-23 14:31:33 +01:00
atomic . AddUint64 ( & env . StatRestarts , 1 )
2017-05-19 12:00:36 -07:00
env . cmd , err0 = makeCommand ( env . pid , env . bin , env . config , env . inFile , env . outFile )
2015-11-10 20:30:50 +01:00
if err0 != nil {
return
}
}
2016-03-09 12:07:15 +01:00
var restart bool
2017-01-25 22:18:42 +01:00
output , failed , hanged , restart , err0 = env . cmd . exec ( cover , dedup )
2016-03-09 12:07:15 +01:00
if err0 != nil || restart {
2015-11-10 20:30:50 +01:00
env . cmd . close ( )
env . cmd = nil
2015-10-13 11:55:19 +02:00
return
}
2017-05-19 12:00:36 -07:00
if env . config . Flags & FlagSignal == 0 || p == nil {
2015-10-13 11:55:19 +02:00
return
2015-10-13 11:34:13 +02:00
}
2017-01-25 22:18:42 +01:00
info , err0 = env . readOutCoverage ( p )
2017-01-20 23:55:25 +01:00
return
}
2017-01-25 22:18:42 +01:00
func ( env * Env ) readOutCoverage ( p * prog . Prog ) ( info [ ] CallInfo , err0 error ) {
2017-01-20 23:55:25 +01:00
out := ( ( * [ 1 << 28 ] uint32 ) ( unsafe . Pointer ( & env . Out [ 0 ] ) ) ) [ : len ( env . Out ) / int ( unsafe . Sizeof ( uint32 ( 0 ) ) ) ]
readOut := func ( v * uint32 ) bool {
if len ( out ) == 0 {
return false
}
* v = out [ 0 ]
out = out [ 1 : ]
return true
}
2015-10-13 11:55:19 +02:00
var ncmd uint32
2017-01-20 23:55:25 +01:00
if ! readOut ( & ncmd ) {
err0 = fmt . Errorf ( "executor %v: failed to read output coverage" , env . pid )
2015-10-13 11:55:19 +02:00
return
}
2017-01-25 22:18:42 +01:00
info = make ( [ ] CallInfo , len ( p . Calls ) )
for i := range info {
info [ i ] . Errno = - 1 // not executed
2015-12-17 17:11:08 +01:00
}
2016-12-08 15:06:23 +01:00
dumpCov := func ( ) string {
buf := new ( bytes . Buffer )
2017-01-25 22:18:42 +01:00
for i , inf := range info {
2016-12-08 15:06:23 +01:00
str := "nil"
2017-01-25 22:18:42 +01:00
if inf . Signal != nil {
str = fmt . Sprint ( len ( inf . Signal ) )
2016-12-08 15:06:23 +01:00
}
fmt . Fprintf ( buf , "%v:%v|" , i , str )
}
return buf . String ( )
}
2015-10-13 11:55:19 +02:00
for i := uint32 ( 0 ) ; i < ncmd ; i ++ {
2017-01-25 22:18:42 +01:00
var callIndex , callNum , errno , signalSize , coverSize uint32
if ! readOut ( & callIndex ) || ! readOut ( & callNum ) || ! readOut ( & errno ) || ! readOut ( & signalSize ) || ! readOut ( & coverSize ) {
2017-01-20 23:55:25 +01:00
err0 = fmt . Errorf ( "executor %v: failed to read output coverage" , env . pid )
2015-10-13 11:55:19 +02:00
return
}
2017-01-25 22:18:42 +01:00
if int ( callIndex ) >= len ( info ) {
err0 = fmt . Errorf ( "executor %v: failed to read output coverage: record %v, call %v, total calls %v (cov: %v)" ,
env . pid , i , callIndex , len ( info ) , dumpCov ( ) )
2015-10-13 11:55:19 +02:00
return
}
2017-01-25 22:18:42 +01:00
c := p . Calls [ callIndex ]
if num := c . Meta . ID ; uint32 ( num ) != callNum {
2017-02-06 15:58:22 +01:00
err0 = fmt . Errorf ( "executor %v: failed to read output coverage: record %v call %v: expect syscall %v, got %v, executed %v (cov: %v)" ,
env . pid , i , callIndex , num , callNum , ncmd , dumpCov ( ) )
2015-10-13 11:55:19 +02:00
return
}
2017-01-25 22:18:42 +01:00
if info [ callIndex ] . Signal != nil {
2016-12-08 15:06:23 +01:00
err0 = fmt . Errorf ( "executor %v: failed to read output coverage: double coverage for call %v (cov: %v)" ,
env . pid , callIndex , dumpCov ( ) )
2015-10-13 11:55:19 +02:00
return
}
2017-01-25 22:18:42 +01:00
info [ callIndex ] . Errno = int ( errno )
if signalSize > uint32 ( len ( out ) ) {
err0 = fmt . Errorf ( "executor %v: failed to read output signal: record %v, call %v, signalsize=%v coversize=%v" ,
env . pid , i , callIndex , signalSize , coverSize )
2015-10-13 11:55:19 +02:00
return
}
2017-01-25 22:18:42 +01:00
info [ callIndex ] . Signal = out [ : signalSize : signalSize ]
out = out [ signalSize : ]
2017-01-20 23:55:25 +01:00
if coverSize > uint32 ( len ( out ) ) {
2017-01-25 22:18:42 +01:00
err0 = fmt . Errorf ( "executor %v: failed to read output coverage: record %v, call %v, signalsize=%v coversize=%v" ,
env . pid , i , callIndex , signalSize , coverSize )
2017-01-20 23:55:25 +01:00
return
2015-10-13 11:55:19 +02:00
}
2017-01-25 22:18:42 +01:00
info [ callIndex ] . Cover = out [ : coverSize : coverSize ]
2017-01-20 23:55:25 +01:00
out = out [ coverSize : ]
2015-10-13 11:55:19 +02:00
}
return
}
2015-10-12 10:16:57 +02:00
func createMapping ( size int ) ( f * os . File , mem [ ] byte , err error ) {
f , err = ioutil . TempFile ( "./" , "syzkaller-shm" )
if err != nil {
2015-10-16 13:32:08 +02:00
err = fmt . Errorf ( "failed to create temp file: %v" , err )
2015-10-12 10:16:57 +02:00
return
}
2015-10-13 11:34:13 +02:00
if err = f . Truncate ( int64 ( size ) ) ; err != nil {
2015-10-16 13:32:08 +02:00
err = fmt . Errorf ( "failed to truncate shm file: %v" , err )
2015-10-12 10:16:57 +02:00
f . Close ( )
os . Remove ( f . Name ( ) )
return
}
f . Close ( )
2015-12-16 17:01:32 +01:00
fname := f . Name ( )
2015-10-12 10:16:57 +02:00
f , err = os . OpenFile ( f . Name ( ) , os . O_RDWR , 0 )
if err != nil {
2015-10-16 13:32:08 +02:00
err = fmt . Errorf ( "failed to open shm file: %v" , err )
2015-12-16 17:01:32 +01:00
os . Remove ( fname )
2015-10-12 10:16:57 +02:00
return
}
mem , err = syscall . Mmap ( int ( f . Fd ( ) ) , 0 , size , syscall . PROT_READ | syscall . PROT_WRITE , syscall . MAP_SHARED )
if err != nil {
2015-10-16 13:32:08 +02:00
err = fmt . Errorf ( "failed to mmap shm file: %v" , err )
2015-10-12 10:16:57 +02:00
f . Close ( )
os . Remove ( f . Name ( ) )
return
}
return
}
func closeMapping ( f * os . File , mem [ ] byte ) error {
err1 := syscall . Munmap ( mem )
err2 := f . Close ( )
err3 := os . Remove ( f . Name ( ) )
switch {
case err1 != nil :
return err1
case err2 != nil :
return err2
case err3 != nil :
return err3
default :
return nil
}
}
2015-11-10 20:30:50 +01:00
type command struct {
2016-12-08 15:06:23 +01:00
pid int
2017-05-19 12:00:36 -07:00
config Config
2016-08-13 14:44:46 -07:00
cmd * exec . Cmd
dir string
readDone chan [ ] byte
2017-05-19 12:00:36 -07:00
exited chan struct { }
2016-08-13 14:44:46 -07:00
inrp * os . File
outwp * os . File
2015-11-10 20:30:50 +01:00
}
2017-05-19 12:00:36 -07:00
func makeCommand ( pid int , bin [ ] string , config Config , inFile * os . File , outFile * os . File ) ( * command , error ) {
2015-11-10 20:30:50 +01:00
dir , err := ioutil . TempDir ( "./" , "syzkaller-testdir" )
if err != nil {
return nil , fmt . Errorf ( "failed to create temp dir: %v" , err )
}
2016-12-08 15:06:23 +01:00
c := & command {
2017-05-19 12:00:36 -07:00
pid : pid ,
config : config ,
dir : dir ,
2016-12-08 15:06:23 +01:00
}
2015-11-10 20:30:50 +01:00
defer func ( ) {
if c != nil {
c . close ( )
}
} ( )
2017-05-19 12:00:36 -07:00
if config . Flags & ( FlagSandboxSetuid | FlagSandboxNamespace ) != 0 {
2015-12-16 17:01:32 +01:00
if err := os . Chmod ( dir , 0777 ) ; err != nil {
return nil , fmt . Errorf ( "failed to chmod temp dir: %v" , err )
}
}
2015-11-10 20:30:50 +01:00
// Output capture pipe.
rp , wp , err := os . Pipe ( )
if err != nil {
return nil , fmt . Errorf ( "failed to create pipe: %v" , err )
}
defer wp . Close ( )
// Input command pipe.
inrp , inwp , err := os . Pipe ( )
if err != nil {
return nil , fmt . Errorf ( "failed to create pipe: %v" , err )
}
defer inwp . Close ( )
c . inrp = inrp
// Output command pipe.
outrp , outwp , err := os . Pipe ( )
if err != nil {
return nil , fmt . Errorf ( "failed to create pipe: %v" , err )
}
defer outrp . Close ( )
c . outwp = outwp
2016-08-13 14:44:46 -07:00
c . readDone = make ( chan [ ] byte , 1 )
2017-05-19 12:00:36 -07:00
c . exited = make ( chan struct { } )
2016-08-13 14:44:46 -07:00
2015-11-10 20:30:50 +01:00
cmd := exec . Command ( bin [ 0 ] , bin [ 1 : ] ... )
cmd . ExtraFiles = [ ] * os . File { inFile , outFile , outrp , inwp }
cmd . Env = [ ] string { }
cmd . Dir = dir
2017-05-19 12:00:36 -07:00
if config . Flags & FlagDebug == 0 {
2015-11-10 20:30:50 +01:00
cmd . Stdout = wp
cmd . Stderr = wp
2016-08-13 14:44:46 -07:00
go func ( c * command ) {
// Read out output in case executor constantly prints something.
2017-05-19 15:55:00 -07:00
output := make ( [ ] byte , c . config . BufferSize )
var size uint64
2016-08-13 14:44:46 -07:00
for {
n , err := rp . Read ( output [ size : ] )
if n > 0 {
2017-05-19 15:55:00 -07:00
size += uint64 ( n )
if size >= c . config . BufferSize * 3 / 4 {
copy ( output , output [ size - c . config . BufferSize / 2 : size ] )
size = c . config . BufferSize / 2
2016-08-13 14:44:46 -07:00
}
}
if err != nil {
rp . Close ( )
c . readDone <- output [ : size ]
close ( c . readDone )
return
}
}
} ( c )
2015-11-10 20:30:50 +01:00
} else {
2017-02-01 11:00:01 +01:00
close ( c . readDone )
2015-11-10 20:30:50 +01:00
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stdout
}
if err := cmd . Start ( ) ; err != nil {
return nil , fmt . Errorf ( "failed to start executor binary: %v" , err )
}
c . cmd = cmd
2016-11-29 15:19:29 +01:00
wp . Close ( )
inwp . Close ( )
2016-01-27 14:44:15 +01:00
if err := c . waitServing ( ) ; err != nil {
return nil , err
}
2015-11-10 20:30:50 +01:00
tmp := c
c = nil // disable defer above
return tmp , nil
}
func ( c * command ) close ( ) {
2015-11-23 14:31:33 +01:00
if c . cmd != nil {
2017-05-19 12:00:36 -07:00
c . abort ( )
c . wait ( )
2015-11-23 14:31:33 +01:00
}
2016-01-11 17:28:34 +01:00
fileutil . UmountAll ( c . dir )
2015-11-10 20:30:50 +01:00
os . RemoveAll ( c . dir )
if c . inrp != nil {
c . inrp . Close ( )
}
if c . outwp != nil {
c . outwp . Close ( )
}
}
2016-01-27 14:44:15 +01:00
// Wait for executor to start serving (sandbox setup can take significant time).
func ( c * command ) waitServing ( ) error {
read := make ( chan error , 1 )
go func ( ) {
var buf [ 1 ] byte
_ , err := c . inrp . Read ( buf [ : ] )
read <- err
} ( )
timeout := time . NewTimer ( time . Minute )
select {
case err := <- read :
timeout . Stop ( )
2016-11-29 15:19:29 +01:00
if err != nil {
2017-05-19 12:00:36 -07:00
c . abort ( )
2016-11-29 15:19:29 +01:00
output := <- c . readDone
err = fmt . Errorf ( "executor is not serving: %v\n%s" , err , output )
2017-05-19 12:00:36 -07:00
c . wait ( )
2016-11-29 15:19:29 +01:00
if c . cmd . ProcessState != nil {
sys := c . cmd . ProcessState . Sys ( )
if ws , ok := sys . ( syscall . WaitStatus ) ; ok {
// Magic values returned by executor.
2017-02-27 14:46:32 +01:00
if ws . ExitStatus ( ) == statusFail {
2016-11-29 15:19:29 +01:00
err = ExecutorFailure ( fmt . Sprintf ( "executor is not serving:\n%s" , output ) )
}
}
}
}
2016-01-27 14:44:15 +01:00
return err
case <- timeout . C :
return fmt . Errorf ( "executor is not serving" )
}
}
2017-05-19 12:00:36 -07:00
// abort sends the abort signal to the command and then SIGKILL if wait doesn't
// return within 5s.
func ( c * command ) abort ( ) {
syscall . Kill ( c . cmd . Process . Pid , c . config . AbortSignal )
if c . config . AbortSignal != syscall . SIGKILL {
go func ( ) {
t := time . NewTimer ( 5 * time . Second )
select {
case <- t . C :
syscall . Kill ( c . cmd . Process . Pid , syscall . SIGKILL )
case <- c . exited :
t . Stop ( )
}
} ( )
}
}
func ( c * command ) wait ( ) error {
err := c . cmd . Wait ( )
select {
case <- c . exited :
// c.exited closed by an earlier call to wait.
default :
close ( c . exited )
}
return err
2015-11-10 20:30:50 +01:00
}
2017-01-25 22:18:42 +01:00
func ( c * command ) exec ( cover , dedup bool ) ( output [ ] byte , failed , hanged , restart bool , err0 error ) {
var flags [ 1 ] byte
if cover {
flags [ 0 ] |= 1 << 0
if dedup {
flags [ 0 ] |= 1 << 1
}
}
if _ , err := c . outwp . Write ( flags [ : ] ) ; err != nil {
2016-08-13 14:44:46 -07:00
output = <- c . readDone
2015-11-10 20:30:50 +01:00
err0 = fmt . Errorf ( "failed to write control pipe: %v" , err )
return
}
done := make ( chan bool )
hang := make ( chan bool )
go func ( ) {
2017-05-19 12:00:36 -07:00
t := time . NewTimer ( c . config . Timeout )
2015-11-10 20:30:50 +01:00
select {
case <- t . C :
2017-05-19 12:00:36 -07:00
c . abort ( )
2015-11-10 20:30:50 +01:00
hang <- true
case <- done :
t . Stop ( )
hang <- false
}
} ( )
2017-01-25 22:18:42 +01:00
readN , readErr := c . inrp . Read ( flags [ : ] )
2015-11-10 20:30:50 +01:00
close ( done )
2017-02-27 14:46:32 +01:00
status := 0
2015-11-10 20:30:50 +01:00
if readErr == nil {
2017-01-25 22:18:42 +01:00
if readN != len ( flags ) {
2016-12-08 15:06:23 +01:00
panic ( fmt . Sprintf ( "executor %v: read only %v bytes" , c . pid , readN ) )
}
2017-02-27 14:46:32 +01:00
status = int ( flags [ 0 ] )
if status == 0 {
<- hang
return
}
// Executor writes magic values into the pipe before exiting,
// so proceed with killing and joining it.
status = int ( flags [ 0 ] )
2015-11-10 20:30:50 +01:00
}
err0 = fmt . Errorf ( "executor did not answer" )
2017-05-19 12:00:36 -07:00
c . abort ( )
2016-08-13 14:44:46 -07:00
output = <- c . readDone
2017-05-19 12:00:36 -07:00
if err := c . wait ( ) ; <- hang && err != nil {
2015-11-10 20:30:50 +01:00
hanged = true
output = append ( output , [ ] byte ( err . Error ( ) ) ... )
output = append ( output , '\n' )
}
2017-02-27 14:46:32 +01:00
switch status {
case statusFail , statusError , statusRetry :
default :
if c . cmd . ProcessState != nil {
sys := c . cmd . ProcessState . Sys ( )
if ws , ok := sys . ( syscall . WaitStatus ) ; ok {
status = ws . ExitStatus ( )
2016-03-09 12:07:15 +01:00
}
2015-11-10 20:30:50 +01:00
}
}
2017-02-27 14:46:32 +01:00
// Handle magic values returned by executor.
switch status {
case statusFail :
err0 = ExecutorFailure ( fmt . Sprintf ( "executor failed: %s" , output ) )
case statusError :
failed = true
case statusRetry :
// This is a temporal error (ENOMEM) or an unfortunate
// program that messes with testing setup (e.g. kills executor
// loop process). Pretend that nothing happened.
// It's better than a false crash report.
err0 = nil
hanged = false
restart = true
}
2015-11-10 20:30:50 +01:00
return
}