2015-10-12 08:16:57 +00: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.
|
|
|
|
|
2017-06-02 18:09:00 +00:00
|
|
|
// Package vm provides an abstract test machine (VM, physical machine, etc)
|
|
|
|
// interface for the rest of the system.
|
|
|
|
// For convenience test machines are subsequently collectively called VMs.
|
|
|
|
// Package wraps vmimpl package interface with some common functionality
|
|
|
|
// and higher-level interface.
|
2015-10-12 08:16:57 +00:00
|
|
|
package vm
|
|
|
|
|
|
|
|
import (
|
2016-09-01 17:54:55 +00:00
|
|
|
"bytes"
|
2015-10-12 08:16:57 +00:00
|
|
|
"fmt"
|
2016-08-28 17:21:57 +00:00
|
|
|
"os"
|
2016-12-19 16:39:03 +00:00
|
|
|
"regexp"
|
2015-12-23 18:11:29 +00:00
|
|
|
"time"
|
2016-09-01 17:54:55 +00:00
|
|
|
|
2017-07-03 12:00:47 +00:00
|
|
|
"github.com/google/syzkaller/pkg/osutil"
|
2017-06-17 10:50:11 +00:00
|
|
|
"github.com/google/syzkaller/pkg/report"
|
2017-06-02 18:09:00 +00:00
|
|
|
"github.com/google/syzkaller/vm/vmimpl"
|
2016-01-11 16:33:44 +00:00
|
|
|
|
2017-06-02 18:09:00 +00:00
|
|
|
_ "github.com/google/syzkaller/vm/adb"
|
|
|
|
_ "github.com/google/syzkaller/vm/gce"
|
2017-06-12 21:31:03 +00:00
|
|
|
_ "github.com/google/syzkaller/vm/isolated"
|
2017-06-02 18:09:00 +00:00
|
|
|
_ "github.com/google/syzkaller/vm/kvm"
|
|
|
|
_ "github.com/google/syzkaller/vm/odroid"
|
|
|
|
_ "github.com/google/syzkaller/vm/qemu"
|
|
|
|
)
|
2016-01-11 16:33:44 +00:00
|
|
|
|
2017-06-02 18:09:00 +00:00
|
|
|
type Pool struct {
|
|
|
|
impl vmimpl.Pool
|
|
|
|
workdir string
|
2015-10-12 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2017-06-02 18:09:00 +00:00
|
|
|
type Instance struct {
|
|
|
|
impl vmimpl.Instance
|
|
|
|
workdir string
|
|
|
|
index int
|
2015-10-19 10:47:37 +00:00
|
|
|
}
|
|
|
|
|
2017-06-02 18:09:00 +00:00
|
|
|
type Env vmimpl.Env
|
2015-10-12 08:16:57 +00:00
|
|
|
|
2017-06-02 18:09:00 +00:00
|
|
|
var (
|
|
|
|
Shutdown = vmimpl.Shutdown
|
|
|
|
TimeoutErr = vmimpl.TimeoutErr
|
|
|
|
)
|
2015-10-12 08:16:57 +00:00
|
|
|
|
2017-06-02 18:09:00 +00:00
|
|
|
func Create(typ string, env *Env) (*Pool, error) {
|
|
|
|
impl, err := vmimpl.Create(typ, (*vmimpl.Env)(env))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &Pool{
|
|
|
|
impl: impl,
|
|
|
|
workdir: env.Workdir,
|
|
|
|
}, nil
|
2015-10-12 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2017-06-02 18:09:00 +00:00
|
|
|
func (pool *Pool) Count() int {
|
|
|
|
return pool.impl.Count()
|
2015-10-12 08:16:57 +00:00
|
|
|
}
|
2015-12-23 18:11:29 +00:00
|
|
|
|
2017-06-02 18:09:00 +00:00
|
|
|
func (pool *Pool) Create(index int) (*Instance, error) {
|
|
|
|
if index < 0 || index >= pool.Count() {
|
|
|
|
return nil, fmt.Errorf("invalid VM index %v (count %v)", index, pool.Count())
|
|
|
|
}
|
2017-07-03 12:00:47 +00:00
|
|
|
workdir, err := osutil.ProcessTempDir(pool.workdir)
|
2016-08-28 17:21:57 +00:00
|
|
|
if err != nil {
|
2017-06-02 18:09:00 +00:00
|
|
|
return nil, fmt.Errorf("failed to create instance temp dir: %v", err)
|
2016-08-28 17:21:57 +00:00
|
|
|
}
|
2017-06-02 18:09:00 +00:00
|
|
|
impl, err := pool.impl.Create(workdir, index)
|
|
|
|
if err != nil {
|
|
|
|
os.RemoveAll(workdir)
|
|
|
|
return nil, err
|
2016-08-28 17:21:57 +00:00
|
|
|
}
|
2017-06-02 18:09:00 +00:00
|
|
|
return &Instance{
|
|
|
|
impl: impl,
|
|
|
|
workdir: workdir,
|
|
|
|
index: index,
|
|
|
|
}, nil
|
2016-08-28 17:21:57 +00:00
|
|
|
}
|
|
|
|
|
2017-06-02 18:09:00 +00:00
|
|
|
func (inst *Instance) Copy(hostSrc string) (string, error) {
|
|
|
|
return inst.impl.Copy(hostSrc)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (inst *Instance) Forward(port int) (string, error) {
|
|
|
|
return inst.impl.Forward(port)
|
|
|
|
}
|
2016-09-01 17:54:55 +00:00
|
|
|
|
2017-06-02 18:09:00 +00:00
|
|
|
func (inst *Instance) Run(timeout time.Duration, stop <-chan bool, command string) (outc <-chan []byte, errc <-chan error, err error) {
|
|
|
|
return inst.impl.Run(timeout, stop, command)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (inst *Instance) Close() {
|
|
|
|
inst.impl.Close()
|
|
|
|
os.RemoveAll(inst.workdir)
|
|
|
|
}
|
|
|
|
|
|
|
|
func MonitorExecution(outc <-chan []byte, errc <-chan error, needOutput bool, ignores []*regexp.Regexp) (desc string, text, output []byte, crashed, timedout bool) {
|
2016-09-01 17:54:55 +00:00
|
|
|
waitForOutput := func() {
|
|
|
|
dur := time.Second
|
|
|
|
if needOutput {
|
|
|
|
dur = 10 * time.Second
|
|
|
|
}
|
|
|
|
timer := time.NewTimer(dur).C
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case out, ok := <-outc:
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
output = append(output, out...)
|
|
|
|
case <-timer:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
matchPos := 0
|
|
|
|
const (
|
2017-06-21 17:47:42 +00:00
|
|
|
beforeContext = 1024 << 10
|
2016-09-01 17:54:55 +00:00
|
|
|
afterContext = 128 << 10
|
|
|
|
)
|
2016-09-28 16:05:28 +00:00
|
|
|
extractError := func(defaultError string) (string, []byte, []byte, bool, bool) {
|
|
|
|
// Give it some time to finish writing the error message.
|
|
|
|
waitForOutput()
|
2016-12-16 15:11:18 +00:00
|
|
|
if bytes.Contains(output, []byte("SYZ-FUZZER: PREEMPTED")) {
|
|
|
|
return "preempted", nil, nil, false, true
|
|
|
|
}
|
2016-12-19 16:39:03 +00:00
|
|
|
if !report.ContainsCrash(output[matchPos:], ignores) {
|
2017-02-02 19:21:38 +00:00
|
|
|
return defaultError, nil, output, defaultError != "", false
|
2016-09-28 16:05:28 +00:00
|
|
|
}
|
2016-12-19 16:39:03 +00:00
|
|
|
desc, text, start, end := report.Parse(output[matchPos:], ignores)
|
2016-09-28 16:05:28 +00:00
|
|
|
start = start + matchPos - beforeContext
|
|
|
|
if start < 0 {
|
|
|
|
start = 0
|
|
|
|
}
|
|
|
|
end = end + matchPos + afterContext
|
|
|
|
if end > len(output) {
|
|
|
|
end = len(output)
|
|
|
|
}
|
|
|
|
return desc, text, output[start:end], true, false
|
|
|
|
}
|
|
|
|
|
2016-09-01 17:54:55 +00:00
|
|
|
lastExecuteTime := time.Now()
|
|
|
|
ticker := time.NewTimer(3 * time.Minute)
|
2016-09-09 11:22:48 +00:00
|
|
|
tickerFired := false
|
2016-09-01 17:54:55 +00:00
|
|
|
for {
|
2016-09-09 11:22:48 +00:00
|
|
|
if !tickerFired && !ticker.Stop() {
|
2016-09-01 17:54:55 +00:00
|
|
|
<-ticker.C
|
|
|
|
}
|
2016-09-09 11:22:48 +00:00
|
|
|
tickerFired = false
|
2016-09-01 17:54:55 +00:00
|
|
|
ticker.Reset(3 * time.Minute)
|
|
|
|
select {
|
|
|
|
case err := <-errc:
|
|
|
|
switch err {
|
|
|
|
case nil:
|
2017-02-02 19:21:38 +00:00
|
|
|
// The program has exited without errors,
|
|
|
|
// but wait for kernel output in case there is some delayed oops.
|
|
|
|
return extractError("")
|
2016-09-01 17:54:55 +00:00
|
|
|
case TimeoutErr:
|
|
|
|
return err.Error(), nil, nil, false, true
|
|
|
|
default:
|
2016-09-28 16:05:28 +00:00
|
|
|
// Note: connection lost can race with a kernel oops message.
|
|
|
|
// In such case we want to return the kernel oops.
|
|
|
|
return extractError("lost connection to test machine")
|
2016-09-01 17:54:55 +00:00
|
|
|
}
|
|
|
|
case out := <-outc:
|
|
|
|
output = append(output, out...)
|
2016-09-03 10:35:16 +00:00
|
|
|
if bytes.Index(output[matchPos:], []byte("executing program")) != -1 { // syz-fuzzer output
|
|
|
|
lastExecuteTime = time.Now()
|
|
|
|
}
|
|
|
|
if bytes.Index(output[matchPos:], []byte("executed programs:")) != -1 { // syz-execprog output
|
2016-09-01 17:54:55 +00:00
|
|
|
lastExecuteTime = time.Now()
|
|
|
|
}
|
2016-12-19 16:39:03 +00:00
|
|
|
if report.ContainsCrash(output[matchPos:], ignores) {
|
2017-02-02 19:21:38 +00:00
|
|
|
return extractError("unknown error")
|
2016-09-01 17:54:55 +00:00
|
|
|
}
|
|
|
|
if len(output) > 2*beforeContext {
|
|
|
|
copy(output, output[len(output)-beforeContext:])
|
|
|
|
output = output[:beforeContext]
|
|
|
|
}
|
|
|
|
matchPos = len(output) - 128
|
|
|
|
if matchPos < 0 {
|
|
|
|
matchPos = 0
|
|
|
|
}
|
|
|
|
// In some cases kernel constantly prints something to console,
|
|
|
|
// but fuzzer is not actually executing programs.
|
2017-06-02 18:09:00 +00:00
|
|
|
if time.Since(lastExecuteTime) > 3*time.Minute {
|
2016-09-01 17:54:55 +00:00
|
|
|
return "test machine is not executing programs", nil, output, true, false
|
|
|
|
}
|
|
|
|
case <-ticker.C:
|
2016-09-09 11:22:48 +00:00
|
|
|
tickerFired = true
|
2017-06-02 18:09:00 +00:00
|
|
|
return "no output from test machine", nil, output, true, false
|
2016-09-29 13:04:48 +00:00
|
|
|
case <-Shutdown:
|
|
|
|
return "", nil, nil, false, false
|
2016-09-01 17:54:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|