aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-09-27 17:07:31 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-09-27 18:59:50 +0200
commit9fc15c7ea33806fc224fea01dafd6c11a2790c7e (patch)
treece00effe54652f2b6a632679c97e254299eb77fa
parentfd98417f4d1c796ef86cd8f6ebdd2d3c146a30a3 (diff)
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.
-rw-r--r--syz-manager/mgrconfig/mgrconfig.go7
-rw-r--r--vm/gce/gce.go74
-rw-r--r--vm/isolated/isolated.go16
-rw-r--r--vm/qemu/qemu.go18
-rw-r--r--vm/vmimpl/vmimpl.go3
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
}