aboutsummaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-06-17 12:47:35 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-06-17 14:41:15 +0200
commite8e63830a60fc048df976188bf726abc32045162 (patch)
tree9e38c6b9d3e6cbaeba29cc5b29ed36aac0706d3d /ipc
parentbaad3b4b027658f49ba318c41dfc745788ba87d1 (diff)
pkg/ipc: move from ipc
Diffstat (limited to 'ipc')
-rw-r--r--ipc/gate.go76
-rw-r--r--ipc/ipc.go678
-rw-r--r--ipc/ipc_test.go108
3 files changed, 0 insertions, 862 deletions
diff --git a/ipc/gate.go b/ipc/gate.go
deleted file mode 100644
index b1b1f1fc8..000000000
--- a/ipc/gate.go
+++ /dev/null
@@ -1,76 +0,0 @@
-// 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 (
- "sync"
-)
-
-// Gate limits concurrency level and window to the given value.
-// Limitation of concurrency window means that if a very old activity is still
-// running it will not let new activities to start even if concurrency level is low.
-type Gate struct {
- cv *sync.Cond
- busy []bool
- pos int
- running int
- stop bool
- f func()
-}
-
-// If f is not nil, it will be called after each batch of c activities.
-func NewGate(c int, f func()) *Gate {
- return &Gate{
- cv: sync.NewCond(new(sync.Mutex)),
- busy: make([]bool, c),
- f: f,
- }
-}
-
-func (g *Gate) Enter() int {
- g.cv.L.Lock()
- for g.busy[g.pos] || g.stop {
- g.cv.Wait()
- }
- idx := g.pos
- g.pos++
- if g.pos >= len(g.busy) {
- g.pos = 0
- }
- g.busy[idx] = true
- g.running++
- if g.running > len(g.busy) {
- panic("broken gate")
- }
- g.cv.L.Unlock()
- return idx
-}
-
-func (g *Gate) Leave(idx int) {
- g.cv.L.Lock()
- if !g.busy[idx] {
- panic("broken gate")
- }
- g.busy[idx] = false
- g.running--
- if g.running < 0 {
- panic("broken gate")
- }
- if idx == 0 && g.f != nil {
- if g.stop {
- panic("broken gate")
- }
- g.stop = true
- for g.running != 0 {
- g.cv.Wait()
- }
- g.stop = false
- g.f()
- g.cv.Broadcast()
- }
- if idx == g.pos && !g.stop || g.running == 0 && g.stop {
- g.cv.Broadcast()
- }
- g.cv.L.Unlock()
-}
diff --git a/ipc/ipc.go b/ipc/ipc.go
deleted file mode 100644
index 6c68433b6..000000000
--- a/ipc/ipc.go
+++ /dev/null
@@ -1,678 +0,0 @@
-// 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 (
- "bytes"
- "flag"
- "fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
- "sync"
- "sync/atomic"
- "syscall"
- "time"
- "unsafe"
-
- "github.com/google/syzkaller/pkg/fileutil"
- "github.com/google/syzkaller/prog"
-)
-
-type Env struct {
- In []byte
- Out []byte
-
- cmd *command
- inFile *os.File
- outFile *os.File
- bin []string
- pid int
- config Config
-
- StatExecs uint64
- StatRestarts uint64
-}
-
-// Configuration flags for Config.Flags.
-const (
- FlagDebug = uint64(1) << iota // debug output from executor
- FlagSignal // collect feedback signals (coverage)
- FlagThreaded // use multiple threads to mitigate blocked syscalls
- FlagCollide // collide syscalls to provoke data races
- FlagSandboxSetuid // impersonate nobody user
- FlagSandboxNamespace // use namespaces for sandboxing
- FlagEnableTun // initialize and use tun in executor
- FlagEnableFault // enable fault injection support
-)
-
-// Per-exec flags for ExecOpts.Flags:
-const (
- FlagCollectCover = uint64(1) << iota // collect coverage
- FlagDedupCover // deduplicate coverage in executor
- FlagInjectFault // inject a fault in this execution (see ExecOpts)
-)
-
-const (
- outputSize = 16 << 20
- signalOffset = 15 << 20
-
- statusFail = 67
- statusError = 68
- statusRetry = 69
-)
-
-var (
- flagThreaded = flag.Bool("threaded", true, "use threaded mode in executor")
- flagCollide = flag.Bool("collide", true, "collide syscalls to provoke data races")
- flagSignal = flag.Bool("cover", true, "collect feedback signals (coverage)")
- flagSandbox = flag.String("sandbox", "setuid", "sandbox for fuzzing (none/setuid/namespace)")
- flagDebug = flag.Bool("debug", false, "debug output from executor")
- // 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.
- flagTimeout = flag.Duration("timeout", 1*time.Minute, "execution timeout")
- flagAbortSignal = flag.Int("abort_signal", 0, "initial signal to send to executor in error conditions; upgrades to SIGKILL if executor does not exit")
- flagBufferSize = flag.Uint64("buffer_size", 0, "internal buffer size (in bytes) for executor output")
-)
-
-type ExecOpts struct {
- Flags uint64
- FaultCall int // call index for fault injection (0-based)
- FaultNth int // fault n-th operation in the call (0-based)
-}
-
-// 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)
-}
-
-// 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
-
- // BufferSize is the size of the internal buffer for executor output.
- BufferSize uint64
-}
-
-func DefaultConfig() (Config, error) {
- var c Config
- if *flagThreaded {
- c.Flags |= FlagThreaded
- }
- if *flagCollide {
- c.Flags |= FlagCollide
- }
- if *flagSignal {
- c.Flags |= FlagSignal
- }
- switch *flagSandbox {
- case "none":
- case "setuid":
- c.Flags |= FlagSandboxSetuid
- case "namespace":
- c.Flags |= FlagSandboxNamespace
- default:
- return Config{}, fmt.Errorf("flag sandbox must contain one of none/setuid/namespace")
- }
- if *flagDebug {
- c.Flags |= FlagDebug
- }
- c.Timeout = *flagTimeout
- c.AbortSignal = syscall.Signal(*flagAbortSignal)
- c.BufferSize = *flagBufferSize
- return c, nil
-}
-
-func MakeEnv(bin string, pid int, config Config) (*Env, error) {
- // IPC timeout must be larger then executor timeout.
- // Otherwise IPC will kill parent executor but leave child executor alive.
- if config.Timeout < 7*time.Second {
- config.Timeout = 7 * time.Second
- }
- inf, inmem, err := createMapping(prog.ExecBufferSize)
- if err != nil {
- return nil, err
- }
- defer func() {
- if inf != nil {
- closeMapping(inf, inmem)
- }
- }()
- outf, outmem, err := createMapping(outputSize)
- if err != nil {
- return nil, err
- }
- defer func() {
- if outf != nil {
- closeMapping(outf, outmem)
- }
- }()
- serializeUint64(inmem[0:], config.Flags)
- serializeUint64(inmem[8:], uint64(pid))
- inmem = inmem[16:]
- env := &Env{
- In: inmem,
- Out: outmem,
- inFile: inf,
- outFile: outf,
- bin: strings.Split(bin, " "),
- pid: pid,
- config: config,
- }
- if len(env.bin) == 0 {
- return nil, fmt.Errorf("binary is empty string")
- }
- 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)
- }
- // 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
- }
- inf = nil
- outf = nil
- return env, nil
-}
-
-func (env *Env) Close() error {
- if env.cmd != nil {
- env.cmd.close()
- }
- 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
- }
-}
-
-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)
- FaultInjected bool
-}
-
-// Exec starts executor binary to execute program p and returns information about the execution:
-// output: process output
-// info: per-call info
-// 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
-func (env *Env) Exec(opts *ExecOpts, p *prog.Prog) (output []byte, info []CallInfo, failed, hanged bool, err0 error) {
- if p != nil {
- // Copy-in serialized program.
- if err := p.SerializeForExec(env.In, env.pid); err != nil {
- err0 = fmt.Errorf("executor %v: failed to serialize: %v", env.pid, err)
- return
- }
- }
- if env.config.Flags&FlagSignal != 0 {
- // Zero out the first two words (ncmd and nsig), so that we don't have garbage there
- // if executor crashes before writing non-garbage there.
- for i := 0; i < 4; i++ {
- env.Out[i] = 0
- }
- }
-
- atomic.AddUint64(&env.StatExecs, 1)
- if env.cmd == nil {
- atomic.AddUint64(&env.StatRestarts, 1)
- env.cmd, err0 = makeCommand(env.pid, env.bin, env.config, env.inFile, env.outFile)
- if err0 != nil {
- return
- }
- }
- var restart bool
- output, failed, hanged, restart, err0 = env.cmd.exec(opts)
- if err0 != nil || restart {
- env.cmd.close()
- env.cmd = nil
- return
- }
-
- if env.config.Flags&FlagSignal == 0 || p == nil {
- return
- }
- info, err0 = env.readOutCoverage(p)
- return
-}
-
-func (env *Env) readOutCoverage(p *prog.Prog) (info []CallInfo, err0 error) {
- 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
- }
-
- var ncmd uint32
- if !readOut(&ncmd) {
- err0 = fmt.Errorf("executor %v: failed to read output coverage", env.pid)
- return
- }
- info = make([]CallInfo, len(p.Calls))
- for i := range info {
- info[i].Errno = -1 // not executed
- }
- dumpCov := func() string {
- buf := new(bytes.Buffer)
- for i, inf := range info {
- str := "nil"
- if inf.Signal != nil {
- str = fmt.Sprint(len(inf.Signal))
- }
- fmt.Fprintf(buf, "%v:%v|", i, str)
- }
- return buf.String()
- }
- for i := uint32(0); i < ncmd; i++ {
- var callIndex, callNum, errno, faultInjected, signalSize, coverSize uint32
- if !readOut(&callIndex) || !readOut(&callNum) || !readOut(&errno) || !readOut(&faultInjected) || !readOut(&signalSize) || !readOut(&coverSize) {
- err0 = fmt.Errorf("executor %v: failed to read output coverage", env.pid)
- return
- }
- 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())
- return
- }
- c := p.Calls[callIndex]
- if num := c.Meta.ID; uint32(num) != callNum {
- 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())
- return
- }
- if info[callIndex].Signal != nil {
- err0 = fmt.Errorf("executor %v: failed to read output coverage: double coverage for call %v (cov: %v)",
- env.pid, callIndex, dumpCov())
- return
- }
- info[callIndex].Errno = int(errno)
- info[callIndex].FaultInjected = faultInjected != 0
- 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)
- return
- }
- info[callIndex].Signal = out[:signalSize:signalSize]
- out = out[signalSize:]
- if coverSize > uint32(len(out)) {
- err0 = fmt.Errorf("executor %v: failed to read output coverage: record %v, call %v, signalsize=%v coversize=%v",
- env.pid, i, callIndex, signalSize, coverSize)
- return
- }
- info[callIndex].Cover = out[:coverSize:coverSize]
- out = out[coverSize:]
- }
- return
-}
-
-func createMapping(size int) (f *os.File, mem []byte, err error) {
- f, err = ioutil.TempFile("./", "syzkaller-shm")
- if err != nil {
- err = fmt.Errorf("failed to create temp file: %v", err)
- return
- }
- if err = f.Truncate(int64(size)); err != nil {
- err = fmt.Errorf("failed to truncate shm file: %v", err)
- f.Close()
- os.Remove(f.Name())
- return
- }
- f.Close()
- fname := f.Name()
- f, err = os.OpenFile(f.Name(), os.O_RDWR, 0)
- if err != nil {
- err = fmt.Errorf("failed to open shm file: %v", err)
- os.Remove(fname)
- return
- }
- mem, err = syscall.Mmap(int(f.Fd()), 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
- if err != nil {
- err = fmt.Errorf("failed to mmap shm file: %v", err)
- 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
- }
-}
-
-type command struct {
- pid int
- config Config
- cmd *exec.Cmd
- dir string
- readDone chan []byte
- exited chan struct{}
- inrp *os.File
- outwp *os.File
-}
-
-func makeCommand(pid int, bin []string, config Config, inFile *os.File, outFile *os.File) (*command, error) {
- dir, err := ioutil.TempDir("./", "syzkaller-testdir")
- if err != nil {
- return nil, fmt.Errorf("failed to create temp dir: %v", err)
- }
-
- c := &command{
- pid: pid,
- config: config,
- dir: dir,
- }
- defer func() {
- if c != nil {
- c.close()
- }
- }()
-
- if config.Flags&(FlagSandboxSetuid|FlagSandboxNamespace) != 0 {
- if err := os.Chmod(dir, 0777); err != nil {
- return nil, fmt.Errorf("failed to chmod temp dir: %v", err)
- }
- }
-
- // 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
-
- c.readDone = make(chan []byte, 1)
- c.exited = make(chan struct{})
-
- cmd := exec.Command(bin[0], bin[1:]...)
- cmd.ExtraFiles = []*os.File{inFile, outFile, outrp, inwp}
- cmd.Env = []string{}
- cmd.Dir = dir
- if config.Flags&FlagDebug == 0 {
- cmd.Stdout = wp
- cmd.Stderr = wp
- go func(c *command) {
- // Read out output in case executor constantly prints something.
- bufSize := c.config.BufferSize
- if bufSize == 0 {
- bufSize = 128 << 10
- }
- output := make([]byte, bufSize)
- var size uint64
- for {
- n, err := rp.Read(output[size:])
- if n > 0 {
- size += uint64(n)
- if size >= bufSize*3/4 {
- copy(output, output[size-bufSize/2:size])
- size = bufSize / 2
- }
- }
- if err != nil {
- rp.Close()
- c.readDone <- output[:size]
- close(c.readDone)
- return
- }
- }
- }(c)
- } else {
- close(c.readDone)
- 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
- wp.Close()
- inwp.Close()
- if err := c.waitServing(); err != nil {
- return nil, err
- }
-
- tmp := c
- c = nil // disable defer above
- return tmp, nil
-}
-
-func (c *command) close() {
- if c.cmd != nil {
- c.abort()
- c.wait()
- }
- fileutil.UmountAll(c.dir)
- os.RemoveAll(c.dir)
- if c.inrp != nil {
- c.inrp.Close()
- }
- if c.outwp != nil {
- c.outwp.Close()
- }
-}
-
-// 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()
- if err != nil {
- c.abort()
- output := <-c.readDone
- err = fmt.Errorf("executor is not serving: %v\n%s", err, output)
- c.wait()
- if c.cmd.ProcessState != nil {
- sys := c.cmd.ProcessState.Sys()
- if ws, ok := sys.(syscall.WaitStatus); ok {
- // Magic values returned by executor.
- if ws.ExitStatus() == statusFail {
- err = ExecutorFailure(fmt.Sprintf("executor is not serving:\n%s", output))
- }
- }
- }
- }
- return err
- case <-timeout.C:
- return fmt.Errorf("executor is not serving")
- }
-}
-
-// abort sends the abort signal to the command and then SIGKILL if wait doesn't
-// return within 5s.
-func (c *command) abort() {
- sig := c.config.AbortSignal
- if sig <= 0 || sig >= 32 {
- sig = syscall.SIGKILL
- }
- syscall.Kill(c.cmd.Process.Pid, sig)
- if sig != 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
-}
-
-func (c *command) exec(opts *ExecOpts) (output []byte, failed, hanged, restart bool, err0 error) {
- if opts.Flags&FlagInjectFault != 0 {
- enableFaultOnce.Do(enableFaultInjection)
- }
- var inCmd [24]byte
- serializeUint64(inCmd[0:], opts.Flags)
- serializeUint64(inCmd[8:], uint64(opts.FaultCall))
- serializeUint64(inCmd[16:], uint64(opts.FaultNth))
- if _, err := c.outwp.Write(inCmd[:]); err != nil {
- output = <-c.readDone
- err0 = fmt.Errorf("failed to write control pipe: %v", err)
- return
- }
- done := make(chan bool)
- hang := make(chan bool)
- go func() {
- t := time.NewTimer(c.config.Timeout)
- select {
- case <-t.C:
- c.abort()
- hang <- true
- case <-done:
- t.Stop()
- hang <- false
- }
- }()
- var reply [1]byte
- readN, readErr := c.inrp.Read(reply[:])
- close(done)
- status := 0
- if readErr == nil {
- if readN != len(reply) {
- panic(fmt.Sprintf("executor %v: read only %v bytes", c.pid, readN))
- }
- status = int(reply[0])
- if status == 0 {
- // Program was OK.
- <-hang
- return
- }
- // Executor writes magic values into the pipe before exiting,
- // so proceed with killing and joining it.
- }
- c.abort()
- output = <-c.readDone
- if err := c.wait(); <-hang {
- hanged = true
- // In all likelihood, this will be duplicated by the default
- // case below, but that's fine.
- output = append(output, []byte(err.Error())...)
- output = append(output, '\n')
- }
- // Handle magic values returned by executor.
- switch status {
- case statusFail:
- err0 = ExecutorFailure(fmt.Sprintf("executor failed: %s", output))
- case statusError:
- err0 = fmt.Errorf("executor detected kernel bug")
- 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
- default:
- // Failed to get a valid (or perhaps any) status from the
- // executor.
- //
- // Once the executor is serving the status is always written to
- // the pipe, so we don't bother to check the specific exit
- // codes from wait.
- err0 = fmt.Errorf("invalid (or no) executor status received: %d, executor exit: %s", status, c.cmd.ProcessState)
- }
- return
-}
-
-func serializeUint64(buf []byte, v uint64) {
- for i := 0; i < 8; i++ {
- buf[i] = byte(v >> (8 * uint(i)))
- }
-}
-
-var enableFaultOnce sync.Once
-
-func enableFaultInjection() {
- if err := ioutil.WriteFile("/sys/kernel/debug/failslab/ignore-gfp-wait", []byte("N"), 0600); err != nil {
- panic(fmt.Sprintf("failed to write /sys/kernel/debug/failslab/ignore-gfp-wait: %v", err))
- }
- if err := ioutil.WriteFile("/sys/kernel/debug/fail_futex/ignore-private", []byte("N"), 0600); err != nil {
- panic(fmt.Sprintf("failed to write /sys/kernel/debug/fail_futex/ignore-private: %v", err))
- }
-}
diff --git a/ipc/ipc_test.go b/ipc/ipc_test.go
deleted file mode 100644
index 00c1cd95c..000000000
--- a/ipc/ipc_test.go
+++ /dev/null
@@ -1,108 +0,0 @@
-// 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 (
- "math/rand"
- "os"
- "testing"
- "time"
-
- "github.com/google/syzkaller/pkg/csource"
- "github.com/google/syzkaller/pkg/fileutil"
- "github.com/google/syzkaller/prog"
-)
-
-const timeout = 10 * time.Second
-
-func buildExecutor(t *testing.T) string {
- return buildProgram(t, "../executor/executor.cc")
-}
-
-func buildSource(t *testing.T, src []byte) string {
- tmp, err := fileutil.WriteTempFile(src)
- if err != nil {
- t.Fatalf("%v", err)
- }
- defer os.Remove(tmp)
- return buildProgram(t, tmp)
-}
-
-func buildProgram(t *testing.T, src string) string {
- bin, err := csource.Build("c++", src)
- if err != nil {
- t.Fatalf("%v", err)
- }
- return bin
-}
-
-func initTest(t *testing.T) (rand.Source, int) {
- t.Parallel()
- iters := 100
- if testing.Short() {
- iters = 10
- }
- seed := int64(time.Now().UnixNano())
- rs := rand.NewSource(seed)
- t.Logf("seed=%v", seed)
- return rs, iters
-}
-
-func TestEmptyProg(t *testing.T) {
- bin := buildExecutor(t)
- defer os.Remove(bin)
-
- cfg := Config{
- Timeout: timeout,
- }
- env, err := MakeEnv(bin, 0, cfg)
- if err != nil {
- t.Fatalf("failed to create env: %v", err)
- }
- defer env.Close()
-
- p := new(prog.Prog)
- opts := &ExecOpts{}
- output, _, failed, hanged, err := env.Exec(opts, p)
- if err != nil {
- t.Fatalf("failed to run executor: %v", err)
- }
- if len(output) != 0 {
- t.Fatalf("output on empty program")
- }
- if failed || hanged {
- t.Fatalf("empty program failed")
- }
-}
-
-func TestExecute(t *testing.T) {
- rs, iters := initTest(t)
- flags := []uint64{0, FlagThreaded, FlagThreaded | FlagCollide}
-
- bin := buildExecutor(t)
- defer os.Remove(bin)
-
- for _, flag := range flags {
- t.Logf("testing flags 0x%x\n", flag)
- cfg := Config{
- Flags: flag,
- Timeout: timeout,
- }
- env, err := MakeEnv(bin, 0, cfg)
- if err != nil {
- t.Fatalf("failed to create env: %v", err)
- }
- defer env.Close()
-
- for i := 0; i < iters/len(flags); i++ {
- p := prog.Generate(rs, 10, nil)
- opts := &ExecOpts{}
- output, _, _, _, err := env.Exec(opts, p)
- if err != nil {
- t.Logf("program:\n%s\n", p.Serialize())
- t.Fatalf("failed to run executor: %v\n%s", err, output)
- }
- }
- }
-}