diff options
Diffstat (limited to 'syz-manager/manager.go')
| -rw-r--r-- | syz-manager/manager.go | 168 |
1 files changed, 152 insertions, 16 deletions
diff --git a/syz-manager/manager.go b/syz-manager/manager.go index 95d4e47e2..34de9b54c 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -5,7 +5,6 @@ package main import ( "bytes" - "encoding/hex" "flag" "fmt" "io/ioutil" @@ -23,6 +22,7 @@ import ( "github.com/google/syzkaller/config" "github.com/google/syzkaller/cover" + "github.com/google/syzkaller/hash" . "github.com/google/syzkaller/log" "github.com/google/syzkaller/prog" "github.com/google/syzkaller/report" @@ -50,9 +50,12 @@ type Manager struct { firstConnect time.Time stats map[string]uint64 shutdown uint32 + vmChecked bool + fresh bool mu sync.Mutex enabledSyscalls string + enabledCalls []string // as determined by fuzzer suppressions []*regexp.Regexp candidates [][]byte // untriaged inputs @@ -61,12 +64,14 @@ type Manager struct { corpusCover []cover.Cover prios [][]float32 - fuzzers map[string]*Fuzzer + fuzzers map[string]*Fuzzer + hub *rpc.Client + hubCorpus map[hash.Sig]bool } type Fuzzer struct { - name string - input int + name string + inputs []RpcInput } func main() { @@ -107,10 +112,12 @@ func RunManager(cfg *config.Config, syscalls map[int]bool, suppressions []*regex suppressions: suppressions, corpusCover: make([]cover.Cover, sys.CallCount), fuzzers: make(map[string]*Fuzzer), + fresh: true, } Logf(0, "loading corpus...") mgr.persistentCorpus = newPersistentSet(filepath.Join(cfg.Workdir, "corpus"), func(data []byte) bool { + mgr.fresh = false if _, err := prog.Deserialize(data); err != nil { Logf(0, "deleting broken program: %v\n%s", err, data) return false @@ -133,8 +140,10 @@ func RunManager(cfg *config.Config, syscalls map[int]bool, suppressions []*regex // This program contains a disabled syscall. // We won't execute it, but remeber its hash so // it is not deleted during minimization. - h := hash(data) - mgr.disabledHashes = append(mgr.disabledHashes, hex.EncodeToString(h[:])) + // TODO: use mgr.enabledCalls which accounts for missing devices, etc. + // But it is available only after vm check. + sig := hash.Hash(data) + mgr.disabledHashes = append(mgr.disabledHashes, sig.String()) continue } mgr.candidates = append(mgr.candidates, data) @@ -202,6 +211,15 @@ func RunManager(cfg *config.Config, syscalls map[int]bool, suppressions []*regex } }() + if mgr.cfg.Hub_Addr != "" { + go func() { + for { + time.Sleep(time.Minute) + mgr.hubSync() + } + }() + } + go func() { c := make(chan os.Signal, 2) signal.Notify(c, syscall.SIGINT) @@ -293,8 +311,8 @@ func (mgr *Manager) saveCrasher(vmCfg *vm.Config, desc string, text, output []by mgr.stats["crashes"]++ mgr.mu.Unlock() - h := hash([]byte(desc)) - id := hex.EncodeToString(h[:]) + sig := hash.Hash([]byte(desc)) + id := sig.String() dir := filepath.Join(mgr.crashdir, id) os.MkdirAll(dir, 0700) if err := ioutil.WriteFile(filepath.Join(dir, "description"), []byte(desc+"\n"), 0660); err != nil { @@ -369,8 +387,8 @@ func (mgr *Manager) minimizeCorpus() { if len(mgr.candidates) == 0 { hashes := make(map[string]bool) for _, inp := range mgr.corpus { - h := hash(inp.Prog) - hashes[hex.EncodeToString(h[:])] = true + sig := hash.Hash(inp.Prog) + hashes[sig.String()] = true } for _, h := range mgr.disabledHashes { hashes[h] = true @@ -389,22 +407,50 @@ func (mgr *Manager) Connect(a *ConnectArgs, r *ConnectRes) error { } mgr.stats["vm restarts"]++ + f := &Fuzzer{ + name: a.Name, + } + mgr.fuzzers[a.Name] = f mgr.minimizeCorpus() - mgr.fuzzers[a.Name] = &Fuzzer{ - name: a.Name, - input: 0, + for _, inp := range mgr.corpus { + f.inputs = append(f.inputs, inp) } r.Prios = mgr.prios r.EnabledCalls = mgr.enabledSyscalls + r.NeedCheck = !mgr.vmChecked return nil } +func (mgr *Manager) Check(a *CheckArgs, r *int) error { + mgr.mu.Lock() + defer mgr.mu.Unlock() + + if mgr.vmChecked { + return nil + } + Logf(1, "fuzzer %v vm check: %v calls enabled", a.Name, len(a.Calls)) + if len(a.Calls) == 0 { + Fatalf("no system calls enabled") + } + if mgr.cfg.Cover && !a.Kcov { + Fatalf("/sys/kernel/debug/kcov is missing. Enable CONFIG_KCOV and mount debugfs") + } + mgr.vmChecked = true + mgr.enabledCalls = a.Calls + return nil +} + func (mgr *Manager) NewInput(a *NewInputArgs, r *int) error { Logf(2, "new input from %v for syscall %v", a.Name, a.Call) mgr.mu.Lock() defer mgr.mu.Unlock() + f := mgr.fuzzers[a.Name] + if f == nil { + Fatalf("fuzzer %v is not connected", a.Name) + } + call := sys.CallID[a.Call] if len(cover.Difference(a.Cover, mgr.corpusCover[call])) == 0 { return nil @@ -413,6 +459,12 @@ func (mgr *Manager) NewInput(a *NewInputArgs, r *int) error { mgr.corpus = append(mgr.corpus, a.RpcInput) mgr.stats["manager new inputs"]++ mgr.persistentCorpus.add(a.RpcInput.Prog) + for _, f1 := range mgr.fuzzers { + if f1 == f { + continue + } + f1.inputs = append(f1.inputs, a.RpcInput) + } return nil } @@ -430,9 +482,13 @@ func (mgr *Manager) Poll(a *PollArgs, r *PollRes) error { 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 < 100 && len(f.inputs) > 0; i++ { + last := len(f.inputs) - 1 + r.NewInputs = append(r.NewInputs, f.inputs[last]) + f.inputs = f.inputs[:last] + } + if len(f.inputs) == 0 { + f.inputs = nil } for i := 0; i < 10 && len(mgr.candidates) > 0; i++ { @@ -446,3 +502,83 @@ func (mgr *Manager) Poll(a *PollArgs, r *PollRes) error { return nil } + +func (mgr *Manager) hubSync() { + mgr.mu.Lock() + defer mgr.mu.Unlock() + if !mgr.vmChecked || len(mgr.candidates) != 0 { + return + } + + mgr.minimizeCorpus() + if mgr.hub == nil { + conn, err := rpc.Dial("tcp", mgr.cfg.Hub_Addr) + if err != nil { + Logf(0, "failed to connect to hub at %v: %v", mgr.cfg.Hub_Addr, err) + return + } + mgr.hub = conn + a := &HubConnectArgs{ + Name: mgr.cfg.Name, + Key: mgr.cfg.Hub_Key, + Fresh: mgr.fresh, + Calls: mgr.enabledCalls, + } + mgr.hubCorpus = make(map[hash.Sig]bool) + for _, inp := range mgr.corpus { + mgr.hubCorpus[hash.Hash(inp.Prog)] = true + a.Corpus = append(a.Corpus, inp.Prog) + } + if err := mgr.hub.Call("Hub.Connect", a, nil); err != nil { + Logf(0, "Hub.Connect rpc failed: %v", err) + mgr.hub.Close() + mgr.hub = nil + return + } + mgr.fresh = false + Logf(0, "connected to hub at %v, corpus %v", mgr.cfg.Hub_Addr, len(mgr.corpus)) + } + + a := &HubSyncArgs{ + Name: mgr.cfg.Name, + Key: mgr.cfg.Hub_Key, + } + corpus := make(map[hash.Sig]bool) + for _, inp := range mgr.corpus { + sig := hash.Hash(inp.Prog) + corpus[sig] = true + if mgr.hubCorpus[sig] { + continue + } + mgr.hubCorpus[sig] = true + a.Add = append(a.Add, inp.Prog) + } + for sig := range mgr.hubCorpus { + if corpus[sig] { + continue + } + delete(mgr.hubCorpus, sig) + a.Del = append(a.Del, sig.String()) + } + r := new(HubSyncRes) + if err := mgr.hub.Call("Hub.Sync", a, r); err != nil { + Logf(0, "Hub.Sync rpc failed: %v", err) + mgr.hub.Close() + mgr.hub = nil + return + } + dropped := 0 + for _, inp := range r.Inputs { + _, err := prog.Deserialize(inp) + if err != nil { + dropped++ + continue + } + mgr.candidates = append(mgr.candidates, inp) + } + mgr.stats["hub add"] += uint64(len(a.Add)) + mgr.stats["hub del"] += uint64(len(a.Del)) + mgr.stats["hub drop"] += uint64(dropped) + mgr.stats["hub new"] += uint64(len(r.Inputs) - dropped) + Logf(0, "hub sync: add %v, del %v, drop %v, new %v", len(a.Add), len(a.Del), dropped, len(r.Inputs)-dropped) +} |
