aboutsummaryrefslogtreecommitdiffstats
path: root/tools/syz-db-export
diff options
context:
space:
mode:
Diffstat (limited to 'tools/syz-db-export')
-rw-r--r--tools/syz-db-export/README.md25
-rw-r--r--tools/syz-db-export/reprolist.go138
2 files changed, 163 insertions, 0 deletions
diff --git a/tools/syz-db-export/README.md b/tools/syz-db-export/README.md
new file mode 100644
index 000000000..47b3248c9
--- /dev/null
+++ b/tools/syz-db-export/README.md
@@ -0,0 +1,25 @@
+# Syzbot DB export
+Every week syzbot runs syz-db-export to export its databases:
+1. [Upstream Linux](https://syzkaller.appspot.com/upstream)
+db is [here](https://storage.googleapis.com/artifacts.syzkaller.appspot.com/shared-files/repro-export/upstream.tar.gz).
+2. Contact us if you want see others.
+
+## Export structure
+DB currently includes:
+1. Bugs descriptions.
+2. First C-Reproducer for every bug.
+
+It doesn't include:
+1. Second+ C-Reproducers for every bug.
+2. Syz-Reproducers.
+3. Any reproducer related metadata (like triggering requirements).
+
+## How to export more data
+
+The best way to see more data exported is to modify the tool and send us PR with your changes.
+
+To reproduce locally what syzbot is doing for upstream Linux:
+```golang
+go run ./tools/syz-db-export/... -namespace upstream
+```
+Extending tools/syz-db-export you can teach syzbot to export more.
diff --git a/tools/syz-db-export/reprolist.go b/tools/syz-db-export/reprolist.go
new file mode 100644
index 000000000..24a548423
--- /dev/null
+++ b/tools/syz-db-export/reprolist.go
@@ -0,0 +1,138 @@
+// Copyright 2019 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "path"
+ "strings"
+
+ "github.com/google/syzkaller/dashboard/api"
+ "golang.org/x/sync/errgroup"
+)
+
+var (
+ flagDashboard = flag.String("dashboard", "https://syzkaller.appspot.com", "dashboard address")
+ flagOutputDir = flag.String("output", "export", "output dir")
+ flagNamespace = flag.String("namespace", "upstream", "target namespace")
+ flagToken = flag.String("token", "", "gcp bearer token to disable throttling (contact syzbot first)\n"+
+ "usage example: ./tools/syz-db-export -namespace upstream -token $(gcloud auth print-access-token)")
+ flagParallel = flag.Int("j", 2, "number of parallel threads")
+ flagVerbose = flag.Bool("v", false, "verbose output")
+)
+
+func main() {
+ flag.Parse()
+ if err := os.MkdirAll(*flagOutputDir, 0755); err != nil {
+ log.Fatalf("alert: failed to create output dir: %v", err)
+ }
+ if *flagNamespace == "" {
+ log.Fatal("alert: namespace can't be empty")
+ }
+ if err := exportNamespace(); err != nil {
+ log.Fatalf("alert: error: %s", err.Error())
+ }
+}
+
+func exportNamespace() error {
+ cli := api.NewClient(*flagDashboard, *flagToken)
+ bugs, err := cli.BugGroups(*flagNamespace, api.BugGroupOpen|api.BugGroupFixed)
+ if err != nil {
+ return err
+ }
+ fmt.Printf("total %d bugs available\n", len(bugs))
+
+ iBugChan := make(chan int)
+ g, _ := errgroup.WithContext(context.Background())
+ for i := 0; i < *flagParallel; i++ {
+ i := i
+ g.Go(func() error {
+ for iBug := range iBugChan {
+ bug, err := cli.Bug(bugs[iBug].Link)
+ if err != nil {
+ return err
+ }
+ if *flagVerbose {
+ fmt.Printf("[%v](%v/%v)saving bug %v\n",
+ i, iBug, len(bugs), bug.ID)
+ }
+ if err := saveBug(bug); err != nil {
+ return fmt.Errorf("saveBug(bugID=%s): %w", bug.ID, err)
+ }
+ cReproURL := bug.Crashes[0].CReproducerLink // export max 1 CRepro per bug
+ if cReproURL == "" {
+ continue
+ }
+ reproID := reproIDFromURL(cReproURL)
+ if *flagVerbose {
+ fmt.Printf("[%v](%v/%v)saving c-repro %v for bug %v\n",
+ i, iBug, len(bugs), reproID, bug.ID)
+ }
+ cReproBody, err := cli.Text(cReproURL)
+ if err != nil {
+ return err
+ }
+ if err := saveCRepro(bug.ID, reproID, cReproBody); err != nil {
+ return fmt.Errorf("saveRepro(bugID=%s, reproID=%s): %w", bug.ID, reproID, err)
+ }
+ }
+ return nil
+ })
+ }
+ errChan := make(chan error)
+ go func() {
+ errChan <- g.Wait()
+ }()
+ for iBug := range bugs {
+ select {
+ case iBugChan <- iBug:
+ case err := <-errChan:
+ return err
+ }
+ }
+ close(iBugChan)
+ return g.Wait()
+}
+
+// saceCRepro assumes the bug dir already exists.
+func saveCRepro(bugID, reproID string, reproData []byte) error {
+ reproPath := path.Join(*flagOutputDir, "bugs", bugID, reproID+".c")
+ if err := os.WriteFile(reproPath, reproData, 0666); err != nil {
+ return fmt.Errorf("os.WriteFile: %w", err)
+ }
+ return nil
+}
+
+func reproIDFromURL(url string) string {
+ parts := strings.Split(url, "&")
+ if len(parts) != 2 {
+ log.Panicf("can't split %s in two parts by ?", url)
+ }
+ parts = strings.Split(parts[1], "=")
+ if len(parts) != 2 {
+ log.Panicf("can't split %s in two parts by =", url)
+ }
+ return parts[1]
+}
+
+func saveBug(bug *api.Bug) error {
+ jsonBytes, err := json.Marshal(bug)
+ if err != nil {
+ return fmt.Errorf("json.Marshal: %w", err)
+ }
+ bugDir := path.Join(*flagOutputDir, "bugs", bug.ID)
+ if err := os.MkdirAll(bugDir, 0755); err != nil {
+ return fmt.Errorf("os.MkdirAll(%s): %w", bugDir, err)
+ }
+ bugDetailsPath := path.Join(bugDir, "details.json")
+ if err := os.WriteFile(bugDetailsPath, jsonBytes, 0666); err != nil {
+ return fmt.Errorf("os.WriteFile: %w", err)
+ }
+ return nil
+}