mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-27 05:10:43 +00:00
tools/syz-reprolist: add utility to extract list of reproducers
This commit is contained in:
parent
0f33548a39
commit
64612bfd3c
@ -49,6 +49,8 @@ var apiNamespaceHandlers = map[string]APINamespaceHandler{
|
||||
"manager_stats": apiManagerStats,
|
||||
"commit_poll": apiCommitPoll,
|
||||
"upload_commits": apiUploadCommits,
|
||||
"bug_list": apiBugList,
|
||||
"load_bug": apiLoadBug,
|
||||
}
|
||||
|
||||
type JSONHandler func(c context.Context, r *http.Request) (interface{}, error)
|
||||
@ -971,6 +973,71 @@ func apiManagerStats(c context.Context, ns string, r *http.Request, payload []by
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func apiBugList(c context.Context, ns string, r *http.Request, payload []byte) (interface{}, error) {
|
||||
keys, err := db.NewQuery("Bug").
|
||||
Filter("Namespace=", ns).
|
||||
KeysOnly().
|
||||
GetAll(c, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query bugs: %v", err)
|
||||
}
|
||||
resp := &dashapi.BugListResp{}
|
||||
for _, key := range keys {
|
||||
resp.List = append(resp.List, key.StringID())
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func apiLoadBug(c context.Context, ns string, r *http.Request, payload []byte) (interface{}, error) {
|
||||
req := new(dashapi.LoadBugReq)
|
||||
if err := json.Unmarshal(payload, req); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal request: %v", err)
|
||||
}
|
||||
bug := new(Bug)
|
||||
bugKey := db.NewKey(c, "Bug", req.ID, 0, nil)
|
||||
if err := db.Get(c, bugKey, bug); err != nil {
|
||||
return nil, fmt.Errorf("failed to get bug: %v", err)
|
||||
}
|
||||
if bug.Namespace != ns {
|
||||
return nil, fmt.Errorf("no such bug")
|
||||
}
|
||||
if bug.sanitizeAccess(AccessPublic) > AccessPublic {
|
||||
return nil, nil
|
||||
}
|
||||
crash, _, err := findCrashForBug(c, bug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
build, err := loadBuild(c, ns, crash.BuildID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reproSyz, _, err := getText(c, textReproSyz, crash.ReproSyz)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reproC, _, err := getText(c, textReproC, crash.ReproC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
statuses := map[int]string{
|
||||
BugStatusOpen: "open",
|
||||
BugStatusFixed: "fixed",
|
||||
BugStatusInvalid: "invalid",
|
||||
BugStatusDup: "dup",
|
||||
}
|
||||
resp := &dashapi.LoadBugResp{
|
||||
ID: req.ID,
|
||||
Title: bug.displayTitle(),
|
||||
Status: statuses[bug.Status],
|
||||
SyzkallerCommit: build.SyzkallerCommit,
|
||||
ReproOpts: crash.ReproOpts,
|
||||
ReproSyz: reproSyz,
|
||||
ReproC: reproC,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func findBugForCrash(c context.Context, ns, title string) (*Bug, *db.Key, error) {
|
||||
var bugs []*Bug
|
||||
keys, err := db.NewQuery("Bug").
|
||||
|
@ -460,6 +460,38 @@ func (dash *Dashboard) UploadManagerStats(req *ManagerStatsReq) error {
|
||||
return dash.Query("manager_stats", req, nil)
|
||||
}
|
||||
|
||||
type BugListResp struct {
|
||||
List []string
|
||||
}
|
||||
|
||||
func (dash *Dashboard) BugList() (*BugListResp, error) {
|
||||
resp := new(BugListResp)
|
||||
err := dash.Query("bug_list", nil, resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
type LoadBugReq struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
type LoadBugResp struct {
|
||||
ID string
|
||||
Title string
|
||||
Status string
|
||||
SyzkallerCommit string
|
||||
Arch string
|
||||
ReproOpts []byte
|
||||
ReproSyz []byte
|
||||
ReproC []byte
|
||||
}
|
||||
|
||||
func (dash *Dashboard) LoadBug(id string) (*LoadBugResp, error) {
|
||||
req := LoadBugReq{id}
|
||||
resp := new(LoadBugResp)
|
||||
err := dash.Query("load_bug", req, resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
type (
|
||||
BugStatus int
|
||||
BugNotif int
|
||||
|
229
tools/syz-reprolist/reprolist.go
Normal file
229
tools/syz-reprolist/reprolist.go
Normal file
@ -0,0 +1,229 @@
|
||||
// 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 (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/syzkaller/dashboard/dashapi"
|
||||
"github.com/google/syzkaller/pkg/csource"
|
||||
"github.com/google/syzkaller/pkg/osutil"
|
||||
"github.com/google/syzkaller/pkg/vcs"
|
||||
)
|
||||
|
||||
var (
|
||||
flagDashboard = flag.String("dashboard", "https://syzkaller.appspot.com", "dashboard address")
|
||||
flagAPIClient = flag.String("client", "", "api client")
|
||||
flagAPIKey = flag.String("key", "", "api key")
|
||||
flagOutputDir = flag.String("output", "repros", "output dir")
|
||||
flagSyzkallerDir = flag.String("syzkaller", ".", "syzkaller dir")
|
||||
flagOS = flag.String("os", runtime.GOOS, "target OS")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *flagAPIClient == "" || *flagAPIKey == "" {
|
||||
log.Fatalf("api client and key are required")
|
||||
}
|
||||
if err := os.MkdirAll(*flagOutputDir, 0755); err != nil {
|
||||
log.Fatalf("failed to create output dir: %v", err)
|
||||
}
|
||||
dash := dashapi.New(*flagAPIClient, *flagDashboard, *flagAPIKey)
|
||||
resp, err := dash.BugList()
|
||||
if err != nil {
|
||||
log.Fatalf("api call failed: %v", err)
|
||||
}
|
||||
log.Printf("loading %v bugs", len(resp.List))
|
||||
const P = 10
|
||||
idchan := make(chan string, 10*P)
|
||||
bugchan := make(chan *dashapi.LoadBugResp, 10*P)
|
||||
go func() {
|
||||
for _, id := range resp.List {
|
||||
if _, err := os.Stat(filepath.Join(*flagOutputDir, id+".c")); err == nil {
|
||||
log.Printf("%v: already present", id)
|
||||
continue
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(*flagOutputDir, id+".norepro")); err == nil {
|
||||
log.Printf("%v: no repro (cached)", id)
|
||||
continue
|
||||
}
|
||||
idchan <- id
|
||||
}
|
||||
close(idchan)
|
||||
}()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(P)
|
||||
for p := 0; p < P; p++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for id := range idchan {
|
||||
resp, err := dash.LoadBug(id)
|
||||
if err != nil {
|
||||
log.Printf("%v: failed to load bug: %v", id, err)
|
||||
continue
|
||||
}
|
||||
if resp.ID == "" {
|
||||
continue
|
||||
}
|
||||
bugchan <- resp
|
||||
}
|
||||
}()
|
||||
}
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(bugchan)
|
||||
}()
|
||||
writeRepros(bugchan)
|
||||
}
|
||||
|
||||
func writeRepros(bugchan chan *dashapi.LoadBugResp) {
|
||||
for bug := range bugchan {
|
||||
if len(bug.ReproSyz) == 0 {
|
||||
log.Printf("%v: %v: no repro", bug.ID, bug.Status)
|
||||
file := filepath.Join(*flagOutputDir, bug.ID+".norepro")
|
||||
if err := ioutil.WriteFile(file, nil, 0644); err != nil {
|
||||
log.Fatalf("failed to write file: %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if len(bug.ReproC) == 0 {
|
||||
log.Printf("%v: %v: syz repro on %v", bug.ID, bug.Status, bug.SyzkallerCommit)
|
||||
if err := createCRepro(bug); err != nil {
|
||||
log.Print(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
log.Printf("%v: %v: C repro", bug.ID, bug.Status)
|
||||
arch := ""
|
||||
if bug.Arch != "" && bug.Arch != "amd64" {
|
||||
arch = fmt.Sprintf(" arch:%v", bug.Arch)
|
||||
}
|
||||
repro := []byte(fmt.Sprintf("// %v\n// %v/bug?id=%v\n// status:%v%v\n",
|
||||
bug.Title, *flagDashboard, bug.ID, bug.Status, arch))
|
||||
repro = append(repro, bug.ReproC...)
|
||||
file := filepath.Join(*flagOutputDir, bug.ID+".c")
|
||||
if err := ioutil.WriteFile(file, repro, 0644); err != nil {
|
||||
log.Fatalf("failed to write file: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createCRepro(bug *dashapi.LoadBugResp) error {
|
||||
opts, err := csource.DeserializeOptions(bug.ReproOpts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deserialize opts: %v", err)
|
||||
}
|
||||
file := filepath.Join(*flagOutputDir, bug.ID+".syz")
|
||||
if err := ioutil.WriteFile(file, bug.ReproSyz, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write file: %v", err)
|
||||
}
|
||||
repo := vcs.NewSyzkallerRepo(*flagSyzkallerDir)
|
||||
if _, err := repo.SwitchCommit(bug.SyzkallerCommit); err != nil {
|
||||
return fmt.Errorf("failed to checkout commit %v: %v", bug.SyzkallerCommit, err)
|
||||
}
|
||||
if _, err := osutil.RunCmd(time.Hour, *flagSyzkallerDir, "make", "prog2c"); err != nil {
|
||||
return err
|
||||
}
|
||||
bin := filepath.Join(*flagSyzkallerDir, "bin", "syz-prog2c")
|
||||
args := createProg2CArgs(bug, opts, file)
|
||||
output, err := osutil.RunCmd(time.Hour, "", bin, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bug.ReproC = output
|
||||
return err
|
||||
}
|
||||
|
||||
func createProg2CArgs(bug *dashapi.LoadBugResp, opts csource.Options, file string) []string {
|
||||
haveEnableFlag := containsCommit("dfd609eca1871f01757d6b04b19fc273c87c14e5")
|
||||
haveRepeatFlag := containsCommit("b25fc7b83119e8dca728a199fd92e24dd4c33fa4")
|
||||
haveCgroupFlag := containsCommit("9753d3be5e6c79e271ed128795039f161ee339b7")
|
||||
haveWaitRepeatFlag := containsCommit("c99b02d2248fbdcd6f44037326b16c928f4423f1")
|
||||
haveWaitRepeatRemoved := containsCommit("9fe4bdc5f1037a409e82299f36117030114c7b94")
|
||||
haveCloseFds := containsCommit("5c51045d28eb1ad9465a51487d436133ce7b98d2")
|
||||
haveOSFlag := containsCommit("aa2533b98d21ebcad5777310215159127bfe3573")
|
||||
args := []string{
|
||||
"-prog", file,
|
||||
"-sandbox", opts.Sandbox,
|
||||
fmt.Sprintf("-segv=%v", opts.HandleSegv),
|
||||
fmt.Sprintf("-collide=%v", opts.Collide),
|
||||
fmt.Sprintf("-threaded=%v", opts.Threaded),
|
||||
}
|
||||
if haveOSFlag {
|
||||
args = append(args, "-os", *flagOS)
|
||||
}
|
||||
if bug.Arch != "" && bug.Arch != "amd64" {
|
||||
args = append(args, "-arch", bug.Arch)
|
||||
}
|
||||
if opts.Fault {
|
||||
args = append(args, []string{
|
||||
fmt.Sprintf("-fault_call=%v", opts.FaultCall),
|
||||
fmt.Sprintf("-fault_nth=%v", opts.FaultNth),
|
||||
}...)
|
||||
}
|
||||
if opts.Repeat {
|
||||
if haveRepeatFlag {
|
||||
args = append(args, fmt.Sprintf("-repeat=%v", opts.RepeatTimes))
|
||||
} else {
|
||||
args = append(args, "-repeat")
|
||||
}
|
||||
}
|
||||
if opts.Procs > 0 {
|
||||
args = append(args, fmt.Sprintf("-procs=%v", opts.Procs))
|
||||
}
|
||||
if opts.UseTmpDir {
|
||||
args = append(args, "-tmpdir")
|
||||
}
|
||||
if opts.Leak {
|
||||
args = append(args, "-leak")
|
||||
}
|
||||
var enable, flags []string
|
||||
if opts.EnableTun {
|
||||
enable = append(enable, "tun")
|
||||
flags = append(flags, "-tun")
|
||||
}
|
||||
if opts.EnableNetDev {
|
||||
enable = append(enable, "net_dev")
|
||||
flags = append(flags, "-netdev")
|
||||
}
|
||||
if opts.EnableNetReset {
|
||||
enable = append(enable, "net_reset")
|
||||
flags = append(flags, "-resetnet")
|
||||
}
|
||||
if opts.EnableCgroups {
|
||||
enable = append(enable, "cgroups")
|
||||
if haveCgroupFlag {
|
||||
flags = append(flags, "-cgroups")
|
||||
if haveWaitRepeatFlag && !haveWaitRepeatRemoved {
|
||||
flags = append(flags, "-waitrepeat")
|
||||
}
|
||||
}
|
||||
}
|
||||
if opts.EnableBinfmtMisc {
|
||||
enable = append(enable, "binfmt_misc")
|
||||
}
|
||||
if opts.EnableCloseFds && haveCloseFds {
|
||||
enable = append(enable, "close_fds")
|
||||
}
|
||||
if !haveEnableFlag {
|
||||
args = append(args, flags...)
|
||||
} else if len(enable) != 0 {
|
||||
args = append(args, "-enable", strings.Join(enable, ","))
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func containsCommit(hash string) bool {
|
||||
_, err := osutil.RunCmd(time.Hour, *flagSyzkallerDir, "git", "merge-base", "--is-ancestor", hash, "HEAD")
|
||||
return err == nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user