// Copyright 2018 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 ( "time" "github.com/google/syzkaller/pkg/hash" "github.com/google/syzkaller/pkg/log" "github.com/google/syzkaller/pkg/mgrconfig" "github.com/google/syzkaller/pkg/report" "github.com/google/syzkaller/pkg/rpctype" "github.com/google/syzkaller/prog" ) func (mgr *Manager) hubSyncLoop() { hc := &HubConnector{ mgr: mgr, cfg: mgr.cfg, target: mgr.target, stats: mgr.stats, enabledCalls: mgr.checkResult.EnabledCalls[mgr.cfg.Sandbox], fresh: mgr.fresh, hubReproQueue: mgr.hubReproQueue, } if mgr.cfg.Reproduce && mgr.dash != nil { hc.needMoreRepros = mgr.needMoreRepros } hc.loop() } type HubConnector struct { mgr HubManagerView cfg *mgrconfig.Config target *prog.Target stats *Stats enabledCalls []int fresh bool hubCorpus map[hash.Sig]bool newRepros [][]byte hubReproQueue chan *Crash needMoreRepros chan chan bool } // HubManagerView restricts interface between HubConnector and Manager. type HubManagerView interface { getMinimizedCorpus() (corpus, repros [][]byte) addNewCandidates(progs [][]byte) } func (hc *HubConnector) loop() { var hub *rpctype.RPCClient for { time.Sleep(time.Minute) corpus, repros := hc.mgr.getMinimizedCorpus() hc.newRepros = append(hc.newRepros, repros...) if hub == nil { var err error if hub, err = hc.connect(corpus); err != nil { log.Logf(0, "failed to connect to hub at %v: %v", hc.cfg.HubAddr, err) continue } log.Logf(0, "connected to hub at %v, corpus %v", hc.cfg.HubAddr, len(corpus)) } if err := hc.sync(hub, corpus); err != nil { log.Logf(0, "hub sync failed: %v", err) hub.Close() hub = nil } } } func (hc *HubConnector) connect(corpus [][]byte) (*rpctype.RPCClient, error) { a := &rpctype.HubConnectArgs{ Client: hc.cfg.HubClient, Key: hc.cfg.HubKey, Manager: hc.cfg.Name, Fresh: hc.fresh, } for _, id := range hc.enabledCalls { a.Calls = append(a.Calls, hc.target.Syscalls[id].Name) } hubCorpus := make(map[hash.Sig]bool) for _, inp := range corpus { hubCorpus[hash.Hash(inp)] = true a.Corpus = append(a.Corpus, inp) } // Hub.Connect request can be very large, so do it on a transient connection // (rpc connection buffers never shrink). if err := rpctype.RPCCall(hc.cfg.HubAddr, "Hub.Connect", a, nil); err != nil { return nil, err } hub, err := rpctype.NewRPCClient(hc.cfg.HubAddr) if err != nil { return nil, err } hc.hubCorpus = hubCorpus hc.fresh = false return hub, nil } func (hc *HubConnector) sync(hub *rpctype.RPCClient, corpus [][]byte) error { a := &rpctype.HubSyncArgs{ Client: hc.cfg.HubClient, Key: hc.cfg.HubKey, Manager: hc.cfg.Name, } sigs := make(map[hash.Sig]bool) for _, inp := range corpus { sig := hash.Hash(inp) sigs[sig] = true if hc.hubCorpus[sig] { continue } hc.hubCorpus[sig] = true a.Add = append(a.Add, inp) } for sig := range hc.hubCorpus { if sigs[sig] { continue } delete(hc.hubCorpus, sig) a.Del = append(a.Del, sig.String()) } if hc.needMoreRepros != nil { needReproReply := make(chan bool) hc.needMoreRepros <- needReproReply a.NeedRepros = <-needReproReply } a.Repros = hc.newRepros for { r := new(rpctype.HubSyncRes) if err := hub.Call("Hub.Sync", a, r); err != nil { return err } progDropped := hc.processProgs(r.Progs) reproDropped := hc.processRepros(r.Repros) hc.stats.hubSendProgAdd.add(len(a.Add)) hc.stats.hubSendProgDel.add(len(a.Del)) hc.stats.hubSendRepro.add(len(a.Repros)) hc.stats.hubRecvProg.add(len(r.Progs) - progDropped) hc.stats.hubRecvProgDrop.add(progDropped) hc.stats.hubRecvRepro.add(len(r.Repros) - reproDropped) hc.stats.hubRecvReproDrop.add(reproDropped) log.Logf(0, "hub sync: send: add %v, del %v, repros %v;"+ " recv: progs %v, repros %v; more %v", len(a.Add), len(a.Del), len(a.Repros), len(r.Progs)-progDropped, len(r.Repros)-reproDropped, r.More) a.Add = nil a.Del = nil a.Repros = nil a.NeedRepros = false hc.newRepros = nil if len(r.Progs)+r.More == 0 { return nil } } } func (hc *HubConnector) processProgs(progs [][]byte) int { dropped := 0 candidates := make([][]byte, 0, len(progs)) for _, inp := range progs { if _, err := hc.target.Deserialize(inp); err != nil { dropped++ continue } candidates = append(candidates, inp) } hc.mgr.addNewCandidates(candidates) return dropped } func (hc *HubConnector) processRepros(repros [][]byte) int { dropped := 0 for _, repro := range repros { if _, err := hc.target.Deserialize(repro); err != nil { dropped++ continue } hc.hubReproQueue <- &Crash{ vmIndex: -1, hub: true, Report: &report.Report{ Title: "external repro", Output: repro, }, } } return dropped }