diff options
| author | Radoslav Gerganov <rgerganov@vmware.com> | 2020-09-25 02:04:09 -0700 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2020-10-10 11:58:52 +0200 |
| commit | 4a77ae0bdc5cd75ebe88ce7c896aae6bbf457a29 (patch) | |
| tree | f6e97682c59e708acc25fa4f8e9f66602b2b8037 /vm/vmware | |
| parent | b74c49a6af84e6ab50018024e8862263b1e0bd6d (diff) | |
vm: implement the VM interface for VMware Workstation
Use the "vmrun" utility to manage Workstation VMs. The syzkaller manager
creates temporary VMs (linked clones) from a base image, gets their IP
address and uses ssh to deploy and run programs (similar to the isolated
mode).
Diffstat (limited to 'vm/vmware')
| -rw-r--r-- | vm/vmware/vmware.go | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/vm/vmware/vmware.go b/vm/vmware/vmware.go new file mode 100644 index 000000000..05d06e0a6 --- /dev/null +++ b/vm/vmware/vmware.go @@ -0,0 +1,232 @@ +// Copyright 2020 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 vmware + +import ( + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/google/syzkaller/pkg/config" + "github.com/google/syzkaller/pkg/log" + "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/vm/vmimpl" +) + +func init() { + vmimpl.Register("vmware", ctor, false) +} + +type Config struct { + BaseVMX string `json:"base_vmx"` // location of the base vmx + Count int `json:"count"` // number of VMs to run in parallel +} + +type Pool struct { + env *vmimpl.Env + cfg *Config +} + +type instance struct { + cfg *Config + baseVMX string + vmx string + ipAddr string + snapshot string + closed chan bool + debug bool + sshuser string + sshkey string + forwardPort int +} + +func ctor(env *vmimpl.Env) (vmimpl.Pool, error) { + cfg := &Config{} + if err := config.LoadData(env.Config, cfg); err != nil { + return nil, err + } + if cfg.BaseVMX == "" { + return nil, fmt.Errorf("config param base_vmx is empty") + } + if cfg.Count < 1 || cfg.Count > 128 { + return nil, fmt.Errorf("invalid config param count: %v, want [1, 128]", cfg.Count) + } + if _, err := exec.LookPath("vmrun"); err != nil { + return nil, fmt.Errorf("cannot find vmrun") + } + if env.Debug && cfg.Count > 1 { + log.Logf(0, "limiting number of VMs from %v to 1 in debug mode", cfg.Count) + cfg.Count = 1 + } + pool := &Pool{ + cfg: cfg, + env: env, + } + return pool, nil +} + +func (pool *Pool) Count() int { + return pool.cfg.Count +} + +func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) { + vmx := filepath.Join(workdir, "syzkaller.vmx") + sshkey := pool.env.SSHKey + sshuser := pool.env.SSHUser + inst := &instance{ + cfg: pool.cfg, + debug: pool.env.Debug, + baseVMX: pool.cfg.BaseVMX, + vmx: vmx, + sshkey: sshkey, + sshuser: sshuser, + snapshot: strconv.FormatInt(time.Now().Unix(), 10), + closed: make(chan bool), + } + if err := inst.clone(); err != nil { + return nil, err + } + if err := inst.boot(); err != nil { + return nil, err + } + return inst, nil +} + +func (inst *instance) clone() error { + if inst.debug { + log.Logf(0, "snapshotting %v (%v)", inst.baseVMX, inst.snapshot) + } + if _, err := osutil.RunCmd(2*time.Minute, "", "vmrun", "snapshot", inst.baseVMX, inst.snapshot); err != nil { + return err + } + if inst.debug { + log.Logf(0, "cloning %v to %v", inst.baseVMX, inst.vmx) + } + if _, err := osutil.RunCmd(2*time.Minute, "", "vmrun", "clone", inst.baseVMX, inst.vmx, "linked", + "-snapshot="+inst.snapshot); err != nil { + return err + } + return nil +} + +func (inst *instance) boot() error { + if inst.debug { + log.Logf(0, "starting %v", inst.vmx) + } + if _, err := osutil.RunCmd(5*time.Minute, "", "vmrun", "start", inst.vmx, "nogui"); err != nil { + return err + } + if inst.debug { + log.Logf(0, "getting IP of %v", inst.vmx) + } + ip, err := osutil.RunCmd(5*time.Minute, "", "vmrun", "getGuestIPAddress", inst.vmx, "-wait") + if err != nil { + return err + } + inst.ipAddr = strings.TrimSuffix(string(ip), "\n") + if inst.debug { + log.Logf(0, "VM %v has IP: %v", inst.vmx, inst.ipAddr) + } + return nil +} + +func (inst *instance) Forward(port int) (string, error) { + if inst.forwardPort != 0 { + return "", fmt.Errorf("isolated: Forward port already set") + } + if port == 0 { + return "", fmt.Errorf("isolated: Forward port is zero") + } + inst.forwardPort = port + return fmt.Sprintf("127.0.0.1:%v", port), nil +} + +func (inst *instance) Close() { + if inst.debug { + log.Logf(0, "stopping %v", inst.vmx) + } + osutil.RunCmd(2*time.Minute, "", "vmrun", "stop", inst.vmx) + if inst.debug { + log.Logf(0, "deleting %v", inst.vmx) + } + osutil.RunCmd(2*time.Minute, "", "vmrun", "deleteVM", inst.vmx) + if inst.debug { + log.Logf(0, "deleting snapshot %v", inst.snapshot) + } + osutil.RunCmd(2*time.Minute, "", "vmrun", "deleteSnapshot", inst.baseVMX, inst.snapshot) + close(inst.closed) +} + +func (inst *instance) Copy(hostSrc string) (string, error) { + base := filepath.Base(hostSrc) + vmDst := filepath.Join("/", base) + + args := append(vmimpl.SCPArgs(inst.debug, inst.sshkey, 22), + hostSrc, fmt.Sprintf("%v@%v:%v", inst.sshuser, inst.ipAddr, vmDst)) + + if inst.debug { + log.Logf(0, "running command: scp %#v", args) + } + + _, err := osutil.RunCmd(3*time.Minute, "", "scp", args...) + if err != nil { + return "", err + } + return vmDst, nil +} + +func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command string) ( + <-chan []byte, <-chan error, error) { + args := append(vmimpl.SSHArgs(inst.debug, inst.sshkey, 22), inst.sshuser+"@"+inst.ipAddr) + dmesg, err := vmimpl.OpenRemoteConsole("ssh", args...) + if err != nil { + return nil, nil, err + } + + rpipe, wpipe, err := osutil.LongPipe() + if err != nil { + dmesg.Close() + return nil, nil, err + } + + args = vmimpl.SSHArgs(inst.debug, inst.sshkey, 22) + // Forward target port as part of the ssh connection (reverse proxy) + if inst.forwardPort != 0 { + proxy := fmt.Sprintf("%v:127.0.0.1:%v", inst.forwardPort, inst.forwardPort) + args = append(args, "-R", proxy) + } + args = append(args, inst.sshuser+"@"+inst.ipAddr, "cd / && exec "+command) + if inst.debug { + log.Logf(0, "running command: ssh %#v", args) + } + cmd := osutil.Command("ssh", args...) + cmd.Stdout = wpipe + cmd.Stderr = wpipe + if err := cmd.Start(); err != nil { + dmesg.Close() + rpipe.Close() + wpipe.Close() + return nil, nil, err + } + wpipe.Close() + + var tee io.Writer + if inst.debug { + tee = os.Stdout + } + merger := vmimpl.NewOutputMerger(tee) + merger.Add("dmesg", dmesg) + merger.Add("ssh", rpipe) + + return vmimpl.Multiplex(cmd, merger, dmesg, timeout, stop, inst.closed, inst.debug) +} + +func (inst *instance) Diagnose() ([]byte, bool) { + return nil, false +} |
