aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-02-17 18:52:07 +0100
committerDmitry Vyukov <dvyukov@google.com>2017-02-17 18:52:07 +0100
commit1466d0cb9f3cfcd088ad7e7e0c8b79b521d129ab (patch)
treef89f0c47aed7d545b9bb6230b9b89a97bf564bef
parent104126b97057a9a60c055762dfb1b29ae2b458f5 (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.go22
-rw-r--r--dashboard/dash.go92
-rw-r--r--syz-manager/manager.go56
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() {