diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2015-12-17 16:06:33 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2015-12-17 16:06:33 +0100 |
| commit | 8e7ca7c5ff18e17cab7b6b3ae569565224f95fcc (patch) | |
| tree | 613bbb3f12f7f2f63ad225648f6971a1393d3703 /manager | |
| parent | 06e67265374faa677dba2dbd2577054278f19823 (diff) | |
remove master and naming overhaul
Remove master process entirely, it is not useful in its current form.
We first need to understand what we want from it, and them re-implement it.
Prefix all binaries with syz- to avoid name clashes.
Diffstat (limited to 'manager')
| -rw-r--r-- | manager/cover.go | 288 | ||||
| -rw-r--r-- | manager/example.cfg | 25 | ||||
| -rw-r--r-- | manager/html.go | 300 | ||||
| -rw-r--r-- | manager/main.go | 197 | ||||
| -rw-r--r-- | manager/manager.go | 252 |
5 files changed, 0 insertions, 1062 deletions
diff --git a/manager/cover.go b/manager/cover.go deleted file mode 100644 index 190fb6038..000000000 --- a/manager/cover.go +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright 2015 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 ( - "bufio" - "bytes" - "fmt" - "html/template" - "io" - "io/ioutil" - "os/exec" - "sort" - "strconv" - "strings" - "sync" - - "github.com/google/syzkaller/cover" -) - -type LineInfo struct { - file string - line int -} - -var ( - mu sync.Mutex - pcLines = make(map[uint32][]LineInfo) - parsedFiles = make(map[string][][]byte) - htmlReplacer = strings.NewReplacer(">", ">", "<", "<", "&", "&", "\t", " ") - sourcePrefix string -) - -func generateCoverHtml(w io.Writer, vmlinux string, cov []uint32) error { - mu.Lock() - defer mu.Unlock() - - info, err := covToLineInfo(vmlinux, cov) - if err != nil { - return err - } - files := fileSet(info) - for f := range files { - if _, ok := parsedFiles[f]; ok { - continue - } - if err := parseFile(f); err != nil { - return err - } - } - - var d templateData - for f, covered := range files { - lines := parsedFiles[f] - coverage := len(covered) - var buf bytes.Buffer - for i, ln := range lines { - if len(covered) > 0 && covered[0] == i+1 { - buf.Write([]byte("<span id='covered'>")) - buf.Write(ln) - buf.Write([]byte("</span>\n")) - covered = covered[1:] - } else { - buf.Write(ln) - buf.Write([]byte("\n")) - } - } - stripped := f - if len(stripped) > len(sourcePrefix) { - stripped = stripped[len(sourcePrefix):] - } - d.Files = append(d.Files, &templateFile{ - Name: stripped, - Body: template.HTML(buf.String()), - Coverage: coverage, - }) - } - - sort.Sort(templateFileArray(d.Files)) - if err := coverTemplate.Execute(w, d); err != nil { - return err - } - return nil -} - -func covToLineInfo(vmlinux string, cov []uint32) ([]LineInfo, error) { - var missing []uint32 - for _, pc := range cov { - if _, ok := pcLines[pc]; !ok { - missing = append(missing, pc) - } - } - if len(missing) > 0 { - if err := symbolize(vmlinux, missing); err != nil { - return nil, err - } - } - var info []LineInfo - for _, pc := range cov { - info = append(info, pcLines[pc]...) - } - return info, nil -} - -func fileSet(info []LineInfo) map[string][]int { - files := make(map[string]map[int]struct{}) - for _, li := range info { - if files[li.file] == nil { - files[li.file] = make(map[int]struct{}) - } - files[li.file][li.line] = struct{}{} - } - res := make(map[string][]int) - for f, lines := range files { - sorted := make([]int, 0, len(lines)) - for ln := range lines { - sorted = append(sorted, ln) - } - sort.Ints(sorted) - res[f] = sorted - } - return res -} - -func parseFile(fn string) error { - data, err := ioutil.ReadFile(fn) - if err != nil { - return err - } - var lines [][]byte - for { - idx := bytes.IndexByte(data, '\n') - if idx == -1 { - break - } - lines = append(lines, []byte(htmlReplacer.Replace(string(data[:idx])))) - data = data[idx+1:] - } - if len(data) != 0 { - lines = append(lines, data) - } - parsedFiles[fn] = lines - if sourcePrefix == "" { - sourcePrefix = fn - } else { - i := 0 - for ; i < len(sourcePrefix) && i < len(fn); i++ { - if sourcePrefix[i] != fn[i] { - break - } - } - sourcePrefix = sourcePrefix[:i] - } - return nil -} - -func symbolize(vmlinux string, cov []uint32) error { - cmd := exec.Command("addr2line", "-a", "-i", "-e", vmlinux) - stdin, err := cmd.StdinPipe() - if err != nil { - return err - } - defer stdin.Close() - stdout, err := cmd.StdoutPipe() - if err != nil { - return err - } - defer stdout.Close() - if err := cmd.Start(); err != nil { - return err - } - defer cmd.Wait() - go func() { - for _, pc := range cov { - fmt.Fprintf(stdin, "0x%x\n", cover.RestorePC(pc)-1) - } - stdin.Close() - }() - s := bufio.NewScanner(stdout) - var pc uint32 - for s.Scan() { - ln := s.Text() - if len(ln) > 3 && ln[0] == '0' && ln[1] == 'x' { - v, err := strconv.ParseUint(ln, 0, 64) - if err != nil { - return fmt.Errorf("failed to parse pc in addr2line output: %v", err) - } - pc = uint32(v) + 1 - continue - } - colon := strings.IndexByte(ln, ':') - if colon == -1 { - continue - } - file := ln[:colon] - line, err := strconv.Atoi(ln[colon+1:]) - if err != nil || pc == 0 || file == "" || file == "??" || line <= 0 { - continue - } - pcLines[pc] = append(pcLines[pc], LineInfo{file, line}) - } - if err := s.Err(); err != nil { - return err - } - return nil -} - -type templateData struct { - Files []*templateFile -} - -type templateFile struct { - Name string - Body template.HTML - Coverage int -} - -type templateFileArray []*templateFile - -func (a templateFileArray) Len() int { return len(a) } -func (a templateFileArray) Less(i, j int) bool { return a[i].Name < a[j].Name } -func (a templateFileArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -var coverTemplate = template.Must(template.New("").Parse( - ` -<!DOCTYPE html> -<html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - <style> - body { - background: white; - } - #topbar { - background: black; - position: fixed; - top: 0; left: 0; right: 0; - height: 42px; - border-bottom: 1px solid rgb(70, 70, 70); - } - #nav { - float: left; - margin-left: 10px; - margin-top: 10px; - } - #content { - font-family: 'Courier New', Courier, monospace; - color: rgb(70, 70, 70); - margin-top: 50px; - } - #covered { - color: rgb(0, 0, 0); - font-weight: bold; - } - </style> - </head> - <body> - <div id="topbar"> - <div id="nav"> - <select id="files"> - {{range $i, $f := .Files}} - <option value="file{{$i}}">{{$f.Name}} ({{$f.Coverage}})</option> - {{end}} - </select> - </div> - </div> - <div id="content"> - {{range $i, $f := .Files}} - <pre class="file" id="file{{$i}}" {{if $i}}style="display: none"{{end}}>{{$f.Body}}</pre> - {{end}} - </div> - </body> - <script> - (function() { - var files = document.getElementById('files'); - var visible = document.getElementById('file0'); - files.addEventListener('change', onChange, false); - function onChange() { - visible.style.display = 'none'; - visible = document.getElementById(files.value); - visible.style.display = 'block'; - window.scrollTo(0, 0); - } - })(); - </script> -</html> -`)) diff --git a/manager/example.cfg b/manager/example.cfg deleted file mode 100644 index 216e09eeb..000000000 --- a/manager/example.cfg +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "my-qemu-asan", - "http": "myhost.com:56741", - "master": "myhost.com:48342", - "workdir": "/syzkaller/manager/workdir", - "vmlinux": "/linux/vmlinux", - "type": "qemu", - "count": 16, - "port": 23504, - "params": { - "kernel": "/linux/arch/x86/boot/bzImage", - "image": "/linux_image/wheezy.img", - "sshkey": "/linux_image/ssh/id_rsa", - "fuzzer": "/syzkaller/fuzzer/fuzzer", - "executor": "/syzkaller/executor/executor", - "port": 23505, - "cpu": 2, - "mem": 2048 - }, - "disable_syscalls": [ - "keyctl", - "add_key", - "request_key" - ] -} diff --git a/manager/html.go b/manager/html.go deleted file mode 100644 index 6123160d9..000000000 --- a/manager/html.go +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright 2015 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 ( - "encoding/hex" - "encoding/json" - "fmt" - "html/template" - "net/http" - "sort" - "strconv" - "time" - - "github.com/google/syzkaller/cover" - "github.com/google/syzkaller/prog" - "github.com/google/syzkaller/sys" -) - -func (mgr *Manager) initHttp() { - http.HandleFunc("/", mgr.httpInfo) - http.HandleFunc("/corpus", mgr.httpCorpus) - http.HandleFunc("/cover", mgr.httpCover) - http.HandleFunc("/prio", mgr.httpPrio) - http.HandleFunc("/current_corpus", mgr.httpCurrentCorpus) - go func() { - logf(0, "serving http on http://%v", mgr.cfg.Http) - panic(http.ListenAndServe(mgr.cfg.Http, nil)) - }() -} - -func (mgr *Manager) httpInfo(w http.ResponseWriter, r *http.Request) { - mgr.mu.Lock() - defer mgr.mu.Unlock() - - type CallCov struct { - count int - cov cover.Cover - } - calls := make(map[string]*CallCov) - for _, inp := range mgr.corpus { - if calls[inp.Call] == nil { - calls[inp.Call] = new(CallCov) - } - cc := calls[inp.Call] - cc.count++ - cc.cov = cover.Union(cc.cov, cover.Cover(inp.Cover)) - } - - uptime := time.Since(mgr.startTime) - data := &UIData{ - Name: mgr.cfg.Name, - MasterHttp: mgr.masterHttp, - MasterCorpusSize: len(mgr.masterCorpus), - CorpusSize: len(mgr.corpus), - TriageQueue: len(mgr.candidates), - Uptime: fmt.Sprintf("%v", uptime), - } - - secs := uint64(uptime) / 1e9 - for k, v := range mgr.stats { - val := "" - if x := v / secs; x >= 10 { - val = fmt.Sprintf("%v/sec", x) - } else if x := v * 60 / secs; x >= 10 { - val = fmt.Sprintf("%v/min", x) - } else { - x := v * 60 * 60 / secs - val = fmt.Sprintf("%v/hour", x) - } - data.Stats = append(data.Stats, UIStat{Name: k, Value: val}) - } - sort.Sort(UIStatArray(data.Stats)) - - var cov cover.Cover - for c, cc := range calls { - cov = cover.Union(cov, cc.cov) - data.Calls = append(data.Calls, UICallType{c, cc.count, len(cc.cov)}) - } - sort.Sort(UICallTypeArray(data.Calls)) - data.CoverSize = len(cov) - - if err := htmlTemplate.Execute(w, data); err != nil { - http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) - } -} - -func (mgr *Manager) httpCorpus(w http.ResponseWriter, r *http.Request) { - mgr.mu.Lock() - defer mgr.mu.Unlock() - - var data []UIInput - call := r.FormValue("call") - for i, inp := range mgr.corpus { - if call != inp.Call { - continue - } - p, err := prog.Deserialize(inp.Prog) - if err != nil { - http.Error(w, fmt.Sprintf("failed to deserialize program: %v", err), http.StatusInternalServerError) - } - data = append(data, UIInput{ - Short: p.String(), - Full: string(inp.Prog), - Cover: len(inp.Cover), - N: i, - }) - } - sort.Sort(UIInputArray(data)) - - if err := corpusTemplate.Execute(w, data); err != nil { - http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) - } -} - -func (mgr *Manager) httpCover(w http.ResponseWriter, r *http.Request) { - mgr.mu.Lock() - defer mgr.mu.Unlock() - - var cov cover.Cover - call := r.FormValue("call") - if n, err := strconv.Atoi(call); err == nil && n < len(mgr.corpus) { - cov = mgr.corpus[n].Cover - } else { - for _, inp := range mgr.corpus { - if call == "" || call == inp.Call { - cov = cover.Union(cov, cover.Cover(inp.Cover)) - } - } - } - - if err := generateCoverHtml(w, mgr.cfg.Vmlinux, cov); err != nil { - http.Error(w, fmt.Sprintf("failed to generate coverage profile: %v", err), http.StatusInternalServerError) - } -} - -func (mgr *Manager) httpPrio(w http.ResponseWriter, r *http.Request) { - mgr.mu.Lock() - defer mgr.mu.Unlock() - - mgr.minimizeCorpus() - call := r.FormValue("call") - idx := -1 - for i, c := range sys.Calls { - if c.CallName == call { - idx = i - break - } - } - if idx == -1 { - http.Error(w, fmt.Sprintf("unknown call: %v", call), http.StatusInternalServerError) - return - } - - data := &UIPrioData{Call: call} - for i, p := range mgr.prios[idx] { - data.Prios = append(data.Prios, UIPrio{sys.Calls[i].Name, p}) - } - sort.Sort(UIPrioArray(data.Prios)) - - if err := prioTemplate.Execute(w, data); err != nil { - http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) - } -} - -func (mgr *Manager) httpCurrentCorpus(w http.ResponseWriter, r *http.Request) { - mgr.mu.Lock() - defer mgr.mu.Unlock() - - mgr.minimizeCorpus() - var hashes []string - for _, inp := range mgr.corpus { - hash := hash(inp.Prog) - hashes = append(hashes, hex.EncodeToString(hash[:])) - } - data, err := json.Marshal(&hashes) - if err != nil { - http.Error(w, fmt.Sprintf("failed to marshal corpus: %v", err), http.StatusInternalServerError) - return - } - w.Write(data) -} - -type UIData struct { - Name string - MasterHttp string - MasterCorpusSize int - CorpusSize int - TriageQueue int - CoverSize int - Uptime string - Stats []UIStat - Calls []UICallType -} - -type UIStat struct { - Name string - Value string -} - -type UICallType struct { - Name string - Inputs int - Cover int -} - -type UIInput struct { - Short string - Full string - Calls int - Cover int - N int -} - -type UICallTypeArray []UICallType - -func (a UICallTypeArray) Len() int { return len(a) } -func (a UICallTypeArray) Less(i, j int) bool { return a[i].Name < a[j].Name } -func (a UICallTypeArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -type UIInputArray []UIInput - -func (a UIInputArray) Len() int { return len(a) } -func (a UIInputArray) Less(i, j int) bool { return a[i].Cover > a[j].Cover } -func (a UIInputArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -type UIStatArray []UIStat - -func (a UIStatArray) Len() int { return len(a) } -func (a UIStatArray) Less(i, j int) bool { return a[i].Name < a[j].Name } -func (a UIStatArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -var htmlTemplate = template.Must(template.New("").Parse(` -<!doctype html> -<html> -<head> - <title>syzkaller {{.Name}}</title> -</head> -<body> -Manager: {{.Name}} <a href='http://{{.MasterHttp}}'>[master]</a> <br> -Uptime: {{.Uptime}}<br> -Master corpus: {{.MasterCorpusSize}} <br> -Corpus: {{.CorpusSize}}<br> -Triage queue len: {{.TriageQueue}}<br> -<a href='/cover'>Cover: {{.CoverSize}}</a> <br> -<br> -Stats: <br> -{{range $stat := $.Stats}} - {{$stat.Name}}: {{$stat.Value}}<br> -{{end}} -<br> -{{range $c := $.Calls}} - {{$c.Name}} <a href='/corpus?call={{$c.Name}}'>inputs:{{$c.Inputs}}</a> <a href='/cover?call={{$c.Name}}'>cover:{{$c.Cover}}</a> <a href='/prio?call={{$c.Name}}'>prio</a> <br> -{{end}} -</body></html> -`)) - -var corpusTemplate = template.Must(template.New("").Parse(` -<!doctype html> -<html> -<head> - <title>syzkaller corpus</title> -</head> -<body> -{{range $c := $}} - <span title="{{$c.Full}}">{{$c.Short}}</span> <a href='/cover?call={{$c.N}}'>cover:{{$c.Cover}}</a> <br> -{{end}} -</body></html> -`)) - -type UIPrioData struct { - Call string - Prios []UIPrio -} - -type UIPrio struct { - Call string - Prio float32 -} - -type UIPrioArray []UIPrio - -func (a UIPrioArray) Len() int { return len(a) } -func (a UIPrioArray) Less(i, j int) bool { return a[i].Prio > a[j].Prio } -func (a UIPrioArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -var prioTemplate = template.Must(template.New("").Parse(` -<!doctype html> -<html> -<head> - <title>syzkaller priorities</title> -</head> -<body> -Priorities for {{$.Call}} <br> <br> -{{range $p := $.Prios}} - {{printf "%.4f\t%s" $p.Prio $p.Call}} <br> -{{end}} -</body></html> -`)) diff --git a/manager/main.go b/manager/main.go deleted file mode 100644 index 9a3523cb8..000000000 --- a/manager/main.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2015 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 ( - "bytes" - "encoding/json" - "flag" - "fmt" - "io/ioutil" - "log" - "regexp" - "strings" - - "github.com/google/syzkaller/sys" - "github.com/google/syzkaller/vm" - _ "github.com/google/syzkaller/vm/kvm" - _ "github.com/google/syzkaller/vm/local" - _ "github.com/google/syzkaller/vm/qemu" -) - -var ( - flagConfig = flag.String("config", "", "configuration file") - flagV = flag.Int("v", 0, "verbosity") -) - -type Config struct { - Name string - Http string - Master string - Workdir string - Vmlinux string - Type string - Count int // number of VMs - Procs int // number of parallel processes inside of every VM - Port int - Nocover bool - NoDropPrivs bool - Leak bool // do memory leak checking - Params map[string]interface{} - Enable_Syscalls []string - Disable_Syscalls []string - Suppressions []string -} - -func main() { - flag.Parse() - cfg, syscalls := parseConfig() - params, err := json.Marshal(cfg.Params) - if err != nil { - fatalf("failed to marshal config params: %v", err) - } - enabledSyscalls := "" - if len(syscalls) != 0 { - buf := new(bytes.Buffer) - for c := range syscalls { - fmt.Fprintf(buf, ",%v", c) - } - enabledSyscalls = buf.String()[1:] - logf(1, "enabled syscalls: %v", enabledSyscalls) - } - vmCfg := &vm.Config{ - Workdir: cfg.Workdir, - ManagerPort: cfg.Port, - Params: params, - EnabledSyscalls: enabledSyscalls, - NoCover: cfg.Nocover, - NoDropPrivs: cfg.NoDropPrivs, - Leak: cfg.Leak, - Procs: cfg.Procs, - } - - // Add some builtin suppressions. - cfg.Suppressions = append(cfg.Suppressions, []string{ - "panic: failed to start executor binary", - "panic: executor failed: pthread_create failed", - "panic: failed to create temp dir", - "Out of memory: Kill process .* \\(syzkaller_fuzze\\)", - "WARNING: KASAN doesn't support memory hot-add", - }...) - for _, s := range cfg.Suppressions { - re, err := regexp.Compile(s) - if err != nil { - fatalf("failed to compile suppression '%v': %v", s, err) - } - vmCfg.Suppressions = append(vmCfg.Suppressions, re) - } - - var instances []vm.Instance - for i := 0; i < cfg.Count; i++ { - inst, err := vm.Create(cfg.Type, vmCfg, i) - if err != nil { - fatalf("failed to create an instance: %v", err) - } - instances = append(instances, inst) - } - RunManager(cfg, syscalls, instances) -} - -func parseConfig() (*Config, map[int]bool) { - if *flagConfig == "" { - fatalf("supply config file name in -config flag") - } - data, err := ioutil.ReadFile(*flagConfig) - if err != nil { - fatalf("failed to read config file: %v", err) - } - cfg := new(Config) - if err := json.Unmarshal(data, cfg); err != nil { - fatalf("failed to parse config file: %v", err) - } - if cfg.Name == "" { - fatalf("config param name is empty") - } - if cfg.Http == "" { - fatalf("config param http is empty") - } - if cfg.Master == "" { - fatalf("config param master is empty") - } - if cfg.Workdir == "" { - fatalf("config param workdir is empty") - } - if cfg.Vmlinux == "" { - fatalf("config param vmlinux is empty") - } - if cfg.Type == "" { - fatalf("config param type is empty") - } - if cfg.Count <= 0 || cfg.Count > 1000 { - fatalf("invalid config param count: %v, want (1, 1000]", cfg.Count) - } - if cfg.Procs <= 0 { - cfg.Procs = 1 - } - - match := func(call *sys.Call, str string) bool { - if str == call.CallName || str == call.Name { - return true - } - if len(str) > 1 && str[len(str)-1] == '*' && strings.HasPrefix(call.Name, str[:len(str)-1]) { - return true - } - return false - } - - var syscalls map[int]bool - if len(cfg.Enable_Syscalls) != 0 || len(cfg.Disable_Syscalls) != 0 { - syscalls = make(map[int]bool) - if len(cfg.Enable_Syscalls) != 0 { - for _, c := range cfg.Enable_Syscalls { - n := 0 - for _, call := range sys.Calls { - if match(call, c) { - syscalls[call.ID] = true - n++ - } - } - if n == 0 { - fatalf("unknown enabled syscall: %v", c) - } - } - } else { - for _, call := range sys.Calls { - syscalls[call.ID] = true - } - } - for _, c := range cfg.Disable_Syscalls { - n := 0 - for _, call := range sys.Calls { - if match(call, c) { - delete(syscalls, call.ID) - n++ - } - } - if n == 0 { - fatalf("unknown disabled syscall: %v", c) - } - } - // They will be generated anyway. - syscalls[sys.CallMap["mmap"].ID] = true - syscalls[sys.CallMap["clock_gettime"].ID] = true - } - - return cfg, syscalls -} - -func logf(v int, msg string, args ...interface{}) { - if *flagV >= v { - log.Printf(msg, args...) - } -} - -func fatalf(msg string, args ...interface{}) { - log.Fatalf(msg, args...) -} diff --git a/manager/manager.go b/manager/manager.go deleted file mode 100644 index ab40bbe0d..000000000 --- a/manager/manager.go +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2015 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 ( - "crypto/sha1" - "fmt" - "net" - "net/rpc" - "sync" - "time" - - "github.com/google/syzkaller/cover" - "github.com/google/syzkaller/prog" - . "github.com/google/syzkaller/rpctype" - "github.com/google/syzkaller/sys" - "github.com/google/syzkaller/vm" -) - -type Sig [sha1.Size]byte - -func hash(data []byte) Sig { - return Sig(sha1.Sum(data)) -} - -type Manager struct { - cfg *Config - master *rpc.Client - masterHttp string - instances []vm.Instance - startTime time.Time - stats map[string]uint64 - - mu sync.Mutex - masterCorpus [][]byte // mirror of master corpus - masterHashes map[Sig]struct{} // hashes of master corpus - candidates [][]byte // new untriaged inputs from master - syscalls map[int]bool - - corpus []RpcInput - corpusCover []cover.Cover - prios [][]float32 - - fuzzers map[string]*Fuzzer -} - -type Fuzzer struct { - name string - input int -} - -func RunManager(cfg *Config, syscalls map[int]bool, instances []vm.Instance) { - // Connect to master. - master, err := rpc.Dial("tcp", cfg.Master) - if err != nil { - fatalf("failed to dial mastger: %v", err) - } - a := &MasterConnectArgs{cfg.Name, cfg.Http} - r := &MasterConnectRes{} - if err := master.Call("Master.Connect", a, r); err != nil { - fatalf("failed to connect to master: %v", err) - } - logf(0, "connected to master at %v", cfg.Master) - - mgr := &Manager{ - cfg: cfg, - master: master, - masterHttp: r.Http, - startTime: time.Now(), - stats: make(map[string]uint64), - instances: instances, - masterHashes: make(map[Sig]struct{}), - syscalls: syscalls, - corpusCover: make([]cover.Cover, sys.CallCount), - fuzzers: make(map[string]*Fuzzer), - } - - // Create HTTP server. - mgr.initHttp() - - // Create RPC server for fuzzers. - rpcAddr := fmt.Sprintf("localhost:%v", cfg.Port) - ln, err := net.Listen("tcp", rpcAddr) - if err != nil { - fatalf("failed to listen on port %v: %v", cfg.Port, err) - } - s := rpc.NewServer() - s.Register(mgr) - go s.Accept(ln) - logf(0, "serving rpc on tcp://%v", rpcAddr) - - mgr.run() -} - -func (mgr *Manager) run() { - mgr.pollMaster() - for _, inst := range mgr.instances { - go inst.Run() - } - pollTicker := time.NewTicker(10 * time.Second).C - for { - select { - case <-pollTicker: - mgr.mu.Lock() - mgr.pollMaster() - mgr.mu.Unlock() - } - } -} - -func (mgr *Manager) pollMaster() { - for { - a := &MasterPollArgs{mgr.cfg.Name} - r := &MasterPollRes{} - if err := mgr.master.Call("Master.PollInputs", a, r); err != nil { - fatalf("failed to poll master: %v", err) - } - logf(3, "polling master, got %v inputs", len(r.Inputs)) - if len(r.Inputs) == 0 { - break - } - nextProg: - for _, prg := range r.Inputs { - p, err := prog.Deserialize(prg) - if err != nil { - logf(0, "failed to deserialize master program: %v", err) - continue - } - if mgr.syscalls != nil { - for _, c := range p.Calls { - if !mgr.syscalls[c.Meta.ID] { - continue nextProg - } - } - } - sig := hash(prg) - if _, ok := mgr.masterHashes[sig]; ok { - continue - } - mgr.masterHashes[sig] = struct{}{} - mgr.masterCorpus = append(mgr.masterCorpus, prg) - mgr.candidates = append(mgr.candidates, prg) - } - } -} - -func (mgr *Manager) minimizeCorpus() { - if !mgr.cfg.Nocover && len(mgr.corpus) != 0 { - // First, sort corpus per call. - type Call struct { - inputs []RpcInput - cov []cover.Cover - } - calls := make(map[string]Call) - for _, inp := range mgr.corpus { - c := calls[inp.Call] - c.inputs = append(c.inputs, inp) - c.cov = append(c.cov, inp.Cover) - calls[inp.Call] = c - } - // Now minimize and build new corpus. - var newCorpus []RpcInput - for _, c := range calls { - for _, idx := range cover.Minimize(c.cov) { - newCorpus = append(newCorpus, c.inputs[idx]) - } - } - logf(1, "minimized corpus: %v -> %v", len(mgr.corpus), len(newCorpus)) - mgr.corpus = newCorpus - } - var corpus []*prog.Prog - for _, inp := range mgr.corpus { - p, err := prog.Deserialize(inp.Prog) - if err != nil { - panic(err) - } - corpus = append(corpus, p) - } - mgr.prios = prog.CalculatePriorities(corpus) -} - -func (mgr *Manager) Connect(a *ManagerConnectArgs, r *ManagerConnectRes) error { - logf(1, "fuzzer %v connected", a.Name) - mgr.mu.Lock() - defer mgr.mu.Unlock() - - mgr.stats["vm restarts"]++ - mgr.minimizeCorpus() - mgr.fuzzers[a.Name] = &Fuzzer{ - name: a.Name, - input: 0, - } - r.Prios = mgr.prios - - return nil -} - -func (mgr *Manager) NewInput(a *NewManagerInputArgs, r *int) error { - logf(2, "new input from fuzzer %v", a.Name) - mgr.mu.Lock() - defer mgr.mu.Unlock() - - call := sys.CallID[a.Call] - if len(cover.Difference(a.Cover, mgr.corpusCover[call])) == 0 { - return nil - } - mgr.corpusCover[call] = cover.Union(mgr.corpusCover[call], a.Cover) - mgr.corpus = append(mgr.corpus, a.RpcInput) - mgr.stats["manager new inputs"]++ - - sig := hash(a.Prog) - if _, ok := mgr.masterHashes[sig]; !ok { - mgr.masterHashes[sig] = struct{}{} - mgr.masterCorpus = append(mgr.masterCorpus, a.Prog) - - a1 := &NewMasterInputArgs{mgr.cfg.Name, a.Prog} - if err := mgr.master.Call("Master.NewInput", a1, nil); err != nil { - fatalf("call Master.NewInput failed: %v", err) - } - } - - return nil -} - -func (mgr *Manager) Poll(a *ManagerPollArgs, r *ManagerPollRes) error { - logf(2, "poll from %v", a.Name) - mgr.mu.Lock() - defer mgr.mu.Unlock() - - for k, v := range a.Stats { - mgr.stats[k] += v - } - - f := mgr.fuzzers[a.Name] - if f == nil { - fatalf("fuzzer %v is not connected", a.Name) - } - - for i := 0; i < 100 && f.input < len(mgr.corpus); i++ { - r.NewInputs = append(r.NewInputs, mgr.corpus[f.input]) - f.input++ - } - - for i := 0; i < 10 && len(mgr.candidates) > 0; i++ { - last := len(mgr.candidates) - 1 - r.Candidates = append(r.Candidates, mgr.candidates[last]) - mgr.candidates = mgr.candidates[:last] - } - - return nil -} |
