aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2016-08-30 14:33:39 +0200
committerDmitry Vyukov <dvyukov@google.com>2016-08-30 14:33:39 +0200
commitbc9b349bd77c6ccc224a8e7cf72e76595bfc0342 (patch)
treefd5e3f485dbd6ee40caa52038a117aa9e26a2359
parent26a5cf9efa8d0fc4ffbf2f9810241d4fdead8e16 (diff)
vm/adb: support multiple adb devices
Device IDs are specified in "devices" config param.
-rw-r--r--config/config.go71
-rw-r--r--syz-manager/manager.go6
-rw-r--r--tools/syz-repro/repro.go15
-rw-r--r--vm/adb/adb.go57
-rw-r--r--vm/vm.go28
5 files changed, 119 insertions, 58 deletions
diff --git a/config/config.go b/config/config.go
index b98b481db..83e92a25b 100644
--- a/config/config.go
+++ b/config/config.go
@@ -34,10 +34,11 @@ type Config struct {
Debug bool // dump all VM output to console
Output string // one of stdout/dmesg/file (useful only for local VM)
- Syzkaller string // path to syzkaller checkout (syz-manager will look for binaries in bin subdir)
- Type string // VM type (qemu, kvm, local)
- Count int // number of VMs
- Procs int // number of parallel processes inside of every VM
+ Syzkaller string // path to syzkaller checkout (syz-manager will look for binaries in bin subdir)
+ Type string // VM type (qemu, kvm, local)
+ Count int // number of VMs (don't secify for adb, instead specify devices)
+ Devices []string // device IDs for adb
+ Procs int // number of parallel processes inside of every VM
Sandbox string // type of sandbox to use during fuzzing:
// "none": don't do anything special (has false positives, e.g. due to killing init)
@@ -48,8 +49,6 @@ type Config struct {
Cover bool // use kcov coverage (default: true)
Leak bool // do memory leak checking
- ConsoleDev string // console device for adb vm
-
Enable_Syscalls []string
Disable_Syscalls []string
Suppressions []string
@@ -98,21 +97,36 @@ func parse(data []byte) (*Config, map[int]bool, []*regexp.Regexp, error) {
if cfg.Type == "" {
return nil, nil, nil, fmt.Errorf("config param type is empty")
}
- if cfg.Type == "none" {
+ switch cfg.Type {
+ case "none":
if cfg.Count != 0 {
return nil, nil, nil, fmt.Errorf("invalid config param count: %v, type \"none\" does not support param count", cfg.Count)
}
if cfg.Rpc == "" {
return nil, nil, nil, fmt.Errorf("config param rpc is empty (required for type \"none\")")
}
- } else {
+ if len(cfg.Devices) != 0 {
+ return nil, nil, nil, fmt.Errorf("type %v does not support devices param", cfg.Type)
+ }
+ case "adb":
+ if cfg.Count != 0 {
+ return nil, nil, nil, fmt.Errorf("don't specify count for adb, instead specify devices")
+ }
+ if len(cfg.Devices) == 0 {
+ return nil, nil, nil, fmt.Errorf("specify at least 1 adb device")
+ }
+ cfg.Count = len(cfg.Devices)
+ default:
if cfg.Count <= 0 || cfg.Count > 1000 {
return nil, nil, nil, fmt.Errorf("invalid config param count: %v, want (1, 1000]", cfg.Count)
}
- if cfg.Rpc == "" {
- cfg.Rpc = "localhost:0"
+ if len(cfg.Devices) != 0 {
+ return nil, nil, nil, fmt.Errorf("type %v does not support devices param", cfg.Type)
}
}
+ if cfg.Rpc == "" {
+ cfg.Rpc = "localhost:0"
+ }
if cfg.Procs <= 0 {
cfg.Procs = 1
}
@@ -219,26 +233,31 @@ func parseSuppressions(cfg *Config) ([]*regexp.Regexp, error) {
return suppressions, nil
}
-func CreateVMConfig(cfg *Config) (*vm.Config, error) {
+func CreateVMConfig(cfg *Config, index int) (*vm.Config, error) {
+ if index < 0 || index >= cfg.Count {
+ return nil, fmt.Errorf("invalid VM index %v (count %v)", index, cfg.Count)
+ }
workdir, index, err := fileutil.ProcessTempDir(cfg.Workdir)
if err != nil {
return nil, fmt.Errorf("failed to create instance temp dir: %v", err)
}
vmCfg := &vm.Config{
- Name: fmt.Sprintf("%v-%v", cfg.Type, index),
- Index: index,
- Workdir: workdir,
- Bin: cfg.Bin,
- Kernel: cfg.Kernel,
- Cmdline: cfg.Cmdline,
- Image: cfg.Image,
- Initrd: cfg.Initrd,
- Sshkey: cfg.Sshkey,
- Executor: filepath.Join(cfg.Syzkaller, "bin", "syz-executor"),
- ConsoleDev: cfg.ConsoleDev,
- Cpu: cfg.Cpu,
- Mem: cfg.Mem,
- Debug: cfg.Debug,
+ Name: fmt.Sprintf("%v-%v", cfg.Type, index),
+ Index: index,
+ Workdir: workdir,
+ Bin: cfg.Bin,
+ Kernel: cfg.Kernel,
+ Cmdline: cfg.Cmdline,
+ Image: cfg.Image,
+ Initrd: cfg.Initrd,
+ Sshkey: cfg.Sshkey,
+ Executor: filepath.Join(cfg.Syzkaller, "bin", "syz-executor"),
+ Cpu: cfg.Cpu,
+ Mem: cfg.Mem,
+ Debug: cfg.Debug,
+ }
+ if len(cfg.Devices) != 0 {
+ vmCfg.Device = cfg.Devices[index]
}
return vmCfg, nil
}
@@ -264,11 +283,11 @@ func checkUnknownFields(data []byte) (string, error) {
"Syzkaller",
"Type",
"Count",
+ "Devices",
"Procs",
"Cover",
"Sandbox",
"Leak",
- "ConsoleDev",
"Enable_Syscalls",
"Disable_Syscalls",
"Suppressions",
diff --git a/syz-manager/manager.go b/syz-manager/manager.go
index b88956583..fa457a2c8 100644
--- a/syz-manager/manager.go
+++ b/syz-manager/manager.go
@@ -166,18 +166,18 @@ func RunManager(cfg *config.Config, syscalls map[int]bool, suppressions []*regex
var wg sync.WaitGroup
wg.Add(cfg.Count + 1)
for i := 0; i < cfg.Count; i++ {
- first := i == 0
+ i := i
go func() {
defer wg.Done()
for {
- vmCfg, err := config.CreateVMConfig(cfg)
+ vmCfg, err := config.CreateVMConfig(cfg, i)
if atomic.LoadUint32(&shutdown) != 0 {
break
}
if err != nil {
fatalf("failed to create VM config: %v", err)
}
- ok := mgr.runInstance(vmCfg, first)
+ ok := mgr.runInstance(vmCfg, i == 0)
if atomic.LoadUint32(&shutdown) != 0 {
break
}
diff --git a/tools/syz-repro/repro.go b/tools/syz-repro/repro.go
index 63adad52c..1e616c986 100644
--- a/tools/syz-repro/repro.go
+++ b/tools/syz-repro/repro.go
@@ -30,12 +30,13 @@ var (
flagCount = flag.Int("count", 0, "number of VMs to use (overrides config count param)")
instances chan VM
- bootRequests chan bool
+ bootRequests chan int
shutdown = make(chan struct{})
)
type VM struct {
vm.Instance
+ index int
execprogBin string
executorBin string
}
@@ -70,12 +71,12 @@ func main() {
log.Printf("target crash: '%s'", crashDesc)
instances = make(chan VM, cfg.Count)
- bootRequests = make(chan bool, cfg.Count)
+ bootRequests = make(chan int, cfg.Count)
for i := 0; i < cfg.Count; i++ {
- bootRequests <- true
+ bootRequests <- i
go func() {
- for range bootRequests {
- vmCfg, err := config.CreateVMConfig(cfg)
+ for index := range bootRequests {
+ vmCfg, err := config.CreateVMConfig(cfg, index)
if err != nil {
log.Fatalf("failed to create VM config: %v", err)
}
@@ -91,7 +92,7 @@ func main() {
if err != nil {
log.Fatalf("failed to copy to VM: %v", err)
}
- instances <- VM{inst, execprogBin, executorBin}
+ instances <- VM{inst, index, execprogBin, executorBin}
}
}()
}
@@ -197,7 +198,7 @@ func repro(cfg *config.Config, entries []*prog.LogEntry, crashStart int) {
func returnInstance(inst VM, res bool) {
if res {
// The test crashed, discard the VM and issue another boot request.
- bootRequests <- true
+ bootRequests <- inst.index
inst.Close()
} else {
// The test did not crash, reuse the same VM in future.
diff --git a/vm/adb/adb.go b/vm/adb/adb.go
index 91e59d519..e8090eddb 100644
--- a/vm/adb/adb.go
+++ b/vm/adb/adb.go
@@ -11,6 +11,9 @@ import (
"os"
"os/exec"
"path/filepath"
+ "regexp"
+ "strconv"
+ "sync"
"time"
"github.com/google/syzkaller/vm"
@@ -21,8 +24,9 @@ func init() {
}
type instance struct {
- cfg *vm.Config
- closed chan bool
+ cfg *vm.Config
+ console string
+ closed chan bool
}
func ctor(cfg *vm.Config) (vm.Instance, error) {
@@ -39,6 +43,9 @@ func ctor(cfg *vm.Config) (vm.Instance, error) {
if err := validateConfig(cfg); err != nil {
return nil, err
}
+ if err := inst.findConsole(); err != nil {
+ return nil, err
+ }
if err := inst.repair(); err != nil {
return nil, err
}
@@ -52,9 +59,43 @@ 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)
+ if !regexp.MustCompile("[0-9A-F]+").MatchString(cfg.Device) {
+ return fmt.Errorf("invalid adb device id '%v'", cfg.Device)
+ }
+ return nil
+}
+
+var (
+ consoleCacheMu sync.Mutex
+ consoleCache = make(map[string]string)
+)
+
+func (inst *instance) findConsole() error {
+ // Case Closed Debugging using Suzy-Q:
+ // https://chromium.googlesource.com/chromiumos/platform/ec/+/master/docs/case_closed_debugging.md
+ consoleCacheMu.Lock()
+ defer consoleCacheMu.Unlock()
+ if inst.console = consoleCache[inst.cfg.Device]; inst.console != "" {
+ return nil
+ }
+ out, err := exec.Command(inst.cfg.Bin, "devices", "-l").CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("failed to execute 'adb devices -l': %v\n%v\n", err, string(out))
+ }
+ re := regexp.MustCompile(fmt.Sprintf("%v +device usb:([0-9]+)-([0-9]+)\\.([0-9]+) ", inst.cfg.Device))
+ match := re.FindAllStringSubmatch(string(out), 1)
+ if match == nil {
+ return fmt.Errorf("can't find adb device '%v' in 'adb devices' output:\n%v\n", inst.cfg.Device, string(out))
+ }
+ bus, _ := strconv.ParseUint(match[0][1], 10, 64)
+ port, _ := strconv.ParseUint(match[0][2], 10, 64)
+ files, err := filepath.Glob(fmt.Sprintf("/sys/bus/usb/devices/%v-%v.2:1.1/ttyUSB*", bus, port))
+ if err != nil || len(files) == 0 {
+ return fmt.Errorf("can't find any ttyUDB devices for adb device '%v' on bus %v-%v", inst.cfg.Device, bus, port)
}
+ inst.console = "/dev/" + filepath.Base(files[0])
+ consoleCache[inst.cfg.Device] = inst.console
+ log.Printf("associating adb device %v with console %v", inst.cfg.Device, inst.console)
return nil
}
@@ -77,7 +118,7 @@ func (inst *instance) adb(args ...string) error {
}
defer wpipe.Close()
defer rpipe.Close()
- cmd := exec.Command(inst.cfg.Bin, args...)
+ cmd := exec.Command(inst.cfg.Bin, append([]string{"-s", inst.cfg.Device}, args...)...)
cmd.Stdout = wpipe
cmd.Stderr = wpipe
if err := cmd.Start(); err != nil {
@@ -158,13 +199,13 @@ func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte,
return nil, nil, err
}
- cat := exec.Command("cat", inst.cfg.ConsoleDev)
+ cat := exec.Command("cat", inst.console)
cat.Stdout = catWpipe
cat.Stderr = catWpipe
if err := cat.Start(); err != nil {
catRpipe.Close()
catWpipe.Close()
- return nil, nil, fmt.Errorf("failed to start cat %v: %v", inst.cfg.ConsoleDev, err)
+ return nil, nil, fmt.Errorf("failed to start cat %v: %v", inst.console, err)
}
catWpipe.Close()
@@ -186,7 +227,7 @@ func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte,
if inst.cfg.Debug {
log.Printf("starting: adb shell %v", command)
}
- adb := exec.Command(inst.cfg.Bin, "shell", "cd /data; "+command)
+ adb := exec.Command(inst.cfg.Bin, "-s", inst.cfg.Device, "shell", "cd /data; "+command)
adb.Stdout = adbWpipe
adb.Stderr = adbWpipe
if err := adb.Start(); err != nil {
diff --git a/vm/vm.go b/vm/vm.go
index 45c0f53f9..1fc52514b 100644
--- a/vm/vm.go
+++ b/vm/vm.go
@@ -32,20 +32,20 @@ type Instance interface {
}
type Config struct {
- Name string
- Index int
- Workdir string
- Bin string
- Initrd string
- Kernel string
- Cmdline string
- Image string
- Sshkey string
- Executor string
- ConsoleDev string
- Cpu int
- Mem int
- Debug bool
+ Name string
+ Index int
+ Workdir string
+ Bin string
+ Initrd string
+ Kernel string
+ Cmdline string
+ Image string
+ Sshkey string
+ Executor string
+ Device string
+ Cpu int
+ Mem int
+ Debug bool
}
type ctorFunc func(cfg *Config) (Instance, error)