vm: refactor VM interface in preparation for adb support

adb has more complex port forwarding setup, also / is mounted read-only.
Make VM interface more flexible to support such cases.
This commit is contained in:
Dmitry Vyukov 2016-01-11 17:33:44 +01:00
parent 46fa57f3b4
commit de48f7b019
7 changed files with 110 additions and 73 deletions

View File

@ -41,6 +41,8 @@ type Config struct {
NoDropPrivs bool
Leak bool // do memory leak checking
ConsoleDev string // console device for adb vm
Enable_Syscalls []string
Disable_Syscalls []string
Suppressions []string
@ -188,17 +190,18 @@ func CreateVMConfig(cfg *Config) (*vm.Config, error) {
return nil, fmt.Errorf("failed to create instance temp dir: %v", err)
}
vmCfg := &vm.Config{
Name: fmt.Sprintf("%v-%v", cfg.Type, index),
Index: index,
Workdir: workdir,
Bin: cfg.Bin,
Kernel: cfg.Kernel,
Cmdline: cfg.Cmdline,
Image: cfg.Image,
Sshkey: cfg.Sshkey,
Cpu: cfg.Cpu,
Mem: cfg.Mem,
Debug: cfg.Debug,
Name: fmt.Sprintf("%v-%v", cfg.Type, index),
Index: index,
Workdir: workdir,
Bin: cfg.Bin,
Kernel: cfg.Kernel,
Cmdline: cfg.Cmdline,
Image: cfg.Image,
Sshkey: cfg.Sshkey,
ConsoleDev: cfg.ConsoleDev,
Cpu: cfg.Cpu,
Mem: cfg.Mem,
Debug: cfg.Debug,
}
return vmCfg, nil
}

View File

