From e40a490e9c5e71bb3c0aca2a1ddcf7af4845635d Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Mon, 29 Mar 2021 14:37:22 +0800 Subject: vm/adb: add startup_script config --- vm/adb/adb.go | 19 +++++++++++++++++-- vm/isolated/isolated.go | 42 +----------------------------------------- vm/isolated/isolated_test.go | 8 ++++++-- vm/vmimpl/vmimpl.go | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 45 deletions(-) diff --git a/vm/adb/adb.go b/vm/adb/adb.go index cbd5109b1..92e49c346 100644 --- a/vm/adb/adb.go +++ b/vm/adb/adb.go @@ -40,7 +40,8 @@ type Config struct { BatteryCheck bool `json:"battery_check"` // If this option is set (default), the device is rebooted after each crash. // Set it to false to disable reboots. - TargetReboot bool `json:"target_reboot"` + TargetReboot bool `json:"target_reboot"` + StartupScript string `json:"startup_script"` // script to execute after each startup } type Pool struct { @@ -283,7 +284,21 @@ func (inst *instance) repair() error { } // Switch to root for userdebug builds. inst.adb("root") - return inst.waitForSSH() + inst.waitForSSH() + if inst.cfg.StartupScript != "" { + log.Logf(2, "adb: executing startup_script") + // Execute the contents of the StartupScript on the DUT. + contents, err := ioutil.ReadFile(inst.cfg.StartupScript) + if err != nil { + return fmt.Errorf("unable to read startup_script: %v", err) + } + c := string(contents) + if _, err := inst.adb("shell", fmt.Sprintf("sh -c \"%v\"", vmimpl.EscapeDoubleQuotes(c))); err != nil { + return fmt.Errorf("failed to execute startup_script: %v", err) + } + log.Logf(2, "adb: done executing startup_script") + } + return nil } func (inst *instance) waitForSSH() error { diff --git a/vm/isolated/isolated.go b/vm/isolated/isolated.go index 3d69683e9..5bff5f1c0 100755 --- a/vm/isolated/isolated.go +++ b/vm/isolated/isolated.go @@ -214,46 +214,6 @@ func (inst *instance) waitRebootAndSSH(rebootTimeout int, sshTimeout time.Durati return nil } -// Escapes double quotes(and nested double quote escapes). Ignores any other escapes. -// Reference: https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html -func escapeDoubleQuotes(inp string) string { - var ret strings.Builder - for pos := 0; pos < len(inp); pos++ { - // If inp[pos] is not a double quote or a backslash, just use - // as is. - if inp[pos] != '"' && inp[pos] != '\\' { - ret.WriteByte(inp[pos]) - continue - } - // If it is a double quote, escape. - if inp[pos] == '"' { - ret.WriteString("\\\"") - continue - } - // If we detect a backslash, reescape only if what it's already escaping - // is a double-quotes. - temp := "" - j := pos - for ; j < len(inp); j++ { - if inp[j] == '\\' { - temp += string(inp[j]) - continue - } - // If the escape corresponds to a double quotes, re-escape. - // Else, just use as is. - if inp[j] == '"' { - temp = temp + temp + "\\\"" - } else { - temp += string(inp[j]) - } - break - } - ret.WriteString(temp) - pos = j - } - return ret.String() -} - func (inst *instance) repair() error { log.Logf(2, "isolated: trying to ssh") if err := inst.waitForSSH(30 * time.Minute); err != nil { @@ -288,7 +248,7 @@ func (inst *instance) repair() error { return fmt.Errorf("unable to read startup_script: %v", err) } c := string(contents) - if err := inst.ssh(fmt.Sprintf("bash -c \"%v\"", escapeDoubleQuotes(c))); err != nil { + if err := inst.ssh(fmt.Sprintf("bash -c \"%v\"", vmimpl.EscapeDoubleQuotes(c))); err != nil { return fmt.Errorf("failed to execute startup_script: %v", err) } log.Logf(2, "isolated: done executing startup_script") diff --git a/vm/isolated/isolated_test.go b/vm/isolated/isolated_test.go index 74bbff9f1..f1b4dbda0 100644 --- a/vm/isolated/isolated_test.go +++ b/vm/isolated/isolated_test.go @@ -3,7 +3,11 @@ package isolated -import "testing" +import ( + "testing" + + "github.com/google/syzkaller/vm/vmimpl" +) func TestEscapeDoubleQuotes(t *testing.T) { testcases := []struct { @@ -65,7 +69,7 @@ bash -c \"bash -c \\\"ls -al\\\"\"`, }, } for i, tc := range testcases { - output := escapeDoubleQuotes(tc.inp) + output := vmimpl.EscapeDoubleQuotes(tc.inp) if tc.expected != output { t.Fatalf("%v: For input %v Expected escaped string %v got %v", i+1, tc.inp, tc.expected, output) } diff --git a/vm/vmimpl/vmimpl.go b/vm/vmimpl/vmimpl.go index b3b7b19e4..854bef0a0 100644 --- a/vm/vmimpl/vmimpl.go +++ b/vm/vmimpl/vmimpl.go @@ -14,6 +14,7 @@ import ( "math/rand" "net" "os/exec" + "strings" "time" "github.com/google/syzkaller/pkg/log" @@ -181,3 +182,43 @@ func UnusedTCPPort() int { } } } + +// Escapes double quotes(and nested double quote escapes). Ignores any other escapes. +// Reference: https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html +func EscapeDoubleQuotes(inp string) string { + var ret strings.Builder + for pos := 0; pos < len(inp); pos++ { + // If inp[pos] is not a double quote or a backslash, just use + // as is. + if inp[pos] != '"' && inp[pos] != '\\' { + ret.WriteByte(inp[pos]) + continue + } + // If it is a double quote, escape. + if inp[pos] == '"' { + ret.WriteString("\\\"") + continue + } + // If we detect a backslash, reescape only if what it's already escaping + // is a double-quotes. + temp := "" + j := pos + for ; j < len(inp); j++ { + if inp[j] == '\\' { + temp += string(inp[j]) + continue + } + // If the escape corresponds to a double quotes, re-escape. + // Else, just use as is. + if inp[j] == '"' { + temp = temp + temp + "\\\"" + } else { + temp += string(inp[j]) + } + break + } + ret.WriteString(temp) + pos = j + } + return ret.String() +} -- cgit mrf-deployment