github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/tools/syz-db-export/reprolist.go (about) 1 // Copyright 2019 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package main 5 6 import ( 7 "context" 8 "encoding/json" 9 "flag" 10 "fmt" 11 "log" 12 "os" 13 "path" 14 "strings" 15 16 "github.com/google/syzkaller/dashboard/api" 17 "golang.org/x/sync/errgroup" 18 ) 19 20 var ( 21 flagDashboard = flag.String("dashboard", "https://syzkaller.appspot.com", "dashboard address") 22 flagOutputDir = flag.String("output", "export", "output dir") 23 flagNamespace = flag.String("namespace", "upstream", "target namespace") 24 flagToken = flag.String("token", "", "gcp bearer token to disable throttling (contact syzbot first)\n"+ 25 "usage example: ./tools/syz-db-export -namespace upstream -token $(gcloud auth print-access-token)") 26 flagParallel = flag.Int("j", 2, "number of parallel threads") 27 flagVerbose = flag.Bool("v", false, "verbose output") 28 ) 29 30 func main() { 31 flag.Parse() 32 if err := os.MkdirAll(*flagOutputDir, 0755); err != nil { 33 log.Fatalf("alert: failed to create output dir: %v", err) 34 } 35 if *flagNamespace == "" { 36 log.Fatal("alert: namespace can't be empty") 37 } 38 if err := exportNamespace(); err != nil { 39 log.Fatalf("alert: error: %s", err.Error()) 40 } 41 } 42 43 func exportNamespace() error { 44 cli := api.NewClient(*flagDashboard, *flagToken) 45 bugs, err := cli.BugGroups(*flagNamespace, api.BugGroupOpen|api.BugGroupFixed) 46 if err != nil { 47 return err 48 } 49 fmt.Printf("total %d bugs available\n", len(bugs)) 50 51 iBugChan := make(chan int) 52 g, _ := errgroup.WithContext(context.Background()) 53 for i := 0; i < *flagParallel; i++ { 54 g.Go(func() error { 55 for iBug := range iBugChan { 56 bug, err := cli.Bug(bugs[iBug].Link) 57 if err != nil { 58 return err 59 } 60 if *flagVerbose { 61 fmt.Printf("[%v](%v/%v)saving bug %v\n", 62 i, iBug, len(bugs), bug.ID) 63 } 64 if err := saveBug(bug); err != nil { 65 return fmt.Errorf("saveBug(bugID=%s): %w", bug.ID, err) 66 } 67 cReproURL := bug.Crashes[0].CReproducerLink // export max 1 CRepro per bug 68 if cReproURL == "" { 69 continue 70 } 71 reproID := reproIDFromURL(cReproURL) 72 if *flagVerbose { 73 fmt.Printf("[%v](%v/%v)saving c-repro %v for bug %v\n", 74 i, iBug, len(bugs), reproID, bug.ID) 75 } 76 cReproBody, err := cli.Text(cReproURL) 77 if err != nil { 78 return err 79 } 80 if err := saveCRepro(bug.ID, reproID, cReproBody); err != nil { 81 return fmt.Errorf("saveRepro(bugID=%s, reproID=%s): %w", bug.ID, reproID, err) 82 } 83 } 84 return nil 85 }) 86 } 87 errChan := make(chan error) 88 go func() { 89 errChan <- g.Wait() 90 }() 91 for iBug := range bugs { 92 select { 93 case iBugChan <- iBug: 94 case err := <-errChan: 95 return err 96 } 97 } 98 close(iBugChan) 99 return g.Wait() 100 } 101 102 // saceCRepro assumes the bug dir already exists. 103 func saveCRepro(bugID, reproID string, reproData []byte) error { 104 reproPath := path.Join(*flagOutputDir, "bugs", bugID, reproID+".c") 105 if err := os.WriteFile(reproPath, reproData, 0666); err != nil { 106 return fmt.Errorf("os.WriteFile: %w", err) 107 } 108 return nil 109 } 110 111 func reproIDFromURL(url string) string { 112 parts := strings.Split(url, "&") 113 if len(parts) != 2 { 114 log.Panicf("can't split %s in two parts by ?", url) 115 } 116 parts = strings.Split(parts[1], "=") 117 if len(parts) != 2 { 118 log.Panicf("can't split %s in two parts by =", url) 119 } 120 return parts[1] 121 } 122 123 func saveBug(bug *api.Bug) error { 124 jsonBytes, err := json.Marshal(bug) 125 if err != nil { 126 return fmt.Errorf("json.Marshal: %w", err) 127 } 128 bugDir := path.Join(*flagOutputDir, "bugs", bug.ID) 129 if err := os.MkdirAll(bugDir, 0755); err != nil { 130 return fmt.Errorf("os.MkdirAll(%s): %w", bugDir, err) 131 } 132 bugDetailsPath := path.Join(bugDir, "details.json") 133 if err := os.WriteFile(bugDetailsPath, jsonBytes, 0666); err != nil { 134 return fmt.Errorf("os.WriteFile: %w", err) 135 } 136 return nil 137 }