aboutsummaryrefslogtreecommitdiffstats
path: root/manager
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-12-17 16:06:33 +0100
committerDmitry Vyukov <dvyukov@google.com>2015-12-17 16:06:33 +0100
commit8e7ca7c5ff18e17cab7b6b3ae569565224f95fcc (patch)
tree613bbb3f12f7f2f63ad225648f6971a1393d3703 /manager
parent06e67265374faa677dba2dbd2577054278f19823 (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.go288
-rw-r--r--manager/example.cfg25
-rw-r--r--manager/html.go300
-rw-r--r--manager/main.go197
-rw-r--r--manager/manager.go252
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(">", "&gt;", "<", "&lt;", "&", "&amp;", "\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
-}