diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-02-17 18:52:07 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-02-17 18:52:07 +0100 |
| commit | 1466d0cb9f3cfcd088ad7e7e0c8b79b521d129ab (patch) | |
| tree | f89f0c47aed7d545b9bb6230b9b89a97bf564bef | |
| parent | 104126b97057a9a60c055762dfb1b29ae2b458f5 (diff) | |
manager: add ability to communicate with dashboard
Manager can send crashes and repros to a dashboard app.
Dashboard app is not checked-in yet.
| -rw-r--r-- | config/config.go | 22 | ||||
| -rw-r--r-- | dashboard/dash.go | 92 | ||||
| -rw-r--r-- | syz-manager/manager.go | 56 |
3 files changed, 169 insertions, 1 deletions
diff --git a/config/config.go b/config/config.go index a0e21dc26..410d9c663 100644 --- a/config/config.go +++ b/config/config.go @@ -39,6 +39,9 @@ type Config struct { Hub_Addr string Hub_Key string + Dashboard_Addr string + Dashboard_Key string + Syzkaller string // path to syzkaller checkout (syz-manager will look for binaries in bin subdir) Type string // VM type (qemu, kvm, local) Count int // number of VMs (don't secify for adb, instead specify devices) @@ -197,6 +200,23 @@ func parse(data []byte) (*Config, map[int]bool, error) { return nil, nil, err } + if cfg.Hub_Addr != "" { + if cfg.Name == "" { + return nil, nil, fmt.Errorf("hub_addr is set, but name is empty") + } + if cfg.Hub_Key == "" { + return nil, nil, fmt.Errorf("hub_addr is set, but hub_key is empty") + } + } + if cfg.Dashboard_Addr != "" { + if cfg.Name == "" { + return nil, nil, fmt.Errorf("dashboard_addr is set, but name is empty") + } + if cfg.Dashboard_Key == "" { + return nil, nil, fmt.Errorf("dashboard_addr is set, but dashboard_key is empty") + } + } + return cfg, syscalls, nil } @@ -332,6 +352,8 @@ func checkUnknownFields(data []byte) (string, error) { "Output", "Hub_Addr", "Hub_Key", + "Dashboard_Addr", + "Dashboard_Key", "Syzkaller", "Type", "Count", diff --git a/dashboard/dash.go b/dashboard/dash.go new file mode 100644 index 000000000..2ac8c1439 --- /dev/null +++ b/dashboard/dash.go @@ -0,0 +1,92 @@ +// Copyright 2017 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 dashboard defines data structures used in dashboard communication +// and provides client interface. +package dashboard + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" +) + +type Dashboard struct { + Addr string + Client string + Key string +} + +type Crash struct { + Tag string + Desc string + Log []byte + Report []byte +} + +type Repro struct { + Crash Crash + Reproduced bool + Opts string + Prog []byte + CProg []byte +} + +type Patch struct { + Title string + Diff []byte +} + +func (dash *Dashboard) ReportCrash(crash *Crash) error { + return dash.query("add_crash", crash, nil) +} + +func (dash *Dashboard) ReportRepro(repro *Repro) error { + return dash.query("add_repro", repro, nil) +} + +func (dash *Dashboard) PollPatches() (string, error) { + hash := "" + err := dash.query("poll_patches", nil, &hash) + return hash, err +} + +func (dash *Dashboard) GetPatches() ([]Patch, error) { + var patches []Patch + err := dash.query("get_patches", nil, &patches) + return patches, err +} + +func (dash *Dashboard) query(method string, req, reply interface{}) error { + values := make(url.Values) + values.Add("client", dash.Client) + values.Add("key", dash.Key) + values.Add("method", method) + var body io.Reader + if req != nil { + data, err := json.Marshal(req) + if err != nil { + return fmt.Errorf("failed to marshal request: %v", err) + } + body = bytes.NewReader(data) + } + resp, err := http.Post(fmt.Sprintf("%v/api?%v", dash.Addr, values.Encode()), "application/json", body) + if err != nil { + return fmt.Errorf("http request failed: %v", err) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + data, _ := ioutil.ReadAll(resp.Body) + return fmt.Errorf("request failed with %v: %s", resp.Status, data) + } + if reply != nil { + if err := json.NewDecoder(resp.Body).Decode(reply); err != nil { + return fmt.Errorf("failed to unmarshal response: %v", err) + } + } + return nil +} diff --git a/syz-manager/manager.go b/syz-manager/manager.go index 2da12cd33..6806061c3 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -22,6 +22,7 @@ import ( "github.com/google/syzkaller/config" "github.com/google/syzkaller/cover" "github.com/google/syzkaller/csource" + "github.com/google/syzkaller/dashboard" "github.com/google/syzkaller/db" "github.com/google/syzkaller/hash" . "github.com/google/syzkaller/log" @@ -59,6 +60,8 @@ type Manager struct { fresh bool numFuzzing uint32 + dash *dashboard.Dashboard + mu sync.Mutex enabledSyscalls string enabledCalls []string // as determined by fuzzer @@ -207,6 +210,14 @@ func RunManager(cfg *config.Config, syscalls map[int]bool) { mgr.port = s.Addr().(*net.TCPAddr).Port go s.Serve() + if cfg.Dashboard_Addr != "" { + mgr.dash = &dashboard.Dashboard{ + Addr: cfg.Dashboard_Addr, + Client: cfg.Name, + Key: cfg.Dashboard_Key, + } + } + go func() { for lastTime := time.Now(); ; { time.Sleep(10 * time.Second) @@ -517,7 +528,19 @@ func (mgr *Manager) saveCrash(crash *Crash) { } else { crash.text = symbolized } - ioutil.WriteFile(filepath.Join(dir, fmt.Sprintf("report%v", oldestI)), []byte(crash.text), 0660) + ioutil.WriteFile(filepath.Join(dir, fmt.Sprintf("report%v", oldestI)), crash.text, 0660) + } + + if mgr.dash != nil { + dc := &dashboard.Crash{ + Tag: mgr.cfg.Tag, + Desc: crash.desc, + Log: crash.output, + Report: crash.text, + } + if err := mgr.dash.ReportCrash(dc); err != nil { + Logf(0, "failed to report crash to dashboard: %v", err) + } } } @@ -544,6 +567,18 @@ func (mgr *Manager) saveRepro(crash *Crash, res *repro.Result) { sig := hash.Hash([]byte(crash.desc)) dir := filepath.Join(mgr.crashdir, sig.String()) if res == nil { + if mgr.dash != nil { + dr := &dashboard.Repro{ + Crash: dashboard.Crash{ + Tag: mgr.cfg.Tag, + Desc: crash.desc, + }, + Reproduced: false, + } + if err := mgr.dash.ReportRepro(dr); err != nil { + Logf(0, "failed to report repro to dashboard: %v", err) + } + } for i := 0; i < maxReproAttempts; i++ { name := filepath.Join(dir, fmt.Sprintf("repro%v", i)) if _, err := os.Stat(name); err != nil { @@ -562,6 +597,7 @@ func (mgr *Manager) saveRepro(crash *Crash, res *repro.Result) { if len(crash.text) > 0 { ioutil.WriteFile(filepath.Join(dir, "repro.report"), []byte(crash.text), 0660) } + var cprogText []byte if res.CRepro { cprog, err := csource.Write(res.Prog, res.Opts) if err == nil { @@ -570,10 +606,28 @@ func (mgr *Manager) saveRepro(crash *Crash, res *repro.Result) { cprog = formatted } ioutil.WriteFile(filepath.Join(dir, "repro.cprog"), cprog, 0660) + cprogText = cprog } else { Logf(0, "failed to write C source: %v", err) } } + + if mgr.dash != nil { + dr := &dashboard.Repro{ + Crash: dashboard.Crash{ + Tag: mgr.cfg.Tag, + Desc: crash.desc, + Report: crash.text, + }, + Reproduced: true, + Opts: fmt.Sprintf("%+v", res.Opts), + Prog: res.Prog.Serialize(), + CProg: cprogText, + } + if err := mgr.dash.ReportRepro(dr); err != nil { + Logf(0, "failed to report repro to dashboard: %v", err) + } + } } func (mgr *Manager) minimizeCorpus() { |