@ -170,11 +170,18 @@ func (mgr *Manager) runInstance(vmCfg *vm.Config, first bool) bool {
}
defer inst.Close()
if err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin/syz-fuzzer"), "/syz-fuzzer"); err != nil {
fwdAddr, err := inst.Forward(mgr.port)
if err != nil {
logf(0, "failed to setup port forwarding: %v", err)
return false
}
fuzzerBin, err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin/syz-fuzzer"))
if err != nil {
logf(0, "failed to copy binary: %v", err)
return false
}
if err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin/syz-executor"), "/syz-executor"); err != nil {
executorBin, err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin/syz-executor"))
if err != nil {
logf(0, "failed to copy binary: %v", err)
return false
}
@ -201,8 +208,8 @@ func (mgr *Manager) runInstance(vmCfg *vm.Config, first bool) bool {
// Leak detection significantly slows down fuzzing, so detect leaks only on the first instance.
leak := first && mgr.cfg.Leak
outputC, errorC, err := inst.Run(time.Hour, fmt.Sprintf("/syz-fuzzer -name %v -executor /syz-executor -manager %v:%v -output=%v -procs %v -leak=%v %v %v %v",
vmCfg.Name, inst.HostAddr(), mgr.port, mgr.cfg.Output, mgr.cfg.Procs, leak, cover, dropprivs, calls))
outputC, errorC, err := inst.Run(time.Hour, fmt.Sprintf("%v -executor %v -name %v -manager %v -output=%v -procs %v -leak=%v %v %v %v",
fuzzerBin, executorBin, vmCfg.Name, fwdAddr, mgr.cfg.Output, mgr.cfg.Procs, leak, cover, dropprivs, calls))
if err != nil {
logf(0, "failed to run fuzzer: %v", err)
return false
@ -221,6 +228,7 @@ func (mgr *Manager) runInstance(vmCfg *vm.Config, first bool) bool {
logf(0, "%v: running long enough, restarting", vmCfg.Name)
return true
default:
logf(0, "%v: lost connection: %v", vmCfg.Name, err)
mgr.saveCrasher(vmCfg.Name, "lost connection", output)
return true
}

View File

@ -26,10 +26,16 @@ var (
flagConfig = flag.String("config", "", "configuration file")
flagCount = flag.Int("count", 0, "number of VMs to use (overrides config count param)")
instances chan vm.Instance
instances chan VM
bootRequests chan bool
)
type VM struct {
vm.Instance
execprogBin string
executorBin string
}
func main() {
flag.Parse()
cfg, _, _, err := config.Parse(*flagConfig)
@ -56,7 +62,7 @@ func main() {
}
log.Printf("target crash: '%s'", data[crashLoc[0]:crashLoc[1]])
instances = make(chan vm.Instance, cfg.Count)
instances = make(chan VM, cfg.Count)
bootRequests = make(chan bool, cfg.Count)
for i := 0; i < cfg.Count; i++ {
bootRequests <- true
@ -70,13 +76,15 @@ func main() {
if err != nil {
log.Fatalf("failed to create VM: %v", err)
}
if err := inst.Copy(filepath.Join(cfg.Syzkaller, "bin/syz-execprog"), "/syz-execprog"); err != nil {
execprogBin, err := inst.Copy(filepath.Join(cfg.Syzkaller, "bin/syz-execprog"))
if err != nil {
log.Fatalf("failed to copy to VM: %v", err)
}
if err := inst.Copy(filepath.Join(cfg.Syzkaller, "bin/syz-executor"), "/syz-executor"); err != nil {
executorBin, err := inst.Copy(filepath.Join(cfg.Syzkaller, "bin/syz-executor"))
if err != nil {
log.Fatalf("failed to copy to VM: %v", err)
}
instances <- inst
instances <- VM{inst, execprogBin, executorBin}
}
}()
}
@ -165,7 +173,7 @@ func repro(cfg *config.Config, entries []*prog.LogEntry, crashLoc []int) {
testBin(cfg, bin)
}
func returnInstance(inst vm.Instance, res bool) {
func returnInstance(inst VM, res bool) {
if res {
// The test crashed, discard the VM and issue another boot request.
bootRequests <- true
@ -189,7 +197,8 @@ func testProg(cfg *config.Config, p *prog.Prog, multiplier int, threaded, collid
log.Fatalf("%v", err)
}
defer os.Remove(progFile)
if err := inst.Copy(progFile, "/syz-prog"); err != nil {
bin, err := inst.Copy(progFile)
if err != nil {
log.Fatalf("failed to copy to VM: %v", err)
}
@ -202,8 +211,8 @@ func testProg(cfg *config.Config, p *prog.Prog, multiplier int, threaded, collid
repeat *= multiplier
timeoutSec *= multiplier
timeout := time.Duration(timeoutSec) * time.Second
command := fmt.Sprintf("/syz-execprog -executor /syz-executor -cover=0 -procs=%v -repeat=%v -threaded=%v -collide=%v /syz-prog",
cfg.Procs, repeat, threaded, collide)
command := fmt.Sprintf("%v -executor %v -cover=0 -procs=%v -repeat=%v -threaded=%v -collide=%v %v",
inst.execprogBin, inst.executorBin, cfg.Procs, repeat, threaded, collide, bin)
log.Printf("testing program (threaded=%v, collide=%v, repeat=%v, timeout=%v):\n%s\n",
threaded, collide, repeat, timeout, pstr)
return testImpl(inst, command, timeout)
@ -216,11 +225,12 @@ func testBin(cfg *config.Config, bin string) (res bool) {
returnInstance(inst, res)
}()
if err := inst.Copy(bin, "/syz-bin"); err != nil {
bin, err := inst.Copy(bin)
if err != nil {
log.Fatalf("failed to copy to VM: %v", err)
}
log.Printf("testing compiled C program")
return testImpl(inst, "/syz-bin", 10*time.Second)
return testImpl(inst, bin, 10*time.Second)
}
func testImpl(inst vm.Instance, command string, timeout time.Duration) (res bool) {

View File

@ -19,6 +19,10 @@ import (
"github.com/google/syzkaller/vm"
)
const (
hostAddr = "192.168.33.1"
)
func init() {
vm.Register("kvm", ctor)
}
@ -165,10 +169,6 @@ func validateConfig(cfg *vm.Config) error {
return nil
}
func (inst *instance) HostAddr() string {
return "192.168.33.1"
}
func (inst *instance) Close() {
if inst.lkvm != nil {
inst.lkvm.Process.Kill()
@ -181,12 +181,20 @@ func (inst *instance) Close() {
os.Remove(inst.sandboxPath + ".sock")
}
func (inst *instance) Copy(hostSrc, vmDst string) error {
func (inst *instance) Forward(port int) (string, error) {
return fmt.Sprintf("%v:%v", hostAddr, port), nil
}
func (inst *instance) Copy(hostSrc string) (string, error) {
vmDst := filepath.Join("/", filepath.Base(hostSrc))
dst := filepath.Join(inst.sandboxPath, vmDst)
if err := fileutil.CopyFile(hostSrc, dst, false); err != nil {
return err
return "", err
}
return os.Chmod(dst, 0777)
if err := os.Chmod(dst, 0777); err != nil {
return "", err
}
return vmDst, nil
}
func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte, <-chan error, error) {

View File

@ -23,7 +23,6 @@ func init() {
type instance struct {
cfg *vm.Config
closed chan bool
files map[string]string
}
func ctor(cfg *vm.Config) (vm.Instance, error) {
@ -40,27 +39,28 @@ func ctor(cfg *vm.Config) (vm.Instance, error) {
inst := &instance{
cfg: cfg,
closed: make(chan bool),
files: make(map[string]string),
}
return inst, nil
}
func (inst *instance) HostAddr() string {
return "127.0.0.1"
}
func (inst *instance) Close() {
close(inst.closed)
os.RemoveAll(inst.cfg.Workdir)
}
func (inst *instance) Copy(hostSrc, vmDst string) error {
dst := filepath.Join(inst.cfg.Workdir, vmDst)
inst.files[vmDst] = dst
if err := fileutil.CopyFile(hostSrc, dst, false); err != nil {
return err
func (inst *instance) Forward(port int) (string, error) {
return fmt.Sprintf("127.0.0.1:%v", port), nil
}
func (inst *instance) Copy(hostSrc string) (string, error) {
vmDst := filepath.Join(inst.cfg.Workdir, filepath.Base(hostSrc))
if err := fileutil.CopyFile(hostSrc, vmDst, false); err != nil {
return "", err
}
return os.Chmod(dst, 0777)
if err := os.Chmod(vmDst, 0777); err != nil {
return "", err
}
return vmDst, nil
}
func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte, <-chan error, error) {
@ -68,11 +68,6 @@ func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte,
command = strings.Replace(command, " ", " ", -1)
}
args := strings.Split(command, " ")
for i, arg := range args {
if inst.files[arg] != "" {
args[i] = inst.files[arg]
}
}
cmd := exec.Command(args[0], args[1:]...)
if err := cmd.Start(); err != nil {
return nil, nil, err

View File

@ -20,6 +20,10 @@ import (
"github.com/google/syzkaller/vm"
)
const (
hostAddr = "10.0.2.10"
)
func init() {
vm.Register("qemu", ctor)
}
@ -111,10 +115,6 @@ func validateConfig(cfg *vm.Config) error {
return nil
}
func (inst *instance) HostAddr() string {
return "10.0.2.10"
}
func (inst *instance) Close() {
if inst.qemu != nil {
inst.qemu.Process.Kill()
@ -146,7 +146,7 @@ func (inst *instance) Boot() error {
"-hda", inst.image,
"-m", strconv.Itoa(inst.cfg.Mem),
"-net", "nic",
"-net", fmt.Sprintf("user,host=%v,hostfwd=tcp::%v-:22", inst.HostAddr(), inst.port),
"-net", fmt.Sprintf("user,host=%v,hostfwd=tcp::%v-:22", hostAddr, inst.port),
"-nographic",
"-enable-kvm",
"-numa", "node,nodeid=0,cpus=0-1", "-numa", "node,nodeid=1,cpus=2-3",
@ -247,11 +247,16 @@ func (inst *instance) Boot() error {
return nil
}
func (inst *instance) Copy(hostSrc, vmDst string) error {
func (inst *instance) Forward(port int) (string, error) {
return fmt.Sprintf("%v:%v", hostAddr, port), nil
}
func (inst *instance) Copy(hostSrc string) (string, error) {
vmDst := filepath.Join("/", filepath.Base(hostSrc))
args := append(inst.sshArgs("-P"), hostSrc, "root@localhost:"+vmDst)
cmd := exec.Command("scp", args...)
if err := cmd.Start(); err != nil {
return err
return "", err
}
done := make(chan bool)
go func() {
@ -263,7 +268,10 @@ func (inst *instance) Copy(hostSrc, vmDst string) error {
}()
err := cmd.Wait()
close(done)
return err
if err != nil {
return "", err
}
return vmDst, nil
}
func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte, <-chan error, error) {

View File

@ -12,30 +12,35 @@ import (
// Instance represents a Linux VM or a remote physical machine.
type Instance interface {
// Copy copies a hostSrc file to vmDst file (think of scp).
Copy(hostSrc, vmDst string) error
// Copy copies a hostSrc file into vm and returns file name in vm.
Copy(hostSrc string) (string, error)
// Forward setups forwarding from within VM to host port port
// and returns address to use in VM.
Forward(port int) (string, error)
// Run runs cmd inside of the VM (think of ssh cmd).
// outc receives combined cmd and kernel console output.
// errc receives either command Wait return error or vm.TimeoutErr.
Run(timeout time.Duration, command string) (outc <-chan []byte, errc <-chan error, err error)
// HostAddr returns ip address of the host as seen by the VM.
HostAddr() string
// Close stops and destroys the VM.
Close()
}
type Config struct {
Name string
Index int
Workdir string
Bin string
Kernel string
Cmdline string
Image string
Sshkey string
Cpu int
Mem int
Debug bool
Name string
Index int
Workdir string
Bin string
Kernel string
Cmdline string
Image string
Sshkey string
ConsoleDev string
Cpu int
Mem int
Debug bool
}
type ctorFunc func(cfg *Config) (Instance, error)