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  }