diff options
| -rw-r--r-- | tools/syz-testbed/stats.go | 19 | ||||
| -rw-r--r-- | tools/syz-testbed/targets.go | 55 | ||||
| -rw-r--r-- | tools/syz-testbed/testbed.go | 28 |
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 { |
