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 }