diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2016-01-11 17:40:26 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2016-01-11 17:40:26 +0100 |
| commit | 4d906f05d4d76e153e71a56eccf91eab24286f3a (patch) | |
| tree | f4c9da6ad50d098a9b3a5b08441be7008627c8bb | |
| parent | de48f7b019027f72dc84f56e9e5f0cb03645294e (diff) | |
vm/adb: add adb-based VM
vm/adb can be used to fuzz on real android devices.
| -rw-r--r-- | syz-manager/manager.go | 1 | ||||
| -rw-r--r-- | tools/syz-repro/repro.go | 1 | ||||
| -rw-r--r-- | vm/adb/adb.go | 196 |
3 files changed, 198 insertions, 0 deletions
diff --git a/syz-manager/manager.go b/syz-manager/manager.go index 2bebe8d6e..5a5d63061 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -24,6 +24,7 @@ import ( . "github.com/google/syzkaller/rpctype" "github.com/google/syzkaller/sys" "github.com/google/syzkaller/vm" + _ "github.com/google/syzkaller/vm/adb" _ "github.com/google/syzkaller/vm/kvm" _ "github.com/google/syzkaller/vm/local" _ "github.com/google/syzkaller/vm/qemu" diff --git a/tools/syz-repro/repro.go b/tools/syz-repro/repro.go index ef633ed0d..2b8ad173f 100644 --- a/tools/syz-repro/repro.go +++ b/tools/syz-repro/repro.go @@ -18,6 +18,7 @@ import ( "github.com/google/syzkaller/fileutil" "github.com/google/syzkaller/prog" "github.com/google/syzkaller/vm" + _ "github.com/google/syzkaller/vm/adb" _ "github.com/google/syzkaller/vm/kvm" _ "github.com/google/syzkaller/vm/qemu" ) diff --git a/vm/adb/adb.go b/vm/adb/adb.go new file mode 100644 index 000000000..5fa3d17dd --- /dev/null +++ b/vm/adb/adb.go @@ -0,0 +1,196 @@ +// Copyright 2015 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 adb + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "syscall" + "time" + + "github.com/google/syzkaller/vm" +) + +func init() { + vm.Register("adb", ctor) +} + +type instance struct { + cfg *vm.Config + closed chan bool +} + +func ctor(cfg *vm.Config) (vm.Instance, error) { + if err := validateConfig(cfg); err != nil { + return nil, err + } + inst := &instance{ + cfg: cfg, + closed: make(chan bool), + } + if err := inst.adbOK(); err != nil { + return nil, err + } + if err := inst.adbReboot(); err != nil { + return nil, err + } + return inst, nil +} + +func validateConfig(cfg *vm.Config) error { + if cfg.Bin == "" { + cfg.Bin = "adb" + } + if _, err := os.Stat(cfg.ConsoleDev); err != nil { + return fmt.Errorf("console device '%v' is missing: %v", cfg.ConsoleDev, err) + } + return nil +} + +func (inst *instance) Forward(port int) (string, error) { + // If 35099 turns out to be busy, try to forward random ports several times. + devicePort := 35099 + if out, err := inst.adb("reverse", fmt.Sprintf("tcp:%v", devicePort), fmt.Sprintf("tcp:%v", port)); err != nil { + return "", fmt.Errorf("adb reverse failed: %v\n%s", err, out) + } + return fmt.Sprintf("127.0.0.1:%v", devicePort), nil +} + +func (inst *instance) adb(args ...string) ([]byte, error) { + out, err := exec.Command(inst.cfg.Bin, args...).CombinedOutput() + return out, err +} + +// adbOK checks that adb works and there are is devices attached. +func (inst *instance) adbOK() error { + out, err := inst.adb("shell", "pwd") + if err != nil { + return fmt.Errorf("abd does not work or device is not connected: %v\n%s", err, out) + } + return nil +} + +func (inst *instance) adbReboot() error { + if _, err := inst.adb("reboot"); err != nil { + return fmt.Errorf("adb reboot failed: %v", err) + } + for i := 0; i < 300; i++ { + time.Sleep(time.Second) + if inst.adbOK() == nil { + return nil + } + } + return fmt.Errorf("device did not come up after reboot") +} + +func (inst *instance) Close() { + close(inst.closed) + os.RemoveAll(inst.cfg.Workdir) +} + +func (inst *instance) Copy(hostSrc string) (string, error) { + vmDst := filepath.Join("/data", filepath.Base(hostSrc)) + if _, err := inst.adb("push", hostSrc, vmDst); err != nil { + return "", err + } + return vmDst, nil +} + +func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte, <-chan error, error) { + rpipe, wpipe, err := os.Pipe() + if err != nil { + return nil, nil, fmt.Errorf("failed to create pipe: %v", err) + } + for sz := 128 << 10; sz <= 2<<20; sz *= 2 { + syscall.Syscall(syscall.SYS_FCNTL, wpipe.Fd(), syscall.F_SETPIPE_SZ, uintptr(sz)) + } + + cat := exec.Command("cat", inst.cfg.ConsoleDev) + cat.Stdout = wpipe + cat.Stderr = wpipe + if err := cat.Start(); err != nil { + rpipe.Close() + wpipe.Close() + return nil, nil, fmt.Errorf("failed to start cat %v: %v", inst.cfg.ConsoleDev, err) + + } + catDone := make(chan error, 1) + go func() { + err := cat.Wait() + catDone <- fmt.Errorf("cat exited: %v", err) + }() + + adb := exec.Command(inst.cfg.Bin, "shell", "cd /data; "+command) + adb.Stdout = wpipe + adb.Stderr = wpipe + if err := adb.Start(); err != nil { + cat.Process.Kill() + rpipe.Close() + wpipe.Close() + return nil, nil, fmt.Errorf("failed to start adb: %v", err) + } + adbDone := make(chan error, 1) + go func() { + err := adb.Wait() + adbDone <- fmt.Errorf("adb exited: %v", err) + }() + + wpipe.Close() + outc := make(chan []byte, 10) + errc := make(chan error, 1) + signal := func(err error) { + time.Sleep(5 * time.Second) // wait for any pending output + select { + case errc <- err: + default: + } + } + + go func() { + var buf [64 << 10]byte + var output []byte + for { + n, err := rpipe.Read(buf[:]) + if n != 0 { + if inst.cfg.Debug { + os.Stdout.Write(buf[:n]) + os.Stdout.Write([]byte{'\n'}) + } + output = append(output, buf[:n]...) + select { + case outc <- output: + output = nil + default: + } + time.Sleep(time.Millisecond) + } + if err != nil { + rpipe.Close() + return + } + } + }() + + go func() { + select { + case <-time.After(timeout): + signal(vm.TimeoutErr) + cat.Process.Kill() + adb.Process.Kill() + case <-inst.closed: + signal(fmt.Errorf("instance closed")) + cat.Process.Kill() + adb.Process.Kill() + case err := <-catDone: + signal(err) + adb.Process.Kill() + case err := <-adbDone: + signal(err) + cat.Process.Kill() + } + }() + return outc, errc, nil +} |
