aboutsummaryrefslogtreecommitdiffstats
path: root/vm
diff options
context:
space:
mode:
authorLaura Peskin <pesk@google.com>2024-04-03 17:56:35 -0700
committerDmitry Vyukov <dvyukov@google.com>2024-06-18 09:36:15 +0000
commit35874462c0edca2e6ca70ed0aabddfb33d8686f3 (patch)
treea549eb200e59b9ec9faa1c9761bd94e9d6407e9f /vm
parent9cedaa08fdf79368c434b42c082a21cd09adf8b8 (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.go280
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" {