From dc6152b573c88e2ebe79bea1fbabeaae39308422 Mon Sep 17 00:00:00 2001 From: Kris Alder Date: Fri, 30 Sep 2022 09:18:25 -0700 Subject: vm/cuttlefish, vm/gce: add custom commands for reading console For Cuttlefish we want to read the console from the emulated device instead of the "host" GCE instance. This allows us to pass a custom command through to gce.Pool (and then to gce.instance) which is used instead. We also need to update runOnHost() to use osutil directly instead of delegating to gceInst.Run(), since it's called during VM creation. When setting up the VM the kernel logs don't exist yet. --- vm/cuttlefish/cuttlefish.go | 42 +++++++++++++------------ vm/gce/gce.go | 77 ++++++++++++++++++++++++++------------------- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/vm/cuttlefish/cuttlefish.go b/vm/cuttlefish/cuttlefish.go index ccea7c7e7..f6f61ee16 100644 --- a/vm/cuttlefish/cuttlefish.go +++ b/vm/cuttlefish/cuttlefish.go @@ -15,13 +15,15 @@ import ( "path/filepath" "time" - "github.com/google/syzkaller/pkg/log" + "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/pkg/report" + "github.com/google/syzkaller/vm/gce" "github.com/google/syzkaller/vm/vmimpl" ) const ( - deviceRoot = "/data/fuzz" + deviceRoot = "/data/fuzz" + consoleReadCmd = "tail -f cuttlefish/instances/cvd-1/kernel.log" ) func init() { @@ -30,16 +32,19 @@ func init() { type Pool struct { env *vmimpl.Env - gcePool vmimpl.Pool + gcePool *gce.Pool } type instance struct { + name string + sshKey string + sshUser string debug bool gceInst vmimpl.Instance } func ctor(env *vmimpl.Env) (vmimpl.Pool, error) { - gcePool, err := vmimpl.Types["gce"].Ctor(env) + gcePool, err := gce.Ctor(env, consoleReadCmd) if err != nil { return nil, fmt.Errorf("failed to create underlying GCE pool: %s", err) } @@ -63,6 +68,9 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) { } inst := &instance{ + name: fmt.Sprintf("%v-%v", pool.env.Name, index), + sshKey: pool.env.SSHKey, + sshUser: pool.env.SSHUser, debug: pool.env.Debug, gceInst: gceInst, } @@ -94,24 +102,18 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) { return inst, nil } -func (inst *instance) runOnHost(timeout time.Duration, cmd string) error { - outc, errc, err := inst.gceInst.Run(timeout, nil, cmd) - if err != nil { - return fmt.Errorf("failed to run command: %s", err) +func (inst *instance) sshArgs(command string) []string { + sshArgs := append(vmimpl.SSHArgs(inst.debug, inst.sshKey, 22), inst.sshUser+"@"+inst.name) + if inst.sshUser != "root" { + return append(sshArgs, "sudo", "bash", "-c", "'"+command+"'") } + return append(sshArgs, command) +} - for { - select { - case <-vmimpl.Shutdown: - return nil - case err := <-errc: - return err - case out, ok := <-outc: - if ok && inst.debug { - log.Logf(1, "%s", out) - } - } - } +func (inst *instance) runOnHost(timeout time.Duration, command string) error { + _, err := osutil.RunCmd(timeout, "/root", "ssh", inst.sshArgs(command)...) + + return err } func (inst *instance) Copy(hostSrc string) (string, error) { diff --git a/vm/gce/gce.go b/vm/gce/gce.go index ef213d12e..ba05af51b 100644 --- a/vm/gce/gce.go +++ b/vm/gce/gce.go @@ -49,26 +49,32 @@ type Config struct { } type Pool struct { - env *vmimpl.Env - cfg *Config - GCE *gce.Context + env *vmimpl.Env + cfg *Config + GCE *gce.Context + consoleReadCmd string // optional: command to read non-standard kernel console } type instance struct { - env *vmimpl.Env - cfg *Config - GCE *gce.Context - debug bool - name string - ip string - gceKey string // per-instance private ssh key associated with the instance - sshKey string // ssh key - sshUser string - closed chan bool - consolew io.WriteCloser + env *vmimpl.Env + cfg *Config + GCE *gce.Context + debug bool + name string + ip string + gceKey string // per-instance private ssh key associated with the instance + sshKey string // ssh key + sshUser string + closed chan bool + consolew io.WriteCloser + consoleReadCmd string // optional: command to read non-standard kernel console } func ctor(env *vmimpl.Env) (vmimpl.Pool, error) { + return Ctor(env, "") +} + +func Ctor(env *vmimpl.Env, consoleReadCmd string) (*Pool, error) { if env.Name == "" { return nil, fmt.Errorf("config param name is empty (required for GCE)") } @@ -123,9 +129,10 @@ func ctor(env *vmimpl.Env) (vmimpl.Pool, error) { } } pool := &Pool{ - cfg: cfg, - env: env, - GCE: GCE, + cfg: cfg, + env: env, + GCE: GCE, + consoleReadCmd: consoleReadCmd, } return pool, nil } @@ -182,16 +189,17 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) { } ok = true inst := &instance{ - env: pool.env, - cfg: pool.cfg, - debug: pool.env.Debug, - GCE: pool.GCE, - name: name, - ip: ip, - gceKey: gceKey, - sshKey: sshKey, - sshUser: sshUser, - closed: make(chan bool), + env: pool.env, + cfg: pool.cfg, + debug: pool.env.Debug, + GCE: pool.GCE, + name: name, + ip: ip, + gceKey: gceKey, + sshKey: sshKey, + sshUser: sshUser, + closed: make(chan bool), + consoleReadCmd: pool.consoleReadCmd, } return inst, nil } @@ -224,11 +232,16 @@ func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command strin return nil, nil, err } - conAddr := fmt.Sprintf("%v.%v.%v.syzkaller.port=1@ssh-serialport.googleapis.com", - inst.GCE.ProjectID, inst.GCE.ZoneID, inst.name) - conArgs := append(vmimpl.SSHArgs(inst.debug, inst.gceKey, 9600), conAddr) - // TODO: remove this later (see also a comment in getSerialPortOutput). - conArgs = append(conArgs, "-o", "HostKeyAlgorithms=+ssh-rsa") + var conArgs []string + if inst.consoleReadCmd == "" { + conAddr := fmt.Sprintf("%v.%v.%v.syzkaller.port=1@ssh-serialport.googleapis.com", + inst.GCE.ProjectID, inst.GCE.ZoneID, inst.name) + conArgs = append(vmimpl.SSHArgs(inst.debug, inst.gceKey, 9600), conAddr) + // TODO: remove this later (see also a comment in getSerialPortOutput). + conArgs = append(conArgs, "-o", "HostKeyAlgorithms=+ssh-rsa") + } else { + conArgs = inst.sshArgs(inst.consoleReadCmd) + } con := osutil.Command("ssh", conArgs...) con.Env = []string{} con.Stdout = conWpipe -- cgit mrf-deployment