1// Copyright 2015 syzkaller project authors. All rights reserved. 2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4// +build !ppc64le 5 6package adb 7 8import ( 9 "bytes" 10 "fmt" 11 "io" 12 "io/ioutil" 13 "os" 14 "os/exec" 15 "path/filepath" 16 "regexp" 17 "sync" 18 "time" 19 20 "github.com/google/syzkaller/pkg/config" 21 "github.com/google/syzkaller/pkg/log" 22 "github.com/google/syzkaller/pkg/osutil" 23 "github.com/google/syzkaller/vm/vmimpl" 24) 25 26func init() { 27 vmimpl.Register("adb", ctor) 28} 29 30type Config struct { 31 Adb string `json:"adb"` // adb binary name ("adb" by default) 32 Devices []string `json:"devices"` // list of adb device IDs to use 33 34 // Ensure that a device battery level is at 20+% before fuzzing. 35 // Sometimes we observe that a device can't charge during heavy fuzzing 36 // and eventually powers down (which then requires manual intervention). 37 // This option is enabled by default. Turn it off if your devices 38 // don't have battery service, or it causes problems otherwise. 39 BatteryCheck bool `json:"battery_check"` 40} 41 42type Pool struct { 43 env *vmimpl.Env 44 cfg *Config 45} 46 47type instance struct { 48 adbBin string 49 device string 50 console string 51 closed chan bool 52 debug bool 53} 54 55func ctor(env *vmimpl.Env) (vmimpl.Pool, error) { 56 cfg := &Config{ 57 Adb: "adb", 58 BatteryCheck: true, 59 } 60 if err := config.LoadData(env.Config, cfg); err != nil { 61 return nil, fmt.Errorf("failed to parse adb vm config: %v", err) 62 } 63 if _, err := exec.LookPath(cfg.Adb); err != nil { 64 return nil, err 65 } 66 if len(cfg.Devices) == 0 { 67 return nil, fmt.Errorf("no adb devices specified") 68 } 69 devRe := regexp.MustCompile("[0-9A-F]+") 70 for _, dev := range cfg.Devices { 71 if !devRe.MatchString(dev) { 72 return nil, fmt.Errorf("invalid adb device id '%v'", dev) 73 } 74 } 75 if env.Debug { 76 cfg.Devices = cfg.Devices[:1] 77 } 78 pool := &Pool{ 79 cfg: cfg, 80 env: env, 81 } 82 return pool, nil 83} 84 85func (pool *Pool) Count() int { 86 return len(pool.cfg.Devices) 87} 88 89func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) { 90 inst := &instance{ 91 adbBin: pool.cfg.Adb, 92 device: pool.cfg.Devices[index], 93 closed: make(chan bool), 94 debug: pool.env.Debug, 95 } 96 closeInst := inst 97 defer func() { 98 if closeInst != nil { 99 closeInst.Close() 100 } 101 }() 102 if err := inst.repair(); err != nil { 103 return nil, err 104 } 105 inst.console = findConsole(inst.adbBin, inst.device) 106 if pool.cfg.BatteryCheck { 107 if err := inst.checkBatteryLevel(); err != nil { 108 return nil, err 109 } 110 } 111 // Remove temp files from previous runs. 112 if _, err := inst.adb("shell", "rm -Rf /data/syzkaller*"); err != nil { 113 return nil, err 114 } 115 inst.adb("shell", "echo 0 > /proc/sys/kernel/kptr_restrict") 116 closeInst = nil 117 return inst, nil 118} 119 120var ( 121 consoleCacheMu sync.Mutex 122 consoleToDev = make(map[string]string) 123 devToConsole = make(map[string]string) 124) 125 126// findConsole returns console file associated with the dev device (e.g. /dev/ttyUSB0). 127// This code was tested with Suzy-Q and Android Serial Cable (ASC). For Suzy-Q see: 128// https://chromium.googlesource.com/chromiumos/platform/ec/+/master/docs/case_closed_debugging.md 129// The difference between Suzy-Q and ASC is that ASC is a separate cable, 130// so it is not possible to match USB bus/port used by adb with the serial console device; 131// while Suzy-Q console uses the same USB calbe as adb. 132// The overall idea is as follows. We use 'adb shell' to write a unique string onto console, 133// then we read from all console devices and see on what console the unique string appears. 134func findConsole(adb, dev string) string { 135 consoleCacheMu.Lock() 136 defer consoleCacheMu.Unlock() 137 if con := devToConsole[dev]; con != "" { 138 return con 139 } 140 con, err := findConsoleImpl(adb, dev) 141 if err != nil { 142 log.Logf(0, "failed to associate adb device %v with console: %v", dev, err) 143 log.Logf(0, "falling back to 'adb shell dmesg -w'") 144 log.Logf(0, "note: some bugs may be detected as 'lost connection to test machine' with no kernel output") 145 con = "adb" 146 devToConsole[dev] = con 147 return con 148 } 149 devToConsole[dev] = con 150 consoleToDev[con] = dev 151 log.Logf(0, "associating adb device %v with console %v", dev, con) 152 return con 153} 154 155func findConsoleImpl(adb, dev string) (string, error) { 156 // Attempt to find an exact match, at /dev/ttyUSB.{SERIAL} 157 // This is something that can be set up on Linux via 'udev' rules 158 exactCon := "/dev/ttyUSB." + dev 159 if osutil.IsExist(exactCon) { 160 return exactCon, nil 161 } 162 163 // Search all consoles, as described in 'findConsole' 164 consoles, err := filepath.Glob("/dev/ttyUSB*") 165 if err != nil { 166 return "", fmt.Errorf("failed to list /dev/ttyUSB devices: %v", err) 167 } 168 output := make(map[string]*[]byte) 169 errors := make(chan error, len(consoles)) 170 done := make(chan bool) 171 for _, con := range consoles { 172 if consoleToDev[con] != "" { 173 continue 174 } 175 out := new([]byte) 176 output[con] = out 177 go func(con string) { 178 tty, err := vmimpl.OpenConsole(con) 179 if err != nil { 180 errors <- err 181 return 182 } 183 defer tty.Close() 184 go func() { 185 <-done 186 tty.Close() 187 }() 188 *out, _ = ioutil.ReadAll(tty) 189 errors <- nil 190 }(con) 191 } 192 if len(output) == 0 { 193 return "", fmt.Errorf("no unassociated console devices left") 194 } 195 time.Sleep(500 * time.Millisecond) 196 unique := fmt.Sprintf(">>>%v<<<", dev) 197 cmd := osutil.Command(adb, "-s", dev, "shell", "echo", "\"<1>", unique, "\"", ">", "/dev/kmsg") 198 if out, err := cmd.CombinedOutput(); err != nil { 199 return "", fmt.Errorf("failed to run adb shell: %v\n%s", err, out) 200 } 201 time.Sleep(500 * time.Millisecond) 202 close(done) 203 204 var anyErr error 205 for range output { 206 err := <-errors 207 if anyErr == nil && err != nil { 208 anyErr = err 209 } 210 } 211 212 con := "" 213 for con1, out := range output { 214 if bytes.Contains(*out, []byte(unique)) { 215 if con == "" { 216 con = con1 217 } else { 218 anyErr = fmt.Errorf("device is associated with several consoles: %v and %v", con, con1) 219 } 220 } 221 } 222 223 if con == "" { 224 if anyErr != nil { 225 return "", anyErr 226 } 227 return "", fmt.Errorf("no console is associated with this device") 228 } 229 return con, nil 230} 231 232func (inst *instance) Forward(port int) (string, error) { 233 var err error 234 for i := 0; i < 1000; i++ { 235 devicePort := vmimpl.RandomPort() 236 _, err = inst.adb("reverse", fmt.Sprintf("tcp:%v", devicePort), fmt.Sprintf("tcp:%v", port)) 237 if err == nil { 238 return fmt.Sprintf("127.0.0.1:%v", devicePort), nil 239 } 240 } 241 return "", err 242} 243 244func (inst *instance) adb(args ...string) ([]byte, error) { 245 if inst.debug { 246 log.Logf(0, "executing adb %+v", args) 247 } 248 args = append([]string{"-s", inst.device}, args...) 249 out, err := osutil.RunCmd(time.Minute, "", inst.adbBin, args...) 250 if inst.debug { 251 log.Logf(0, "adb returned") 252 } 253 return out, err 254} 255 256func (inst *instance) repair() error { 257 // Assume that the device is in a bad state initially and reboot it. 258 // Ignore errors, maybe we will manage to reboot it anyway. 259 inst.waitForSSH() 260 // History: adb reboot episodically hangs, so we used a more reliable way: 261 // using syz-executor to issue reboot syscall. However, this has stopped 262 // working, probably due to the introduction of seccomp. Therefore, 263 // we revert this to `adb shell reboot` in the meantime, until a more 264 // reliable solution can be sought out. 265 if _, err := inst.adb("shell", "reboot"); err != nil { 266 return err 267 } 268 // Now give it another 5 minutes to boot. 269 if !vmimpl.SleepInterruptible(10 * time.Second) { 270 return fmt.Errorf("shutdown in progress") 271 } 272 if err := inst.waitForSSH(); err != nil { 273 return err 274 } 275 // Switch to root for userdebug builds. 276 inst.adb("root") 277 return inst.waitForSSH() 278} 279 280func (inst *instance) waitForSSH() error { 281 var err error 282 for i := 0; i < 300; i++ { 283 if !vmimpl.SleepInterruptible(time.Second) { 284 return fmt.Errorf("shutdown in progress") 285 } 286 if _, err = inst.adb("shell", "pwd"); err == nil { 287 return nil 288 } 289 } 290 return fmt.Errorf("instance is dead and unrepairable: %v", err) 291} 292 293func (inst *instance) checkBatteryLevel() error { 294 const ( 295 minLevel = 20 296 requiredLevel = 30 297 ) 298 val, err := inst.getBatteryLevel(30) 299 if err != nil { 300 return err 301 } 302 if val >= minLevel { 303 log.Logf(0, "device %v: battery level %v%%, OK", inst.device, val) 304 return nil 305 } 306 for { 307 log.Logf(0, "device %v: battery level %v%%, waiting for %v%%", inst.device, val, requiredLevel) 308 if !vmimpl.SleepInterruptible(time.Minute) { 309 return nil 310 } 311 val, err = inst.getBatteryLevel(0) 312 if err != nil { 313 return err 314 } 315 if val >= requiredLevel { 316 break 317 } 318 } 319 return nil 320} 321 322func (inst *instance) getBatteryLevel(numRetry int) (int, error) { 323 out, err := inst.adb("shell", "dumpsys battery | grep level:") 324 325 // allow for retrying for devices that does not boot up so fast 326 for ; numRetry >= 0 && err != nil; numRetry-- { 327 if numRetry > 0 { 328 // sleep for 5 seconds before retrying 329 time.Sleep(5 * time.Second) 330 out, err = inst.adb("shell", "dumpsys battery | grep level:") 331 } else { 332 if err != nil { 333 return 0, err 334 } 335 } 336 } 337 val := 0 338 for _, c := range out { 339 if c >= '0' && c <= '9' { 340 val = val*10 + int(c) - '0' 341 continue 342 } 343 if val != 0 { 344 break 345 } 346 } 347 if val == 0 { 348 return 0, fmt.Errorf("failed to parse 'dumpsys battery' output: %s", out) 349 } 350 return val, nil 351} 352 353func (inst *instance) Close() { 354 close(inst.closed) 355} 356 357func (inst *instance) Copy(hostSrc string) (string, error) { 358 vmDst := filepath.Join("/data", filepath.Base(hostSrc)) 359 if _, err := inst.adb("push", hostSrc, vmDst); err != nil { 360 return "", err 361 } 362 return vmDst, nil 363} 364 365func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command string) ( 366 <-chan []byte, <-chan error, error) { 367 var tty io.ReadCloser 368 var err error 369 if inst.console == "adb" { 370 tty, err = vmimpl.OpenAdbConsole(inst.adbBin, inst.device) 371 } else { 372 tty, err = vmimpl.OpenConsole(inst.console) 373 } 374 if err != nil { 375 return nil, nil, err 376 } 377 378 adbRpipe, adbWpipe, err := osutil.LongPipe() 379 if err != nil { 380 tty.Close() 381 return nil, nil, err 382 } 383 if inst.debug { 384 log.Logf(0, "starting: adb shell %v", command) 385 } 386 adb := osutil.Command(inst.adbBin, "-s", inst.device, "shell", "cd /data; "+command) 387 adb.Stdout = adbWpipe 388 adb.Stderr = adbWpipe 389 if err := adb.Start(); err != nil { 390 tty.Close() 391 adbRpipe.Close() 392 adbWpipe.Close() 393 return nil, nil, fmt.Errorf("failed to start adb: %v", err) 394 } 395 adbWpipe.Close() 396 397 var tee io.Writer 398 if inst.debug { 399 tee = os.Stdout 400 } 401 merger := vmimpl.NewOutputMerger(tee) 402 merger.Add("console", tty) 403 merger.Add("adb", adbRpipe) 404 405 return vmimpl.Multiplex(adb, merger, tty, timeout, stop, inst.closed, inst.debug) 406} 407 408func (inst *instance) Diagnose() bool { 409 return false 410} 411