aboutsummaryrefslogtreecommitdiffstats
path: root/tools/syz-testbed
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2022-02-25 16:41:33 +0000
committerAleksandr Nogikh <wp32pw@gmail.com>2022-02-25 18:57:42 +0100
commit74265dcb79bafee216a8704619cf20c1f23339c9 (patch)
tree69585a0d3d4816ae08356b739582da58d75dcdde /tools/syz-testbed
parentaba4330c5c9f0ef53b81382508da7ded88ddffc0 (diff)
tools/syz-testbed: accept syzkaller workdir as crash log source
Diffstat (limited to 'tools/syz-testbed')
-rw-r--r--tools/syz-testbed/stats.go19
-rw-r--r--tools/syz-testbed/targets.go55
-rw-r--r--tools/syz-testbed/testbed.go28
3 files changed, 90 insertions, 12 deletions
diff --git a/tools/syz-testbed/stats.go b/tools/syz-testbed/stats.go
index 5ea3a2931..750507945 100644
--- a/tools/syz-testbed/stats.go
+++ b/tools/syz-testbed/stats.go
@@ -18,6 +18,7 @@ import (
type BugInfo struct {
Title string
+ Logs []string
}
type RunResult interface{}
@@ -61,12 +62,24 @@ func collectBugs(workdir string) ([]BugInfo, error) {
}
bugs := []BugInfo{}
for _, dir := range dirs {
- titleBytes, err := ioutil.ReadFile(filepath.Join(crashdir, dir, "description"))
+ bugFolder := filepath.Join(crashdir, dir)
+ titleBytes, err := ioutil.ReadFile(filepath.Join(bugFolder, "description"))
if err != nil {
return nil, err
}
- title := strings.TrimSpace(string(titleBytes))
- bugs = append(bugs, BugInfo{title})
+ bug := BugInfo{
+ Title: strings.TrimSpace(string(titleBytes)),
+ }
+ files, err := ioutil.ReadDir(bugFolder)
+ if err != nil {
+ return nil, err
+ }
+ for _, f := range files {
+ if strings.HasPrefix(f.Name(), "log") {
+ bug.Logs = append(bug.Logs, filepath.Join(bugFolder, f.Name()))
+ }
+ }
+ bugs = append(bugs, bug)
}
return bugs, nil
}
diff --git a/tools/syz-testbed/targets.go b/tools/syz-testbed/targets.go
index 97941d6ca..788561bb3 100644
--- a/tools/syz-testbed/targets.go
+++ b/tools/syz-testbed/targets.go
@@ -7,9 +7,12 @@ import (
"fmt"
"io/ioutil"
"log"
+ "math/rand"
"os"
"path/filepath"
+ "regexp"
"sync"
+ "time"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/pkg/tool"
@@ -36,28 +39,64 @@ var targetConstructors = map[string]func(cfg *TestbedConfig) TestbedTarget{
}
},
"syz-repro": func(cfg *TestbedConfig) TestbedTarget {
- inputs := []*SyzReproInput{}
- if cfg.InputLogs != "" {
- err := filepath.Walk(cfg.InputLogs, func(path string, info os.FileInfo, err error) error {
+ inputFiles := []string{}
+ reproConfig := cfg.ReproConfig
+ if reproConfig.InputLogs != "" {
+ err := filepath.Walk(reproConfig.InputLogs, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
- inputs = append(inputs, &SyzReproInput{
- Path: path,
- runBy: make(map[*Checkout]int),
- })
+ inputFiles = append(inputFiles, path)
}
return nil
})
if err != nil {
tool.Failf("failed to read logs file directory: %s", err)
}
- // TODO: shuffle?
+ } else if reproConfig.InputWorkdir != "" {
+ skipRegexps := []*regexp.Regexp{}
+ for _, reStr := range reproConfig.SkipBugs {
+ skipRegexps = append(skipRegexps, regexp.MustCompile(reStr))
+ }
+ bugs, err := collectBugs(reproConfig.InputWorkdir)
+ if err != nil {
+ tool.Failf("failed to read workdir: %s", err)
+ }
+ r := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
+ for _, bug := range bugs {
+ skip := false
+ for _, re := range skipRegexps {
+ if re.MatchString(bug.Title) {
+ skip = true
+ break
+ }
+ }
+ if skip {
+ continue
+ }
+ logs := append([]string{}, bug.Logs...)
+ for i := 0; i < reproConfig.CrashesPerBug && len(logs) > 0; i++ {
+ randID := r.Intn(len(logs))
+ logs[len(logs)-1], logs[randID] = logs[randID], logs[len(logs)-1]
+ inputFiles = append(inputFiles, logs[len(logs)-1])
+ logs = logs[:len(logs)-1]
+ }
+ }
+ }
+ inputs := []*SyzReproInput{}
+ log.Printf("picked up crash files:")
+ for _, path := range inputFiles {
+ log.Printf("- %s", path)
+ inputs = append(inputs, &SyzReproInput{
+ Path: path,
+ runBy: make(map[*Checkout]int),
+ })
}
if len(inputs) == 0 {
tool.Failf("no inputs given")
}
+ // TODO: shuffle?
return &SyzReproTarget{
config: cfg,
dedupTitle: make(map[string]int),
diff --git a/tools/syz-testbed/testbed.go b/tools/syz-testbed/testbed.go
index 0af48bc1d..d47ee9e53 100644
--- a/tools/syz-testbed/testbed.go
+++ b/tools/syz-testbed/testbed.go
@@ -33,11 +33,11 @@ type TestbedConfig struct {
Target string `json:"target"` // what application to test
MaxInstances int `json:"max_instances"` // max # of simultaneously running instances
RunTime DurationConfig `json:"run_time"` // lifetime of an instance (default "24h")
- InputLogs string `json:"input_logs"` // folder with logs to be processed by syz-repro
HTTP string `json:"http"` // on which port to set up a simple web dashboard
BenchCmp string `json:"benchcmp"` // path to the syz-benchcmp executable
Corpus string `json:"corpus"` // path to the corpus file
Workdir string `json:"workdir"` // instances will be checked out there
+ ReproConfig ReproTestConfig `json:"repro_config"` // syz-repro benchmarking config
ManagerConfig json.RawMessage `json:"manager_config"` // base manager config
Checkouts []CheckoutConfig `json:"checkouts"`
}
@@ -53,6 +53,13 @@ type CheckoutConfig struct {
ManagerConfig json.RawMessage `json:"manager_config"` // a patch to manager config
}
+type ReproTestConfig struct {
+ InputLogs string `json:"input_logs"` // take crash logs from a folder
+ InputWorkdir string `json:"input_workdir"` // take crash logs from a syzkaller's workdir
+ CrashesPerBug int `json:"crashes_per_bug"` // how many crashes must be taken from each bug
+ SkipBugs []string `json:"skip_bugs"` // crashes to exclude from the workdir, list of regexps
+}
+
type TestbedContext struct {
Config *TestbedConfig
Checkouts []*Checkout
@@ -68,6 +75,9 @@ func main() {
Name: "testbed",
Target: "syz-manager",
RunTime: DurationConfig{24 * time.Hour},
+ ReproConfig: ReproTestConfig{
+ CrashesPerBug: 1,
+ },
}
err := config.LoadFile(*flagConfig, &cfg)
if err != nil {
@@ -270,6 +280,19 @@ func (d *DurationConfig) MarshalJSON() ([]byte, error) {
return json.Marshal(d.String())
}
+func checkReproTestConfig(cfg *ReproTestConfig) error {
+ if cfg.InputLogs != "" && !osutil.IsExist(cfg.InputLogs) {
+ return fmt.Errorf("input_log folder does not exist: %v", cfg.InputLogs)
+ }
+ if cfg.InputWorkdir != "" && !osutil.IsExist(cfg.InputWorkdir) {
+ return fmt.Errorf("input_workdir folder does not exist: %v", cfg.InputWorkdir)
+ }
+ if cfg.CrashesPerBug < 1 {
+ return fmt.Errorf("crashes_per_bug cannot be less than 1: %d", cfg.CrashesPerBug)
+ }
+ return nil
+}
+
func checkConfig(cfg *TestbedConfig) error {
testbedNameRe := regexp.MustCompile(`^[0-9a-z\-]{1,20}$`)
if !testbedNameRe.MatchString(cfg.Name) {
@@ -295,6 +318,9 @@ func checkConfig(cfg *TestbedConfig) error {
if _, ok := targetConstructors[cfg.Target]; !ok {
return fmt.Errorf("unknown target %s", cfg.Target)
}
+ if err = checkReproTestConfig(&cfg.ReproConfig); err != nil {
+ return err
+ }
cfg.Corpus = osutil.Abs(cfg.Corpus)
names := make(map[string]bool)
for idx := range cfg.Checkouts {