github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/tools/syz-reprolist/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  	"flag"
     8  	"fmt"
     9  	"log"
    10  	"os"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strings"
    14  	"sync"
    15  	"time"
    16  
    17  	"github.com/google/syzkaller/dashboard/dashapi"
    18  	"github.com/google/syzkaller/pkg/csource"
    19  	"github.com/google/syzkaller/pkg/osutil"
    20  	"github.com/google/syzkaller/pkg/vcs"
    21  	"github.com/google/syzkaller/sys/targets"
    22  )
    23  
    24  var (
    25  	flagDashboard    = flag.String("dashboard", "https://syzkaller.appspot.com", "dashboard address")
    26  	flagAPIClients   = flag.String("clients", "", "comma-separated list of api clients")
    27  	flagAPIKey       = flag.String("key", "", "api key")
    28  	flagOutputDir    = flag.String("output", "repros", "output dir")
    29  	flagSyzkallerDir = flag.String("syzkaller", ".", "syzkaller dir")
    30  	flagOS           = flag.String("os", runtime.GOOS, "target OS")
    31  )
    32  
    33  func main() {
    34  	flag.Parse()
    35  	clients := strings.Split(*flagAPIClients, ",")
    36  	if len(clients) == 0 {
    37  		log.Fatalf("api client is required")
    38  	}
    39  	if err := os.MkdirAll(*flagOutputDir, 0755); err != nil {
    40  		log.Fatalf("failed to create output dir: %v", err)
    41  	}
    42  	for _, client := range clients {
    43  		log.Printf("processing client %v", client)
    44  		dash, err := dashapi.New(client, *flagDashboard, *flagAPIKey)
    45  		if err != nil {
    46  			log.Fatalf("dashapi failed: %v", err)
    47  		}
    48  		resp, err := dash.BugList()
    49  		if err != nil {
    50  			log.Fatalf("api call failed: %v", err)
    51  		}
    52  		log.Printf("loading %v bugs", len(resp.List))
    53  		const P = 10
    54  		idchan := make(chan string, 10*P)
    55  		bugchan := make(chan *dashapi.BugReport, 10*P)
    56  		go func() {
    57  			for _, id := range resp.List {
    58  				if _, err := os.Stat(filepath.Join(*flagOutputDir, id+".c")); err == nil {
    59  					log.Printf("%v: already present", id)
    60  					continue
    61  				}
    62  				if _, err := os.Stat(filepath.Join(*flagOutputDir, id+".norepro")); err == nil {
    63  					log.Printf("%v: no repro (cached)", id)
    64  					continue
    65  				}
    66  				if _, err := os.Stat(filepath.Join(*flagOutputDir, id+".error")); err == nil {
    67  					log.Printf("%v: error (cached)", id)
    68  					continue
    69  				}
    70  				idchan <- id
    71  			}
    72  			close(idchan)
    73  		}()
    74  		var wg sync.WaitGroup
    75  		wg.Add(P)
    76  		for p := 0; p < P; p++ {
    77  			go func() {
    78  				defer wg.Done()
    79  				for id := range idchan {
    80  					resp, err := dash.LoadBug(id)
    81  					if err != nil {
    82  						log.Printf("%v: failed to load bug: %v", id, err)
    83  						continue
    84  					}
    85  					if resp.ID == "" {
    86  						continue
    87  					}
    88  					bugchan <- resp
    89  				}
    90  			}()
    91  		}
    92  		go func() {
    93  			wg.Wait()
    94  			close(bugchan)
    95  		}()
    96  		writeRepros(bugchan)
    97  	}
    98  }
    99  
   100  func writeRepros(bugchan chan *dashapi.BugReport) {
   101  	for bug := range bugchan {
   102  		if len(bug.ReproSyz) == 0 {
   103  			log.Printf("%v: %v: no repro", bug.ID, bug.BugStatus)
   104  			file := filepath.Join(*flagOutputDir, bug.ID+".norepro")
   105  			if err := os.WriteFile(file, nil, 0644); err != nil {
   106  				log.Fatalf("failed to write file: %v", err)
   107  			}
   108  			continue
   109  		}
   110  		if len(bug.ReproC) == 0 {
   111  			log.Printf("%v: %v: syz repro on %v", bug.ID, bug.BugStatus, bug.SyzkallerCommit)
   112  			if err := createCRepro(bug); err != nil {
   113  				log.Print(err)
   114  				errText := []byte(err.Error())
   115  				file := filepath.Join(*flagOutputDir, bug.ID+".error")
   116  				if err := os.WriteFile(file, errText, 0644); err != nil {
   117  					log.Fatalf("failed to write file: %v", err)
   118  				}
   119  				continue
   120  			}
   121  		}
   122  		log.Printf("%v: %v: C repro", bug.ID, bug.BugStatus)
   123  		arch := ""
   124  		if bug.Arch != "" && bug.Arch != targets.AMD64 {
   125  			arch = fmt.Sprintf(" arch:%v", bug.Arch)
   126  		}
   127  		repro := []byte(fmt.Sprintf("// %v\n// %v/bug?id=%v\n// status:%v%v\n",
   128  			bug.Title, *flagDashboard, bug.ID, bug.BugStatus, arch))
   129  		repro = append(repro, bug.ReproC...)
   130  		file := filepath.Join(*flagOutputDir, bug.ID+".c")
   131  		if err := os.WriteFile(file, repro, 0644); err != nil {
   132  			log.Fatalf("failed to write file: %v", err)
   133  		}
   134  	}
   135  }
   136  
   137  func createCRepro(bug *dashapi.BugReport) error {
   138  	opts, err := csource.DeserializeOptions(bug.ReproOpts)
   139  	if err != nil {
   140  		return fmt.Errorf("failed to deserialize opts: %w", err)
   141  	}
   142  	file := filepath.Join(*flagOutputDir, bug.ID+".syz")
   143  	if err := os.WriteFile(file, bug.ReproSyz, 0644); err != nil {
   144  		return fmt.Errorf("failed to write file: %w", err)
   145  	}
   146  	repo := vcs.NewSyzkallerRepo(*flagSyzkallerDir, vcs.OptPrecious)
   147  	if _, err := repo.SwitchCommit(bug.SyzkallerCommit); err != nil {
   148  		return fmt.Errorf("failed to checkout commit %v: %w", bug.SyzkallerCommit, err)
   149  	}
   150  	// At some points we checked-in generated descriptions, at some  they are not tracked.
   151  	// Also, new arches were added. This can cause build breakages when switching
   152  	// to random commits. But don't clean whole tree as it will drop all local files/dirs.
   153  	if _, err := osutil.RunCmd(time.Hour, *flagSyzkallerDir, "git", "clean", "-fdx", "./sys/"); err != nil {
   154  		return err
   155  	}
   156  	if _, err := osutil.RunCmd(time.Hour, *flagSyzkallerDir, "make", "prog2c"); err != nil {
   157  		return err
   158  	}
   159  	bin := filepath.Join(*flagSyzkallerDir, "bin", "syz-prog2c")
   160  	args := createProg2CArgs(bug, opts, file)
   161  	output, err := osutil.RunCmd(time.Hour, "", bin, args...)
   162  	if err != nil {
   163  		return err
   164  	}
   165  	bug.ReproC = output
   166  	return err
   167  }
   168  
   169  // Although linter complains about this function, it does not seem complex.
   170  // nolint: gocyclo
   171  func createProg2CArgs(bug *dashapi.BugReport, opts csource.Options, file string) []string {
   172  	haveEnableFlag := containsCommit("dfd609eca1871f01757d6b04b19fc273c87c14e5")
   173  	haveRepeatFlag := containsCommit("b25fc7b83119e8dca728a199fd92e24dd4c33fa4")
   174  	haveCgroupFlag := containsCommit("9753d3be5e6c79e271ed128795039f161ee339b7")
   175  	haveWaitRepeatFlag := containsCommit("c99b02d2248fbdcd6f44037326b16c928f4423f1")
   176  	haveWaitRepeatRemoved := containsCommit("9fe4bdc5f1037a409e82299f36117030114c7b94")
   177  	haveCloseFDs := containsCommit("5c51045d28eb1ad9465a51487d436133ce7b98d2")
   178  	haveOSFlag := containsCommit("aa2533b98d21ebcad5777310215159127bfe3573")
   179  	args := []string{
   180  		"-prog", file,
   181  		"-sandbox", opts.Sandbox,
   182  		fmt.Sprintf("-segv=%v", opts.HandleSegv),
   183  		fmt.Sprintf("-threaded=%v", opts.Threaded),
   184  	}
   185  	if opts.Collide {
   186  		args = append(args, "-collide")
   187  	}
   188  	if haveOSFlag {
   189  		args = append(args, "-os", *flagOS)
   190  	}
   191  	if bug.Arch != "" && bug.Arch != targets.AMD64 {
   192  		args = append(args, "-arch", bug.Arch)
   193  	}
   194  	if opts.Fault {
   195  		args = append(args, []string{
   196  			fmt.Sprintf("-fault_call=%v", opts.FaultCall),
   197  			fmt.Sprintf("-fault_nth=%v", opts.FaultNth),
   198  		}...)
   199  	}
   200  	if opts.Repeat {
   201  		if haveRepeatFlag {
   202  			args = append(args, fmt.Sprintf("-repeat=%v", opts.RepeatTimes))
   203  		} else {
   204  			args = append(args, "-repeat")
   205  		}
   206  	}
   207  	if opts.Procs > 0 {
   208  		args = append(args, fmt.Sprintf("-procs=%v", opts.Procs))
   209  	}
   210  	if opts.UseTmpDir {
   211  		args = append(args, "-tmpdir")
   212  	}
   213  	if opts.Leak {
   214  		args = append(args, "-leak")
   215  	}
   216  	var enable, flags []string
   217  	if opts.NetInjection {
   218  		enable = append(enable, "tun")
   219  		flags = append(flags, "-tun")
   220  	}
   221  	if opts.NetDevices {
   222  		enable = append(enable, "net_dev")
   223  		flags = append(flags, "-netdev")
   224  	}
   225  	if opts.NetReset {
   226  		enable = append(enable, "net_reset")
   227  		flags = append(flags, "-resetnet")
   228  	}
   229  	if opts.Cgroups {
   230  		enable = append(enable, "cgroups")
   231  		if haveCgroupFlag {
   232  			flags = append(flags, "-cgroups")
   233  			if haveWaitRepeatFlag && !haveWaitRepeatRemoved {
   234  				flags = append(flags, "-waitrepeat")
   235  			}
   236  		}
   237  	}
   238  	if opts.BinfmtMisc {
   239  		enable = append(enable, "binfmt_misc")
   240  	}
   241  	if opts.CloseFDs && haveCloseFDs {
   242  		enable = append(enable, "close_fds")
   243  	}
   244  	if opts.DevlinkPCI {
   245  		enable = append(enable, "devlink_pci")
   246  		flags = append(flags, "-devlinkpci")
   247  	}
   248  	if opts.NicVF {
   249  		enable = append(enable, "nic_vf")
   250  		flags = append(flags, "-nic_vf")
   251  	}
   252  	if opts.VhciInjection {
   253  		enable = append(enable, "vhci")
   254  		flags = append(flags, "-vhci")
   255  	}
   256  	if opts.Wifi {
   257  		enable = append(enable, "wifi")
   258  		flags = append(flags, "-wifi")
   259  	}
   260  	if opts.IEEE802154 {
   261  		enable = append(enable, "wifi")
   262  	}
   263  	if !haveEnableFlag {
   264  		args = append(args, flags...)
   265  	} else if len(enable) != 0 {
   266  		args = append(args, "-enable", strings.Join(enable, ","))
   267  	}
   268  	return args
   269  }
   270  
   271  func containsCommit(hash string) bool {
   272  	_, err := osutil.RunCmd(time.Hour, *flagSyzkallerDir, "git", "merge-base", "--is-ancestor", hash, "HEAD")
   273  	return err == nil
   274  }