diff options
| author | Laura Peskin <pesk@google.com> | 2024-04-03 17:56:35 -0700 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2024-06-18 09:36:15 +0000 |
| commit | 35874462c0edca2e6ca70ed0aabddfb33d8686f3 (patch) | |
| tree | a549eb200e59b9ec9faa1c9761bd94e9d6407e9f /vm | |
| parent | 9cedaa08fdf79368c434b42c082a21cd09adf8b8 (diff) | |
vm/starnix: fuzz over ssh instead of adb
fuchsia build instructions:
fx set workbench_eng.x64 --with-base \
//src/testing/fuzzing/syzkaller/starnix:syzkaller_starnix \
&& fx build
Co-authored-by: eep@google.com
Co-authored-by: mvanotti@google.com
Diffstat (limited to 'vm')
| -rw-r--r-- | vm/starnix/starnix.go | 280 |
1 files changed, 125 insertions, 155 deletions
diff --git a/vm/starnix/starnix.go b/vm/starnix/starnix.go index 61c893305..32ea0d72b 100644 --- a/vm/starnix/starnix.go +++ b/vm/starnix/starnix.go @@ -4,12 +4,14 @@ package starnix import ( + "bytes" "encoding/json" "fmt" "io" "os" "os/exec" "path/filepath" + "strconv" "strings" "time" @@ -50,9 +52,8 @@ type instance struct { rpipe io.ReadCloser wpipe io.WriteCloser fuchsiaLogs *exec.Cmd - adb *exec.Cmd - adbTimeout time.Duration - adbRetryWait time.Duration + sshPubKey string + sshPrivKey string executor string merger *vmimpl.OutputMerger diagnose chan bool @@ -68,9 +69,6 @@ func ctor(env *vmimpl.Env) (vmimpl.Pool, error) { if cfg.Count < 1 || cfg.Count > 128 { return nil, fmt.Errorf("invalid config param count: %v, want [1, 128]", cfg.Count) } - if _, err := exec.LookPath("adb"); err != nil { - return nil, err - } pool := &Pool{ count: cfg.Count, @@ -92,8 +90,8 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) { cfg: pool.cfg, debug: pool.env.Debug, workdir: workdir, - // This file is auto-generated inside createAdbScript. - executor: filepath.Join(workdir, "adb_executor.sh"), + // This file is auto-generated inside createExecutorScript. + executor: filepath.Join(workdir, "executor.sh"), } closeInst := inst defer func() { @@ -119,6 +117,16 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) { inst.fuchsiaDirectory, err) } + pubkey, err := osutil.RunCmd(30*time.Second, inst.fuchsiaDirectory, inst.ffxBinary, "config", "get", "ssh.pub") + if err != nil { + return nil, err + } + inst.sshPubKey = string(bytes.Trim(pubkey, "\"\n")) + privkey, err := osutil.RunCmd(30*time.Second, inst.fuchsiaDirectory, inst.ffxBinary, "config", "get", "ssh.priv") + if err != nil { + return nil, err + } + inst.sshPrivKey = string(bytes.Trim(privkey, "\"\n")) if err := inst.boot(); err != nil { return nil, err @@ -140,22 +148,16 @@ func (inst *instance) boot() error { if err := inst.startFuchsiaVM(); err != nil { return fmt.Errorf("instance %s: could not start Fuchsia VM: %w", inst.name, err) } - if err := inst.startAdbServerAndConnection(2*time.Minute, 3*time.Second); err != nil { - return fmt.Errorf("instance %s: could not start and connect to the adb server: %w", inst.name, err) + if err := inst.startSshdAndConnect(); err != nil { + return fmt.Errorf("instance %s: could not start sshd: %w", inst.name, err) } if inst.debug { log.Logf(0, "instance %s: setting up...", inst.name) } - if err := inst.restartAdbAsRoot(); err != nil { - return fmt.Errorf("instance %s: could not restart adb with root access: %w", inst.name, err) + if err := inst.createExecutorScript(); err != nil { + return fmt.Errorf("instance %s: could not create executor script: %w", inst.name, err) } - - if err := inst.createAdbScript(); err != nil { - return fmt.Errorf("instance %s: could not create adb script: %w", inst.name, err) - } - - err := inst.startFuchsiaLogs() - if err != nil { + if err := inst.startFuchsiaLogs(); err != nil { return fmt.Errorf("instance %s: could not start fuchsia logs: %w", inst.name, err) } if inst.debug { @@ -170,10 +172,6 @@ func (inst *instance) Close() { inst.fuchsiaLogs.Process.Kill() inst.fuchsiaLogs.Wait() } - if inst.adb != nil { - inst.adb.Process.Kill() - inst.adb.Wait() - } if inst.merger != nil { inst.merger.Wait() } @@ -212,84 +210,106 @@ func (inst *instance) startFuchsiaLogs() error { return nil } -func (inst *instance) startAdbServerAndConnection(timeout, retry time.Duration) error { - cmd := osutil.Command(inst.ffxBinary, "--target", inst.name, "starnix", "adb", - "-p", fmt.Sprintf("%d", inst.port)) +func (inst *instance) startSshdAndConnect() error { + cmd := osutil.Command( + inst.ffxBinary, + "--target", + inst.name, + "component", + "run", + "/core/starnix_runner/playground:alpine", + "fuchsia-pkg://fuchsia.com/syzkaller_starnix#meta/alpine_container.cm", + ) cmd.Dir = inst.fuchsiaDirectory - if err := cmd.Start(); err != nil { + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return err + } + if inst.debug { + log.Logf(1, "instance %s: started alpine container", inst.name) + } + cmd = osutil.Command(inst.ffxBinary, + "--target", + inst.name, + "component", + "run", + "/core/starnix_runner/playground:alpine/daemons:start_sshd", + "fuchsia-pkg://fuchsia.com/syzkaller_starnix#meta/start_sshd.cm", + ) + cmd.Dir = inst.fuchsiaDirectory + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { return err } if inst.debug { - log.Logf(0, fmt.Sprintf("instance %s: the adb bridge is listening on 127.0.0.1:%d", inst.name, inst.port)) + log.Logf(1, "instance %s: started sshd on alpine container", inst.name) + } + cmd = osutil.Command( + inst.ffxBinary, + "--target", + inst.name, + "component", + "copy", + inst.sshPubKey, + "/core/starnix_runner/playground:alpine::out::fs_root/tmp/authorized_keys", + ) + cmd.Dir = inst.fuchsiaDirectory + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return err } - inst.adb = cmd - inst.adbTimeout = timeout - inst.adbRetryWait = retry - return inst.connectToAdb() -} - -func (inst *instance) connectToAdb() error { - startTime := time.Now() - for { - if inst.debug { - log.Logf(1, "instance %s: attempting to connect to adb", inst.name) - } - connectOutput, err := osutil.RunCmd( - 2*time.Minute, - inst.fuchsiaDirectory, - "adb", - "connect", - fmt.Sprintf("127.0.0.1:%d", inst.port)) - if err == nil && strings.HasPrefix(string(connectOutput), "connected to") { - if inst.debug { - log.Logf(0, "instance %s: connected to adb server", inst.name) - } - return nil - } - inst.runCommand("adb", "disconnect", fmt.Sprintf("127.0.0.1:%d", inst.port)) - if inst.debug { - log.Logf(1, "instance %s: adb connect failed", inst.name) - } - if time.Since(startTime) > (inst.adbTimeout - inst.adbRetryWait) { - return fmt.Errorf("instance %s: can't connect to adb server", inst.name) - } - vmimpl.SleepInterruptible(inst.adbRetryWait) + if inst.debug { + log.Logf(0, "instance %s: copied ssh key", inst.name) } + return inst.connect() } -func (inst *instance) restartAdbAsRoot() error { - startTime := time.Now() - for { - if inst.debug { - log.Logf(1, "instance %s: attempting to restart adbd with root access", inst.name) - } - err := inst.runCommand( - "adb", - "-s", - fmt.Sprintf("127.0.0.1:%d", inst.port), - "root", - ) - if err == nil { - return nil - } - if inst.debug { - log.Logf(1, "instance %s: adb root failed", inst.name) - } - if time.Since(startTime) > (inst.adbTimeout - inst.adbRetryWait) { - return fmt.Errorf("instance %s: can't restart adbd with root access", inst.name) - } - vmimpl.SleepInterruptible(inst.adbRetryWait) +func (inst *instance) connect() error { + if inst.debug { + log.Logf(1, "instance %s: attempting to connect to starnix container over ssh", inst.name) + } + address, err := osutil.RunCmd( + 30*time.Second, + inst.fuchsiaDirectory, + inst.ffxBinary, + "--target", + inst.name, + "target", + "get-ssh-address") + if err != nil { + return err } + if inst.debug { + log.Logf(0, fmt.Sprintf("instance %s: the fuchsia instance's address is %s", inst.name, address)) + } + cmd := osutil.Command( + "ssh", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-i", inst.sshPrivKey, + "-NT", + "-L", fmt.Sprintf("localhost:%d:localhost:7000", inst.port), fmt.Sprintf("ssh://%s", bytes.Trim(address, "\n")), + ) + cmd.Stderr = os.Stderr + if err = cmd.Start(); err != nil { + return err + } + time.Sleep(5 * time.Second) + if inst.debug { + log.Logf(0, "instance %s: forwarded port from starnix container", inst.name) + } + return nil } // Script for telling syz-fuzzer how to connect to syz-executor. -func (inst *instance) createAdbScript() error { - adbScript := fmt.Sprintf( +func (inst *instance) createExecutorScript() error { + script := fmt.Sprintf( `#!/usr/bin/env bash - adb_port=$1 - fuzzer_args=${@:2} - exec adb -s 127.0.0.1:$adb_port shell "cd %s; ./syz-executor $fuzzer_args"`, targetDir) - return os.WriteFile(inst.executor, []byte(adbScript), 0777) + ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes \ + -o ConnectTimeout=30 -o ControlMaster=auto -o ControlPath=/tmp/syzkaller-ssh-control \ + -o ControlPersist=10m -i %s -p $1 root@127.0.0.1 "cd %s; exec ./syz-executor ${@:2}"`, + inst.sshPrivKey, targetDir) + return os.WriteFile(inst.executor, []byte(script), 0777) } func (inst *instance) ffx(args ...string) error { @@ -316,77 +336,27 @@ func (inst *instance) Forward(port int) (string, error) { } func (inst *instance) Copy(hostSrc string) (string, error) { - startTime := time.Now() base := filepath.Base(hostSrc) if base == "syz-fuzzer" || base == "syz-execprog" { return hostSrc, nil // we will run these on host. } vmDst := filepath.Join(targetDir, base) - - for { - if inst.debug { - log.Logf(1, "instance %s: attempting to push executor binary over ADB", inst.name) - } - output, err := osutil.RunCmd( - 1*time.Minute, - inst.fuchsiaDirectory, - "adb", - "-s", - fmt.Sprintf("127.0.0.1:%d", inst.port), - "push", - hostSrc, - vmDst) - if err == nil { - err = inst.Chmod(vmDst) - return vmDst, err - } - // Retryable connection errors usually look like "adb: error: ... : device offline" - // or "adb: error: ... closed" - if !strings.HasPrefix(string(output), "adb: error:") { - log.Logf(0, "instance %s: adb push failed: %s", inst.name, string(output)) - return vmDst, err - } - if inst.debug { - log.Logf(1, "instance %s: adb push failed: %s", inst.name, string(output)) - } - if time.Since(startTime) > (inst.adbTimeout - inst.adbRetryWait) { - return vmDst, fmt.Errorf("instance %s: can't push executor binary to VM", inst.name) - } - vmimpl.SleepInterruptible(inst.adbRetryWait) - } -} - -func (inst *instance) Chmod(vmDst string) error { - startTime := time.Now() - for { - if inst.debug { - log.Logf(1, "instance %s: attempting to chmod executor script over ADB", inst.name) - } - output, err := osutil.RunCmd( - 1*time.Minute, - inst.fuchsiaDirectory, - "adb", - "-s", - fmt.Sprintf("127.0.0.1:%d", inst.port), - "shell", - fmt.Sprintf("chmod +x %s", vmDst)) - if err == nil { - return nil - } - // Retryable connection errors usually look like "adb: error: ... : device offline" - // or "adb: error: ... closed" - if !strings.HasPrefix(string(output), "adb: error:") { - log.Logf(0, "instance %s: adb shell command failed: %s", inst.name, string(output)) - return err - } - if inst.debug { - log.Logf(1, "instance %s: adb shell command failed: %s", inst.name, string(output)) - } - if time.Since(startTime) > (inst.adbTimeout - inst.adbRetryWait) { - return fmt.Errorf("instance %s: can't chmod executor script for VM", inst.name) - } - vmimpl.SleepInterruptible(inst.adbRetryWait) - } + if inst.debug { + log.Logf(1, "instance %s: attempting to push binary %s to instance over scp", inst.name, base) + } + err := inst.runCommand( + "scp", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-i", inst.sshPrivKey, + "-P", strconv.Itoa(inst.port), + hostSrc, + fmt.Sprintf("root@localhost:%s", vmDst), + ) + if err == nil { + return vmDst, err + } + return vmDst, fmt.Errorf("instance %s: can't push binary %s to instance over scp", inst.name, base) } func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command string) ( @@ -395,7 +365,7 @@ func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command strin if err != nil { return nil, nil, err } - inst.merger.Add("adb", rpipe) + inst.merger.Add("ssh", rpipe) args := strings.Split(command, " ") if bin := filepath.Base(args[0]); bin == "syz-fuzzer" || bin == "syz-execprog" { |
