From 9fc15c7ea33806fc224fea01dafd6c11a2790c7e Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Wed, 27 Sep 2017 17:07:31 +0200 Subject: vm/gce: windows support Support custom pre-created images. Support non-root user. Use dir instead of pwd on windows. Don't use sudo on windows. --- syz-manager/mgrconfig/mgrconfig.go | 7 ++-- vm/gce/gce.go | 74 ++++++++++++++++++++++---------------- vm/isolated/isolated.go | 16 ++++----- vm/qemu/qemu.go | 18 ++++++---- vm/vmimpl/vmimpl.go | 3 +- 5 files changed, 69 insertions(+), 49 deletions(-) diff --git a/syz-manager/mgrconfig/mgrconfig.go b/syz-manager/mgrconfig/mgrconfig.go index 523cbb585..bd568d36d 100644 --- a/syz-manager/mgrconfig/mgrconfig.go +++ b/syz-manager/mgrconfig/mgrconfig.go @@ -27,7 +27,8 @@ type Config struct { Kernel_Src string // kernel source directory Tag string // arbitrary optional tag that is saved along with crash reports (e.g. branch/commit) Image string // linux image for VMs - Sshkey string // root ssh key for the image (may be empty for some VM types) + Sshkey string // ssh key for the image (may be empty for some VM types) + Ssh_User string // ssh user ("root" by default) Hub_Client string Hub_Addr string @@ -81,6 +82,7 @@ func LoadFile(filename string) (*Config, error) { func DefaultValues() *Config { return &Config{ + Ssh_User: "root", Cover: true, Reproduce: true, Sandbox: "setuid", @@ -271,7 +273,8 @@ func CreateVMEnv(cfg *Config, debug bool) *vm.Env { Arch: cfg.TargetVMArch, Workdir: cfg.Workdir, Image: cfg.Image, - Sshkey: cfg.Sshkey, + SshKey: cfg.Sshkey, + SshUser: cfg.Ssh_User, Debug: debug, Config: cfg.VM, } diff --git a/vm/gce/gce.go b/vm/gce/gce.go index 084189522..bc1a7ac3c 100644 --- a/vm/gce/gce.go +++ b/vm/gce/gce.go @@ -39,16 +39,17 @@ type Config struct { Count int // number of VMs to use Machine_Type string // GCE machine type (e.g. "n1-highcpu-2") GCS_Path string // GCS path to upload image + GCE_Image string // Pre-created GCE image to use } type Pool struct { - env *vmimpl.Env - cfg *Config - GCE *gce.Context - gceImage string + env *vmimpl.Env + cfg *Config + GCE *gce.Context } type instance struct { + env *vmimpl.Env cfg *Config GCE *gce.Context debug bool @@ -84,9 +85,12 @@ func ctor(env *vmimpl.Env) (vmimpl.Pool, error) { if cfg.Machine_Type == "" { return nil, fmt.Errorf("machine_type parameter is empty") } - if cfg.GCS_Path == "" { + if cfg.GCE_Image == "" && cfg.GCS_Path == "" { return nil, fmt.Errorf("gcs_path parameter is empty") } + if cfg.GCE_Image != "" && env.Image != "" { + return nil, fmt.Errorf("both image and gce_image are specified") + } GCE, err := gce.NewContext() if err != nil { @@ -95,24 +99,25 @@ func ctor(env *vmimpl.Env) (vmimpl.Pool, error) { Logf(0, "GCE initialized: running on %v, internal IP %v, project %v, zone %v", GCE.Instance, GCE.InternalIP, GCE.ProjectID, GCE.ZoneID) - gcsImage := filepath.Join(cfg.GCS_Path, env.Name+"-image.tar.gz") - Logf(0, "uploading image to %v...", gcsImage) - if err := uploadImageToGCS(env.Image, gcsImage); err != nil { - return nil, err - } - gceImage := env.Name - Logf(0, "creating GCE image %v...", gceImage) - if err := GCE.DeleteImage(gceImage); err != nil { - return nil, fmt.Errorf("failed to delete GCE image: %v", err) - } - if err := GCE.CreateImage(gceImage, gcsImage); err != nil { - return nil, fmt.Errorf("failed to create GCE image: %v", err) + if cfg.GCE_Image == "" { + cfg.GCE_Image = env.Name + gcsImage := filepath.Join(cfg.GCS_Path, env.Name+"-image.tar.gz") + Logf(0, "uploading image to %v...", gcsImage) + if err := uploadImageToGCS(env.Image, gcsImage); err != nil { + return nil, err + } + Logf(0, "creating GCE image %v...", cfg.GCE_Image) + if err := GCE.DeleteImage(cfg.GCE_Image); err != nil { + return nil, fmt.Errorf("failed to delete GCE image: %v", err) + } + if err := GCE.CreateImage(cfg.GCE_Image, gcsImage); err != nil { + return nil, fmt.Errorf("failed to create GCE image: %v", err) + } } pool := &Pool{ - cfg: cfg, - env: env, - GCE: GCE, - gceImage: gceImage, + cfg: cfg, + env: env, + GCE: GCE, } return pool, nil } @@ -139,7 +144,7 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) { return nil, err } Logf(0, "creating instance: %v", name) - ip, err := pool.GCE.CreateInstance(name, pool.cfg.Machine_Type, pool.gceImage, string(gceKeyPub)) + ip, err := pool.GCE.CreateInstance(name, pool.cfg.Machine_Type, pool.cfg.GCE_Image, string(gceKeyPub)) if err != nil { return nil, err } @@ -150,19 +155,20 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) { pool.GCE.DeleteInstance(name, true) } }() - sshKey := pool.env.Sshkey - sshUser := "root" + sshKey := pool.env.SshKey + sshUser := pool.env.SshUser if sshKey == "" { // Assuming image supports GCE ssh fanciness. sshKey = gceKey sshUser = "syzkaller" } Logf(0, "wait instance to boot: %v (%v)", name, ip) - if err := waitInstanceBoot(pool.env.Debug, ip, sshKey, sshUser); err != nil { + if err := pool.waitInstanceBoot(ip, sshKey, sshUser); err != nil { return nil, err } ok = true inst := &instance{ + env: pool.env, cfg: pool.cfg, debug: pool.env.Debug, GCE: pool.GCE, @@ -272,8 +278,10 @@ func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command strin sshRpipe.Close() return nil, nil, err } - if inst.sshUser != "root" { - command = fmt.Sprintf("sudo bash -c '%v'", command) + if inst.env.OS == "linux" { + if inst.sshUser != "root" { + command = fmt.Sprintf("sudo bash -c '%v'", command) + } } args := append(sshArgs(inst.debug, inst.sshKey, "-p", 22), inst.sshUser+"@"+inst.name, command) ssh := exec.Command("ssh", args...) @@ -334,13 +342,17 @@ func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command strin return merger.Output, errc, nil } -func waitInstanceBoot(debug bool, ip, sshKey, sshUser string) error { +func (pool *Pool) waitInstanceBoot(ip, sshKey, sshUser string) error { + pwd := "pwd" + if pool.env.OS == "windows" { + pwd = "dir" + } for i := 0; i < 100; i++ { if !vmimpl.SleepInterruptible(5 * time.Second) { return fmt.Errorf("shutdown in progress") } - args := append(sshArgs(debug, sshKey, "-p", 22), sshUser+"@"+ip, "pwd") - if _, err := runCmd(debug, "ssh", args...); err == nil { + args := append(sshArgs(pool.env.Debug, sshKey, "-p", 22), sshUser+"@"+ip, pwd) + if _, err := runCmd(pool.env.Debug, "ssh", args...); err == nil { return nil } } @@ -425,7 +437,7 @@ func sshArgs(debug bool, sshKey, portArg string, port int) []string { "-o", "BatchMode=yes", "-o", "IdentitiesOnly=yes", "-o", "StrictHostKeyChecking=no", - "-o", "ConnectTimeout=5", + "-o", "ConnectTimeout=10", } if debug { args = append(args, "-v") diff --git a/vm/isolated/isolated.go b/vm/isolated/isolated.go index ceb3aacc2..39d7378b1 100644 --- a/vm/isolated/isolated.go +++ b/vm/isolated/isolated.go @@ -54,8 +54,8 @@ func ctor(env *vmimpl.Env) (vmimpl.Pool, error) { return nil, fmt.Errorf("config param target_dir is empty") } // sshkey is optional - if env.Sshkey != "" && !osutil.IsExist(env.Sshkey) { - return nil, fmt.Errorf("ssh key '%v' does not exist", env.Sshkey) + if env.SshKey != "" && !osutil.IsExist(env.SshKey) { + return nil, fmt.Errorf("ssh key '%v' does not exist", env.SshKey) } if env.Debug { cfg.Targets = cfg.Targets[:1] @@ -74,10 +74,10 @@ func (pool *Pool) Count() int { func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) { inst := &instance{ cfg: pool.cfg, - target: pool.cfg.Targets[index], + target: pool.env.SshUser + "@" + pool.cfg.Targets[index], closed: make(chan bool), debug: pool.env.Debug, - sshkey: pool.env.Sshkey, + sshkey: pool.env.SshKey, } closeInst := inst defer func() { @@ -120,7 +120,7 @@ func (inst *instance) ssh(command string) ([]byte, error) { return nil, err } - args := append(inst.sshArgs("-p"), "root@"+inst.target, command) + args := append(inst.sshArgs("-p"), inst.target, command) if inst.debug { Logf(0, "running command: ssh %#v", args) } @@ -230,7 +230,7 @@ func (inst *instance) Copy(hostSrc string) (string, error) { baseName := filepath.Base(hostSrc) vmDst := filepath.Join(inst.cfg.Target_Dir, baseName) inst.ssh("pkill -9 '" + baseName + "'; rm -f '" + vmDst + "'") - args := append(inst.sshArgs("-P"), hostSrc, "root@"+inst.target+":"+vmDst) + args := append(inst.sshArgs("-P"), hostSrc, inst.target+":"+vmDst) cmd := exec.Command("scp", args...) if inst.debug { Logf(0, "running command: scp %#v", args) @@ -257,7 +257,7 @@ func (inst *instance) Copy(hostSrc string) (string, error) { } func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command string) (<-chan []byte, <-chan error, error) { - args := append(inst.sshArgs("-p"), "root@"+inst.target) + args := append(inst.sshArgs("-p"), inst.target) dmesg, err := vmimpl.OpenRemoteConsole("ssh", args...) if err != nil { return nil, nil, err @@ -275,7 +275,7 @@ func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command strin proxy := fmt.Sprintf("%v:127.0.0.1:%v", inst.port, inst.port) args = append(args, "-R", proxy) } - args = append(args, "root@"+inst.target, "cd "+inst.cfg.Target_Dir+" && exec "+command) + args = append(args, inst.target, "cd "+inst.cfg.Target_Dir+" && exec "+command) Logf(0, "running command: ssh %#v", args) if inst.debug { Logf(0, "running command: ssh %#v", args) diff --git a/vm/qemu/qemu.go b/vm/qemu/qemu.go index 0e02504a1..e2e68f82f 100644 --- a/vm/qemu/qemu.go +++ b/vm/qemu/qemu.go @@ -51,6 +51,7 @@ type instance struct { debug bool workdir string sshkey string + sshuser string port int rpipe io.ReadCloser wpipe io.WriteCloser @@ -118,8 +119,8 @@ func ctor(env *vmimpl.Env) (vmimpl.Pool, error) { if !osutil.IsExist(env.Image) { return nil, fmt.Errorf("image file '%v' does not exist", env.Image) } - if !osutil.IsExist(env.Sshkey) { - return nil, fmt.Errorf("ssh key '%v' does not exist", env.Sshkey) + if !osutil.IsExist(env.SshKey) { + return nil, fmt.Errorf("ssh key '%v' does not exist", env.SshKey) } } if cfg.Cpu <= 0 || cfg.Cpu > 1024 { @@ -142,9 +143,11 @@ func (pool *Pool) Count() int { } func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) { - sshkey := pool.env.Sshkey + sshkey := pool.env.SshKey + sshuser := pool.env.SshUser if pool.env.Image == "9p" { sshkey = filepath.Join(workdir, "key") + sshuser = "root" keygen := exec.Command("ssh-keygen", "-t", "rsa", "-b", "2048", "-N", "", "-C", "", "-f", sshkey) if out, err := keygen.CombinedOutput(); err != nil { return nil, fmt.Errorf("failed to execute ssh-keygen: %v\n%s", err, out) @@ -156,7 +159,7 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) { } for i := 0; ; i++ { - inst, err := pool.ctor(workdir, sshkey, index) + inst, err := pool.ctor(workdir, sshkey, sshuser, index) if err == nil { return inst, nil } @@ -167,13 +170,14 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) { } } -func (pool *Pool) ctor(workdir, sshkey string, index int) (vmimpl.Instance, error) { +func (pool *Pool) ctor(workdir, sshkey, sshuser string, index int) (vmimpl.Instance, error) { inst := &instance{ cfg: pool.cfg, image: pool.env.Image, debug: pool.env.Debug, workdir: workdir, sshkey: sshkey, + sshuser: sshuser, } closeInst := inst defer func() { @@ -383,7 +387,7 @@ func (inst *instance) Copy(hostSrc string) (string, error) { basePath = "/tmp" } vmDst := filepath.Join(basePath, filepath.Base(hostSrc)) - args := append(inst.sshArgs("-P"), hostSrc, "root@localhost:"+vmDst) + args := append(inst.sshArgs("-P"), hostSrc, inst.sshuser+"@localhost:"+vmDst) cmd := exec.Command("scp", args...) if inst.debug { Logf(0, "running command: scp %#v", args) @@ -416,7 +420,7 @@ func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command strin } inst.merger.Add("ssh", rpipe) - args := append(inst.sshArgs("-p"), "root@localhost", command) + args := append(inst.sshArgs("-p"), inst.sshuser+"@localhost", command) if inst.debug { Logf(0, "running command: ssh %#v", args) } diff --git a/vm/vmimpl/vmimpl.go b/vm/vmimpl/vmimpl.go index 6540ecc91..617f9bc0f 100644 --- a/vm/vmimpl/vmimpl.go +++ b/vm/vmimpl/vmimpl.go @@ -50,7 +50,8 @@ type Env struct { Arch string // target arch Workdir string Image string - Sshkey string + SshKey string + SshUser string Debug bool Config []byte // json-serialized VM-type-specific config } -- cgit mrf-deployment