func addInput(inp RpcInput) { corpusMu.Lock() defer corpusMu.Unlock() coverMu.Lock() defer coverMu.Unlock() if noCover { panic("should not be called when coverage is disabled") } p, err := prog.Deserialize(inp.Prog) if err != nil { panic(err) } if inp.CallIndex < 0 || inp.CallIndex >= len(p.Calls) { panic("bad call index") } call := p.Calls[inp.CallIndex].Meta sig := hash(inp.Prog) if _, ok := corpusHashes[sig]; ok { return } cov := cover.Canonicalize(inp.Cover) diff := cover.Difference(cov, maxCover[call.CallID]) diff = cover.Difference(diff, flakes) if len(diff) == 0 { return } corpus = append(corpus, p) corpusCover[call.CallID] = cover.Union(corpusCover[call.CallID], cov) maxCover[call.CallID] = cover.Union(maxCover[call.CallID], cov) corpusHashes[hash(inp.Prog)] = struct{}{} }
func addInput(inp RpcInput) { p, err := prog.Deserialize(inp.Prog) if err != nil { panic(err) } if inp.CallIndex < 0 || inp.CallIndex >= len(p.Calls) { panic("bad call index") } call := p.Calls[inp.CallIndex].Meta sig := hash(inp.Prog) if _, ok := corpusHashes[sig]; ok { return } cov := cover.Canonicalize(inp.Cover) diff := cover.Difference(cov, maxCover[call.CallID]) diff = cover.Difference(diff, flakes) if len(diff) == 0 { return } inp1 := Input{p, inp.CallIndex, cov} corpus = append(corpus, inp1) corpusCover[call.CallID] = cover.Union(corpusCover[call.CallID], cov) maxCover[call.CallID] = cover.Union(maxCover[call.CallID], cov) corpusHashes[hash(inp.Prog)] = struct{}{} }
func triageInput(env *ipc.Env, inp Input) { if *flagNoCover { panic("should not be called when coverage is disabled") } call := inp.p.Calls[inp.call].Meta newCover := cover.Difference(inp.cover, corpusCover[call.CallID]) newCover = cover.Difference(newCover, flakes) if len(newCover) == 0 { return } if _, ok := corpusHashes[hash(inp.p.Serialize())]; ok { return } minCover := inp.cover for i := 0; i < 3; i++ { allCover := execute1(env, inp.p, &statExecTriage) if len(allCover[inp.call]) == 0 { // The call was not executed. Happens sometimes, reason unknown. continue } cov := allCover[inp.call] diff := cover.SymmetricDifference(inp.cover, cov) if len(diff) != 0 { flakes = cover.Union(flakes, diff) } minCover = cover.Intersection(minCover, cov) } stableNewCover := cover.Intersection(newCover, minCover) if len(stableNewCover) == 0 { return } inp.p, inp.call = prog.Minimize(inp.p, inp.call, func(p1 *prog.Prog, call1 int) bool { allCover := execute1(env, p1, &statExecMinimize) if len(allCover[call1]) == 0 { return false // The call was not executed. } cov := allCover[call1] if len(cover.Intersection(stableNewCover, cov)) != len(stableNewCover) { return false } minCover = cover.Intersection(minCover, cov) return true }) inp.cover = minCover corpusCover[call.CallID] = cover.Union(corpusCover[call.CallID], minCover) corpus = append(corpus, inp) data := inp.p.Serialize() corpusHashes[hash(data)] = struct{}{} logf(2, "added new input for %v to corpus:\n%s", call.CallName, data) statNewInput++ a := &NewManagerInputArgs{*flagName, RpcInput{call.CallName, inp.p.Serialize(), inp.call, []uint32(inp.cover)}} if err := manager.Call("Manager.NewInput", a, nil); err != nil { panic(err) } }
func (mgr *Manager) httpInfo(w http.ResponseWriter, r *http.Request) { mgr.mu.Lock() defer mgr.mu.Unlock() uptime := time.Since(mgr.startTime) data := &UIData{ CorpusSize: len(mgr.corpus), TriageQueue: len(mgr.candidates), Uptime: fmt.Sprintf("%v", uptime), } 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)) data.CorpusCoverMem += len(inp.Cover) * int(unsafe.Sizeof(inp.Cover[0])) } for _, cov := range mgr.corpusCover { data.CallCoverMem += len(cov) * int(unsafe.Sizeof(cov[0])) } 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 execute(pid int, env *ipc.Env, p *prog.Prog, stat *uint64) { allCover := execute1(pid, env, p, stat) coverMu.RLock() defer coverMu.RUnlock() for i, cov := range allCover { if len(cov) == 0 { continue } c := p.Calls[i].Meta diff := cover.Difference(cov, maxCover[c.CallID]) diff = cover.Difference(diff, flakes) if len(diff) != 0 { coverMu.RUnlock() coverMu.Lock() maxCover[c.CallID] = cover.Union(maxCover[c.CallID], diff) coverMu.Unlock() coverMu.RLock() inp := Input{p.Clone(), i, cover.Copy(cov)} triageMu.Lock() triage = append(triage, inp) triageMu.Unlock() } } }
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) 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 } mgr.corpusCover[call] = cover.Union(mgr.corpusCover[call], a.Cover) 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 }
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") unique := r.FormValue("unique") != "" && call != "" perCall := false if n, err := strconv.Atoi(call); err == nil && n < len(mgr.corpus) { cov = mgr.corpus[n].Cover } else { perCall = true for _, inp := range mgr.corpus { if call == "" || call == inp.Call { cov = cover.Union(cov, cover.Cover(inp.Cover)) } } } if unique { cov = cover.Intersection(cov, mgr.uniqueCover(perCall)) } if err := generateCoverHtml(w, mgr.cfg.Vmlinux, cov); err != nil { http.Error(w, fmt.Sprintf("failed to generate coverage profile: %v", err), http.StatusInternalServerError) return } runtime.GC() }
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)) } data := &UIData{ Name: mgr.cfg.Name, MasterHttp: mgr.masterHttp, MasterCorpusSize: len(mgr.masterCorpus), CorpusSize: len(mgr.corpus), } 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) 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() 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"]++ mgr.persistentCorpus.add(a.RpcInput.Prog) return nil }
func main() { debug.SetGCPercent(50) flag.Parse() logf(0, "started") var calls []*sys.Call if *flagSyscalls != "" { for _, id := range strings.Split(*flagSyscalls, ",") { n, err := strconv.ParseUint(id, 10, 64) if err != nil || n >= uint64(len(sys.Calls)) { panic(fmt.Sprintf("invalid syscall in -calls flag: '%v", id)) } calls = append(calls, sys.Calls[n]) } } corpusCover = make([]cover.Cover, sys.CallCount) maxCover = make([]cover.Cover, sys.CallCount) corpusHashes = make(map[Sig]struct{}) conn, err := rpc.Dial("tcp", *flagManager) if err != nil { panic(err) } manager = conn a := &ManagerConnectArgs{*flagName} r := &ManagerConnectRes{} if err := manager.Call("Manager.Connect", a, r); err != nil { panic(err) } ct = prog.BuildChoiceTable(r.Prios, calls) if *flagParallel <= 0 { *flagParallel = 1 } flags := ipc.FlagCover | ipc.FlagDedupCover if *flagStrace { flags |= ipc.FlagStrace } workerIn = make(chan *prog.Prog, *flagParallel+10) workerOut = make(chan []Input, *flagParallel) for i := 0; i < *flagParallel; i++ { env, err := ipc.MakeEnv(*flagExecutor, 4*time.Second, flags) if err != nil { panic(err) } workerId := i + 1 go func() { for p := range workerIn { workerOut <- execute(env, p, workerId) } }() } env, err := ipc.MakeEnv(*flagExecutor, 4*time.Second, flags) if err != nil { panic(err) } rs := rand.NewSource(time.Now().UnixNano()) rnd := rand.New(rs) var lastPoll time.Time var lastPrint time.Time secondTicker := time.NewTicker(100 * time.Millisecond).C for i := 0; ; i++ { if !*flagSaveProg && time.Since(lastPrint) > 10*time.Second { // Keep-alive for manager. logf(0, "#%v: alive", i) lastPrint = time.Now() } if len(triage) != 0 { last := len(triage) - 1 inp := triage[last] triage = triage[:last] logf(1, "#%v: triaging : %s", i, inp.p) triageInput(env, inp) continue } if time.Since(lastPoll) > 10*time.Second { a := &ManagerPollArgs{*flagName} r := &ManagerPollRes{} if err := manager.Call("Manager.Poll", a, r); err != nil { panic(err) } for _, inp := range r.NewInputs { addInput(inp) } for _, data := range r.Candidates { p, err := prog.Deserialize(data) if err != nil { panic(err) } inputs := execute(env, p, 0) for _, inp := range inputs { call := inp.p.Calls[inp.call].Meta maxCover[call.CallID] = cover.Union(maxCover[call.CallID], inp.cover) triage = append(triage, inp) } } if len(r.NewInputs) == 0 && len(r.Candidates) == 0 { lastPoll = time.Now() } continue } // Parallel part. pending := 0 for ; ; i++ { if !(!*flagSaveProg && time.Since(lastPrint) > 10*time.Second) && !(len(triage) != 0) && !(time.Since(lastPoll) > 10*time.Second) { // No need to do any work above. // Send new inputs to workers, if they need some. for len(workerIn) < *flagParallel { if len(corpus) == 0 || i%10 == 0 { p := prog.Generate(rnd, programLength, ct) logf(1, "#%v: generated: %s", i, p) workerIn <- p pending++ p = p.Clone() p.Mutate(rnd, programLength, ct) logf(1, "#%v: mutated: %s", i, p) workerIn <- p pending++ } else { inp := corpus[rnd.Intn(len(corpus))] p := inp.p.Clone() p.Mutate(rs, programLength, ct) logf(1, "#%v: mutated: %s <- %s", i, p, inp.p) workerIn <- p pending++ } } } else if pending == 0 { // Need to do some work above. // Break if collected all pending results. break } // Collect results. select { case inputs := <-workerOut: pending-- for _, inp := range inputs { triage = append(triage, inp) } case <-secondTicker: } } // Do this after the parallel section because workers access maxCover. for _, inp := range triage { call := inp.p.Calls[inp.call].Meta maxCover[call.CallID] = cover.Union(maxCover[call.CallID], inp.cover) } } }
func triageInput(pid int, env *ipc.Env, inp Input) { if noCover { panic("should not be called when coverage is disabled") } call := inp.p.Calls[inp.call].Meta coverMu.RLock() newCover := cover.Difference(inp.cover, corpusCover[call.CallID]) newCover = cover.Difference(newCover, flakes) coverMu.RUnlock() if len(newCover) == 0 { return } corpusMu.RLock() if _, ok := corpusHashes[hash(inp.p.Serialize())]; ok { corpusMu.RUnlock() return } corpusMu.RUnlock() minCover := inp.cover for i := 0; i < 3; i++ { allCover := execute1(pid, env, inp.p, &statExecTriage) if len(allCover[inp.call]) == 0 { // The call was not executed. Happens sometimes, reason unknown. continue } coverMu.RLock() cov := allCover[inp.call] diff := cover.SymmetricDifference(inp.cover, cov) minCover = cover.Intersection(minCover, cov) updateFlakes := len(diff) != 0 && len(cover.Difference(diff, flakes)) != 0 coverMu.RUnlock() if updateFlakes { coverMu.Lock() flakes = cover.Union(flakes, diff) coverMu.Unlock() } } stableNewCover := cover.Intersection(newCover, minCover) if len(stableNewCover) == 0 { return } inp.p, inp.call = prog.Minimize(inp.p, inp.call, func(p1 *prog.Prog, call1 int) bool { allCover := execute1(pid, env, p1, &statExecMinimize) coverMu.RLock() defer coverMu.RUnlock() if len(allCover[call1]) == 0 { return false // The call was not executed. } cov := allCover[call1] if len(cover.Intersection(stableNewCover, cov)) != len(stableNewCover) { return false } minCover = cover.Intersection(minCover, cov) return true }, false) inp.cover = minCover atomic.AddUint64(&statNewInput, 1) data := inp.p.Serialize() Logf(2, "added new input for %v to corpus:\n%s", call.CallName, data) a := &NewInputArgs{*flagName, RpcInput{call.CallName, data, inp.call, []uint32(inp.cover)}} if err := manager.Call("Manager.NewInput", a, nil); err != nil { panic(err) } corpusMu.Lock() defer corpusMu.Unlock() coverMu.Lock() defer coverMu.Unlock() corpusCover[call.CallID] = cover.Union(corpusCover[call.CallID], minCover) corpus = append(corpus, inp.p) corpusHashes[hash(data)] = struct{}{} }
func (mgr *Manager) httpSummary(w http.ResponseWriter, r *http.Request) { mgr.mu.Lock() defer mgr.mu.Unlock() data := &UISummaryData{ Name: mgr.cfg.Name, } data.Stats = append(data.Stats, UIStat{Name: "uptime", Value: fmt.Sprint(time.Since(mgr.startTime) / 1e9 * 1e9)}) data.Stats = append(data.Stats, UIStat{Name: "corpus", Value: fmt.Sprint(len(mgr.corpus))}) data.Stats = append(data.Stats, UIStat{Name: "triage queue", Value: fmt.Sprint(len(mgr.candidates))}) var err error if data.Crashes, err = mgr.collectCrashes(); err != nil { http.Error(w, fmt.Sprintf("failed to collect crashes: %v", err), http.StatusInternalServerError) return } 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)) } secs := uint64(1) if !mgr.firstConnect.IsZero() { secs = uint64(time.Since(mgr.firstConnect))/1e9 + 1 } var cov cover.Cover totalUnique := mgr.uniqueCover(true) for c, cc := range calls { cov = cover.Union(cov, cc.cov) unique := cover.Intersection(cc.cov, totalUnique) data.Calls = append(data.Calls, UICallType{ Name: c, Inputs: cc.count, Cover: len(cc.cov), UniqueCover: len(unique), }) } sort.Sort(UICallTypeArray(data.Calls)) data.Stats = append(data.Stats, UIStat{Name: "cover", Value: fmt.Sprint(len(cov)), Link: "/cover"}) var intStats []UIStat for k, v := range mgr.stats { val := fmt.Sprintf("%v", v) 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) } intStats = append(intStats, UIStat{Name: k, Value: val}) } sort.Sort(UIStatArray(intStats)) data.Stats = append(data.Stats, intStats...) data.Log = CachedLogOutput() if err := summaryTemplate.Execute(w, data); err != nil { http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) return } }