aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2024-10-03 17:48:33 +0200
committerAleksandr Nogikh <nogikh@google.com>2024-10-11 11:58:06 +0000
commit8b02337e33dbcc9aae5b80ebe850733ca7c8fac7 (patch)
treea6c6c8a4e795b8c66ad3b728e3f7c6dbce1514da
parentd041766f6c33a373e0064b832cc4e7a4401b3659 (diff)
pkg/manager: factor out the HTTP server code
Decouple it from syz-manager. Remove a lot of no longer necessary mutex calls.
-rw-r--r--pkg/manager/http.go (renamed from syz-manager/http.go)510
-rw-r--r--syz-manager/hub.go4
-rw-r--r--syz-manager/manager.go183
3 files changed, 281 insertions, 416 deletions
diff --git a/syz-manager/http.go b/pkg/manager/http.go
index 2e5f703a9..96cd5a8bb 100644
--- a/syz-manager/http.go
+++ b/pkg/manager/http.go
@@ -1,7 +1,7 @@
// 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
+package manager
import (
"bytes"
@@ -18,77 +18,102 @@ import (
"sort"
"strconv"
"strings"
+ "sync/atomic"
"time"
+ "github.com/google/syzkaller/pkg/corpus"
"github.com/google/syzkaller/pkg/cover"
"github.com/google/syzkaller/pkg/fuzzer"
"github.com/google/syzkaller/pkg/html/pages"
"github.com/google/syzkaller/pkg/log"
- "github.com/google/syzkaller/pkg/manager"
- "github.com/google/syzkaller/pkg/osutil"
+ "github.com/google/syzkaller/pkg/mgrconfig"
"github.com/google/syzkaller/pkg/stat"
"github.com/google/syzkaller/pkg/vcs"
+ "github.com/google/syzkaller/pkg/vminfo"
"github.com/google/syzkaller/prog"
+ "github.com/google/syzkaller/vm"
"github.com/google/syzkaller/vm/dispatcher"
"github.com/gorilla/handlers"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
-func (mgr *Manager) initHTTP() {
+type CoverageInfo struct {
+ Modules []*vminfo.KernelModule
+ ReportGenerator *ReportGeneratorWrapper
+ CoverFilter map[uint64]struct{}
+}
+
+type HTTPServer struct {
+ // To be set once.
+ Cfg *mgrconfig.Config
+ StartTime time.Time
+ Corpus *corpus.Corpus
+ CrashStore *CrashStore
+
+ // Set dynamically.
+ Fuzzer atomic.Pointer[fuzzer.Fuzzer]
+ Cover atomic.Pointer[CoverageInfo]
+ ReproLoop atomic.Pointer[ReproLoop]
+ Pool atomic.Pointer[dispatcher.Pool[*vm.Instance]]
+ EnabledSyscalls atomic.Value // map[*prog.Syscall]bool
+
+ // Internal state.
+ expertMode bool
+}
+
+func (serv *HTTPServer) Serve() {
handle := func(pattern string, handler func(http.ResponseWriter, *http.Request)) {
http.Handle(pattern, handlers.CompressHandler(http.HandlerFunc(handler)))
}
- handle("/", mgr.httpSummary)
- handle("/config", mgr.httpConfig)
- handle("/expert_mode", mgr.httpExpertMode)
- handle("/stats", mgr.httpStats)
- handle("/vms", mgr.httpVMs)
- handle("/vm", mgr.httpVM)
+ handle("/", serv.httpSummary)
+ handle("/config", serv.httpConfig)
+ handle("/expert_mode", serv.httpExpertMode)
+ handle("/stats", serv.httpStats)
+ handle("/vms", serv.httpVMs)
+ handle("/vm", serv.httpVM)
handle("/metrics", promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{}).ServeHTTP)
- handle("/syscalls", mgr.httpSyscalls)
- handle("/corpus", mgr.httpCorpus)
- handle("/corpus.db", mgr.httpDownloadCorpus)
- handle("/crash", mgr.httpCrash)
- handle("/cover", mgr.httpCover)
- handle("/subsystemcover", mgr.httpSubsystemCover)
- handle("/modulecover", mgr.httpModuleCover)
- handle("/prio", mgr.httpPrio)
- handle("/file", mgr.httpFile)
- handle("/report", mgr.httpReport)
- handle("/rawcover", mgr.httpRawCover)
- handle("/rawcoverfiles", mgr.httpRawCoverFiles)
- handle("/filterpcs", mgr.httpFilterPCs)
- handle("/funccover", mgr.httpFuncCover)
- handle("/filecover", mgr.httpFileCover)
- handle("/input", mgr.httpInput)
- handle("/debuginput", mgr.httpDebugInput)
- handle("/modules", mgr.modulesInfo)
- handle("/jobs", mgr.httpJobs)
+ handle("/syscalls", serv.httpSyscalls)
+ handle("/corpus", serv.httpCorpus)
+ handle("/corpus.db", serv.httpDownloadCorpus)
+ handle("/crash", serv.httpCrash)
+ handle("/cover", serv.httpCover)
+ handle("/subsystemcover", serv.httpSubsystemCover)
+ handle("/modulecover", serv.httpModuleCover)
+ handle("/prio", serv.httpPrio)
+ handle("/file", serv.httpFile)
+ handle("/report", serv.httpReport)
+ handle("/rawcover", serv.httpRawCover)
+ handle("/rawcoverfiles", serv.httpRawCoverFiles)
+ handle("/filterpcs", serv.httpFilterPCs)
+ handle("/funccover", serv.httpFuncCover)
+ handle("/filecover", serv.httpFileCover)
+ handle("/input", serv.httpInput)
+ handle("/debuginput", serv.httpDebugInput)
+ handle("/modules", serv.modulesInfo)
+ handle("/jobs", serv.httpJobs)
// Browsers like to request this, without special handler this goes to / handler.
handle("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {})
- log.Logf(0, "serving http on http://%v", mgr.cfg.HTTP)
- go func() {
- err := http.ListenAndServe(mgr.cfg.HTTP, nil)
- if err != nil {
- log.Fatalf("failed to listen on %v: %v", mgr.cfg.HTTP, err)
- }
- }()
+ log.Logf(0, "serving http on http://%v", serv.Cfg.HTTP)
+ err := http.ListenAndServe(serv.Cfg.HTTP, nil)
+ if err != nil {
+ log.Fatalf("failed to listen on %v: %v", serv.Cfg.HTTP, err)
+ }
}
-func (mgr *Manager) httpSummary(w http.ResponseWriter, r *http.Request) {
+func (serv *HTTPServer) httpSummary(w http.ResponseWriter, r *http.Request) {
revision, link := revisionAndLink()
data := &UISummaryData{
- Name: mgr.cfg.Name,
+ Name: serv.Cfg.Name,
Revision: revision,
RevisionLink: link,
- Expert: mgr.expertMode,
+ Expert: serv.expertMode,
Log: log.CachedLogOutput(),
}
level := stat.Simple
- if mgr.expertMode {
+ if serv.expertMode {
level = stat.All
}
for _, stat := range stat.Collect(level) {
@@ -101,7 +126,7 @@ func (mgr *Manager) httpSummary(w http.ResponseWriter, r *http.Request) {
}
var err error
- if data.Crashes, err = mgr.collectCrashes(mgr.cfg.Workdir); err != nil {
+ if data.Crashes, err = serv.collectCrashes(serv.Cfg.Workdir); err != nil {
http.Error(w, fmt.Sprintf("failed to collect crashes: %v", err), http.StatusInternalServerError)
return
}
@@ -122,8 +147,8 @@ func revisionAndLink() (string, string) {
return revision, link
}
-func (mgr *Manager) httpConfig(w http.ResponseWriter, r *http.Request) {
- data, err := json.MarshalIndent(mgr.cfg, "", "\t")
+func (serv *HTTPServer) httpConfig(w http.ResponseWriter, r *http.Request) {
+ data, err := json.MarshalIndent(serv.Cfg, "", "\t")
if err != nil {
http.Error(w, fmt.Sprintf("failed to encode json: %v", err),
http.StatusInternalServerError)
@@ -132,18 +157,28 @@ func (mgr *Manager) httpConfig(w http.ResponseWriter, r *http.Request) {
w.Write(data)
}
-func (mgr *Manager) httpExpertMode(w http.ResponseWriter, r *http.Request) {
- mgr.expertMode = !mgr.expertMode
+func (serv *HTTPServer) httpExpertMode(w http.ResponseWriter, r *http.Request) {
+ serv.expertMode = !serv.expertMode
http.Redirect(w, r, "/", http.StatusFound)
}
-func (mgr *Manager) httpSyscalls(w http.ResponseWriter, r *http.Request) {
+func (serv *HTTPServer) httpSyscalls(w http.ResponseWriter, r *http.Request) {
+ var calls map[string]*corpus.CallCov
+ if obj := serv.EnabledSyscalls.Load(); obj != nil {
+ calls = serv.Corpus.CallCover()
+ // Add enabled, but not yet covered calls.
+ for call := range obj.(map[*prog.Syscall]bool) {
+ if calls[call.Name] == nil {
+ calls[call.Name] = new(corpus.CallCov)
+ }
+ }
+ }
data := &UISyscallsData{
- Name: mgr.cfg.Name,
+ Name: serv.Cfg.Name,
}
- for c, cc := range mgr.collectSyscallInfo() {
+ for c, cc := range calls {
var syscallID *int
- if syscall, ok := mgr.target.SyscallMap[c]; ok {
+ if syscall, ok := serv.Cfg.Target.SyscallMap[c]; ok {
syscallID = &syscall.ID
}
data.Calls = append(data.Calls, UICallType{
@@ -159,17 +194,22 @@ func (mgr *Manager) httpSyscalls(w http.ResponseWriter, r *http.Request) {
executeTemplate(w, syscallsTemplate, data)
}
-func (mgr *Manager) httpStats(w http.ResponseWriter, r *http.Request) {
+func (serv *HTTPServer) httpStats(w http.ResponseWriter, r *http.Request) {
executeTemplate(w, pages.StatsTemplate, stat.RenderGraphs())
}
-func (mgr *Manager) httpVMs(w http.ResponseWriter, r *http.Request) {
+func (serv *HTTPServer) httpVMs(w http.ResponseWriter, r *http.Request) {
+ pool := serv.Pool.Load()
+ if pool == nil {
+ http.Error(w, "no VM pool is present (yet)", http.StatusInternalServerError)
+ return
+ }
data := &UIVMData{
- Name: mgr.cfg.Name,
+ Name: serv.Cfg.Name,
}
// TODO: we could also query vmLoop for VMs that are idle (waiting to start reproducing),
// and query the exact bug that is being reproduced by a VM.
- for id, state := range mgr.pool.State() {
+ for id, state := range pool.State() {
name := fmt.Sprintf("#%d", id)
info := UIVMInfo{
Name: name,
@@ -200,10 +240,16 @@ func (mgr *Manager) httpVMs(w http.ResponseWriter, r *http.Request) {
executeTemplate(w, vmsTemplate, data)
}
-func (mgr *Manager) httpVM(w http.ResponseWriter, r *http.Request) {
+func (serv *HTTPServer) httpVM(w http.ResponseWriter, r *http.Request) {
+ pool := serv.Pool.Load()
+ if pool == nil {
+ http.Error(w, "no VM pool is present (yet)", http.StatusInternalServerError)
+ return
+ }
+
w.Header().Set("Content-Type", ctTextPlain)
id, err := strconv.Atoi(r.FormValue("id"))
- infos := mgr.pool.State()
+ infos := pool.State()
if err != nil || id < 0 || id >= len(infos) {
http.Error(w, "invalid instance id", http.StatusBadRequest)
return
@@ -223,25 +269,51 @@ func (mgr *Manager) httpVM(w http.ResponseWriter, r *http.Request) {
}
}
-func (mgr *Manager) httpCrash(w http.ResponseWriter, r *http.Request) {
+func makeUICrashType(info *BugInfo, startTime time.Time, repros map[string]bool) *UICrashType {
+ var crashes []*UICrash
+ for _, crash := range info.Crashes {
+ crashes = append(crashes, &UICrash{
+ CrashInfo: crash,
+ Active: crash.Time.After(startTime),
+ })
+ }
+ triaged := reproStatus(info.HasRepro, info.HasCRepro, repros[info.Title],
+ info.ReproAttempts >= MaxReproAttempts)
+ return &UICrashType{
+ Description: info.Title,
+ LastTime: info.LastTime,
+ Active: info.LastTime.After(startTime),
+ ID: info.ID,
+ Count: len(info.Crashes),
+ Triaged: triaged,
+ Strace: info.StraceFile,
+ Crashes: crashes,
+ }
+}
+
+var crashIDRe = regexp.MustCompile(`^\w+$`)
+
+func (serv *HTTPServer) httpCrash(w http.ResponseWriter, r *http.Request) {
crashID := r.FormValue("id")
- crash := readCrash(mgr.cfg.Workdir, crashID, nil, mgr.firstConnect.Load(), true)
- if crash == nil {
+ if !crashIDRe.MatchString(crashID) {
+ http.Error(w, "invalid crash ID", http.StatusBadRequest)
+ return
+ }
+ info, err := serv.CrashStore.BugInfo(crashID, true)
+ if err != nil {
http.Error(w, "failed to read crash info", http.StatusInternalServerError)
return
}
+ crash := makeUICrashType(info, serv.StartTime, nil)
executeTemplate(w, crashTemplate, crash)
}
-func (mgr *Manager) httpCorpus(w http.ResponseWriter, r *http.Request) {
- mgr.mu.Lock()
- defer mgr.mu.Unlock()
-
+func (serv *HTTPServer) httpCorpus(w http.ResponseWriter, r *http.Request) {
data := UICorpus{
Call: r.FormValue("call"),
- RawCover: mgr.cfg.RawCover,
+ RawCover: serv.Cfg.RawCover,
}
- for _, inp := range mgr.corpus.Items() {
+ for _, inp := range serv.Corpus.Items() {
if data.Call != "" && data.Call != inp.StringCall() {
continue
}
@@ -261,8 +333,8 @@ func (mgr *Manager) httpCorpus(w http.ResponseWriter, r *http.Request) {
executeTemplate(w, corpusTemplate, data)
}
-func (mgr *Manager) httpDownloadCorpus(w http.ResponseWriter, r *http.Request) {
- corpus := filepath.Join(mgr.cfg.Workdir, "corpus.db")
+func (serv *HTTPServer) httpDownloadCorpus(w http.ResponseWriter, r *http.Request) {
+ corpus := filepath.Join(serv.Cfg.Workdir, "corpus.db")
file, err := os.Open(corpus)
if err != nil {
http.Error(w, fmt.Sprintf("failed to open corpus : %v", err), http.StatusInternalServerError)
@@ -289,51 +361,50 @@ const (
DoCoverJSONL
)
-func (mgr *Manager) httpCover(w http.ResponseWriter, r *http.Request) {
- if !mgr.cfg.Cover {
- mgr.httpCoverFallback(w, r)
+func (serv *HTTPServer) httpCover(w http.ResponseWriter, r *http.Request) {
+ if !serv.Cfg.Cover {
+ serv.httpCoverFallback(w, r)
return
}
if r.FormValue("jsonl") == "1" {
- mgr.httpCoverCover(w, r, DoCoverJSONL)
+ serv.httpCoverCover(w, r, DoCoverJSONL)
return
}
- mgr.httpCoverCover(w, r, DoHTML)
+ serv.httpCoverCover(w, r, DoHTML)
}
-func (mgr *Manager) httpSubsystemCover(w http.ResponseWriter, r *http.Request) {
- if !mgr.cfg.Cover {
- mgr.httpCoverFallback(w, r)
+func (serv *HTTPServer) httpSubsystemCover(w http.ResponseWriter, r *http.Request) {
+ if !serv.Cfg.Cover {
+ serv.httpCoverFallback(w, r)
return
}
- mgr.httpCoverCover(w, r, DoSubsystemCover)
+ serv.httpCoverCover(w, r, DoSubsystemCover)
}
-func (mgr *Manager) httpModuleCover(w http.ResponseWriter, r *http.Request) {
- if !mgr.cfg.Cover {
- mgr.httpCoverFallback(w, r)
+func (serv *HTTPServer) httpModuleCover(w http.ResponseWriter, r *http.Request) {
+ if !serv.Cfg.Cover {
+ serv.httpCoverFallback(w, r)
return
}
- mgr.httpCoverCover(w, r, DoModuleCover)
+ serv.httpCoverCover(w, r, DoModuleCover)
}
const ctTextPlain = "text/plain; charset=utf-8"
const ctApplicationJSON = "application/json"
-func (mgr *Manager) httpCoverCover(w http.ResponseWriter, r *http.Request, funcFlag int) {
- if !mgr.cfg.Cover {
+func (serv *HTTPServer) httpCoverCover(w http.ResponseWriter, r *http.Request, funcFlag int) {
+ if !serv.Cfg.Cover {
http.Error(w, "coverage is not enabled", http.StatusInternalServerError)
return
}
- // Don't hold the mutex while creating report generator and generating the report,
- // these operations take lots of time.
- if !mgr.checkDone.Load() {
+ coverInfo := serv.Cover.Load()
+ if coverInfo == nil {
http.Error(w, "coverage is not ready, please try again later after fuzzer started", http.StatusInternalServerError)
return
}
- rg, err := mgr.reportGenerator.Get()
+ rg, err := coverInfo.ReportGenerator.Get()
if err != nil {
http.Error(w, fmt.Sprintf("failed to generate coverage profile: %v", err), http.StatusInternalServerError)
return
@@ -341,15 +412,14 @@ func (mgr *Manager) httpCoverCover(w http.ResponseWriter, r *http.Request, funcF
if r.FormValue("flush") != "" {
defer func() {
- mgr.reportGenerator.Reset()
+ coverInfo.ReportGenerator.Reset()
debug.FreeOSMemory()
}()
}
- mgr.mu.Lock()
var progs []cover.Prog
if sig := r.FormValue("input"); sig != "" {
- inp := mgr.corpus.Item(sig)
+ inp := serv.Corpus.Item(sig)
if inp == nil {
http.Error(w, "unknown input hash", http.StatusInternalServerError)
return
@@ -363,37 +433,36 @@ func (mgr *Manager) httpCoverCover(w http.ResponseWriter, r *http.Request, funcF
progs = append(progs, cover.Prog{
Sig: sig,
Data: string(inp.Prog.Serialize()),
- PCs: manager.CoverToPCs(mgr.cfg, inp.Updates[updateID].RawCover),
+ PCs: CoverToPCs(serv.Cfg, inp.Updates[updateID].RawCover),
})
} else {
progs = append(progs, cover.Prog{
Sig: sig,
Data: string(inp.Prog.Serialize()),
- PCs: manager.CoverToPCs(mgr.cfg, inp.Cover),
+ PCs: CoverToPCs(serv.Cfg, inp.Cover),
})
}
} else {
call := r.FormValue("call")
- for _, inp := range mgr.corpus.Items() {
+ for _, inp := range serv.Corpus.Items() {
if call != "" && call != inp.StringCall() {
continue
}
progs = append(progs, cover.Prog{
Sig: inp.Sig,
Data: string(inp.Prog.Serialize()),
- PCs: manager.CoverToPCs(mgr.cfg, inp.Cover),
+ PCs: CoverToPCs(serv.Cfg, inp.Cover),
})
}
}
- mgr.mu.Unlock()
var coverFilter map[uint64]struct{}
if r.FormValue("filter") != "" || funcFlag == DoFilterPCs {
- if mgr.coverFilter == nil {
+ if coverInfo.CoverFilter == nil {
http.Error(w, "cover is not filtered in config", http.StatusInternalServerError)
return
}
- coverFilter = mgr.coverFilter
+ coverFilter = coverInfo.CoverFilter
}
params := cover.HandlerParams{
@@ -429,28 +498,28 @@ func (mgr *Manager) httpCoverCover(w http.ResponseWriter, r *http.Request, funcF
}
}
-func (mgr *Manager) httpCoverFallback(w http.ResponseWriter, r *http.Request) {
- mgr.mu.Lock()
- defer mgr.mu.Unlock()
+func (serv *HTTPServer) httpCoverFallback(w http.ResponseWriter, r *http.Request) {
calls := make(map[int][]int)
- for s := range mgr.corpus.Signal() {
+ for s := range serv.Corpus.Signal() {
id, errno := prog.DecodeFallbackSignal(uint64(s))
calls[id] = append(calls[id], errno)
}
data := &UIFallbackCoverData{}
- for call := range mgr.targetEnabledSyscalls {
- errnos := calls[call.ID]
- sort.Ints(errnos)
- successful := 0
- for len(errnos) != 0 && errnos[0] == 0 {
- successful++
- errnos = errnos[1:]
+ if obj := serv.EnabledSyscalls.Load(); obj != nil {
+ for call := range obj.(map[*prog.Syscall]bool) {
+ errnos := calls[call.ID]
+ sort.Ints(errnos)
+ successful := 0
+ for len(errnos) != 0 && errnos[0] == 0 {
+ successful++
+ errnos = errnos[1:]
+ }
+ data.Calls = append(data.Calls, UIFallbackCall{
+ Name: call.Name,
+ Successful: successful,
+ Errnos: errnos,
+ })
}
- data.Calls = append(data.Calls, UIFallbackCall{
- Name: call.Name,
- Successful: successful,
- Errnos: errnos,
- })
}
sort.Slice(data.Calls, func(i, j int) bool {
return data.Calls[i].Name < data.Calls[j].Name
@@ -458,34 +527,31 @@ func (mgr *Manager) httpCoverFallback(w http.ResponseWriter, r *http.Request) {
executeTemplate(w, fallbackCoverTemplate, data)
}
-func (mgr *Manager) httpFuncCover(w http.ResponseWriter, r *http.Request) {
- mgr.httpCoverCover(w, r, DoFuncCover)
+func (serv *HTTPServer) httpFuncCover(w http.ResponseWriter, r *http.Request) {
+ serv.httpCoverCover(w, r, DoFuncCover)
}
-func (mgr *Manager) httpFileCover(w http.ResponseWriter, r *http.Request) {
- mgr.httpCoverCover(w, r, DoFileCover)
+func (serv *HTTPServer) httpFileCover(w http.ResponseWriter, r *http.Request) {
+ serv.httpCoverCover(w, r, DoFileCover)
}
-func (mgr *Manager) httpPrio(w http.ResponseWriter, r *http.Request) {
- mgr.mu.Lock()
- defer mgr.mu.Unlock()
-
+func (serv *HTTPServer) httpPrio(w http.ResponseWriter, r *http.Request) {
callName := r.FormValue("call")
- call := mgr.target.SyscallMap[callName]
+ call := serv.Cfg.Target.SyscallMap[callName]
if call == nil {
http.Error(w, fmt.Sprintf("unknown call: %v", callName), http.StatusInternalServerError)
return
}
var corpus []*prog.Prog
- for _, inp := range mgr.corpus.Items() {
+ for _, inp := range serv.Corpus.Items() {
corpus = append(corpus, inp.Prog)
}
- prios := mgr.target.CalculatePriorities(corpus)
+ prios := serv.Cfg.Target.CalculatePriorities(corpus)
data := &UIPrioData{Call: callName}
for i, p := range prios[call.ID] {
- data.Prios = append(data.Prios, UIPrio{mgr.target.Syscalls[i].Name, p})
+ data.Prios = append(data.Prios, UIPrio{serv.Cfg.Target.Syscalls[i].Name, p})
}
sort.Slice(data.Prios, func(i, j int) bool {
return data.Prios[i].Prio > data.Prios[j].Prio
@@ -493,13 +559,13 @@ func (mgr *Manager) httpPrio(w http.ResponseWriter, r *http.Request) {
executeTemplate(w, prioTemplate, data)
}
-func (mgr *Manager) httpFile(w http.ResponseWriter, r *http.Request) {
+func (serv *HTTPServer) httpFile(w http.ResponseWriter, r *http.Request) {
file := filepath.Clean(r.FormValue("name"))
if !strings.HasPrefix(file, "crashes/") && !strings.HasPrefix(file, "corpus/") {
http.Error(w, "oh, oh, oh!", http.StatusInternalServerError)
return
}
- file = filepath.Join(mgr.cfg.Workdir, file)
+ file = filepath.Join(serv.Cfg.Workdir, file)
f, err := os.Open(file)
if err != nil {
http.Error(w, "failed to open the file", http.StatusInternalServerError)
@@ -510,10 +576,8 @@ func (mgr *Manager) httpFile(w http.ResponseWriter, r *http.Request) {
io.Copy(w, f)
}
-func (mgr *Manager) httpInput(w http.ResponseWriter, r *http.Request) {
- mgr.mu.Lock()
- defer mgr.mu.Unlock()
- inp := mgr.corpus.Item(r.FormValue("sig"))
+func (serv *HTTPServer) httpInput(w http.ResponseWriter, r *http.Request) {
+ inp := serv.Corpus.Item(r.FormValue("sig"))
if inp == nil {
http.Error(w, "can't find the input", http.StatusInternalServerError)
return
@@ -522,10 +586,8 @@ func (mgr *Manager) httpInput(w http.ResponseWriter, r *http.Request) {
w.Write(inp.Prog.Serialize())
}
-func (mgr *Manager) httpDebugInput(w http.ResponseWriter, r *http.Request) {
- mgr.mu.Lock()
- defer mgr.mu.Unlock()
- inp := mgr.corpus.Item(r.FormValue("sig"))
+func (serv *HTTPServer) httpDebugInput(w http.ResponseWriter, r *http.Request) {
+ inp := serv.Corpus.Item(r.FormValue("sig"))
if inp == nil {
http.Error(w, "can't find the input", http.StatusInternalServerError)
return
@@ -562,18 +624,21 @@ func (mgr *Manager) httpDebugInput(w http.ResponseWriter, r *http.Request) {
executeTemplate(w, rawCoverTemplate, data)
}
-func (mgr *Manager) modulesInfo(w http.ResponseWriter, r *http.Request) {
- if !mgr.checkDone.Load() {
+func (serv *HTTPServer) modulesInfo(w http.ResponseWriter, r *http.Request) {
+ var modules []*vminfo.KernelModule
+ if obj := serv.Cover.Load(); obj != nil {
+ modules = obj.Modules
+ } else {
http.Error(w, "info is not ready, please try again later after fuzzer started", http.StatusInternalServerError)
return
}
- modules, err := json.MarshalIndent(mgr.modules, "", "\t")
+ jsonModules, err := json.MarshalIndent(modules, "", "\t")
if err != nil {
fmt.Fprintf(w, "unable to create JSON modules info: %v", err)
return
}
w.Header().Set("Content-Type", "application/json")
- w.Write(modules)
+ w.Write(jsonModules)
}
var alphaNumRegExp = regexp.MustCompile(`^[a-zA-Z0-9]*$`)
@@ -582,84 +647,68 @@ func isAlphanumeric(s string) bool {
return alphaNumRegExp.MatchString(s)
}
-func (mgr *Manager) httpReport(w http.ResponseWriter, r *http.Request) {
- mgr.mu.Lock()
- defer mgr.mu.Unlock()
-
+func (serv *HTTPServer) httpReport(w http.ResponseWriter, r *http.Request) {
crashID := r.FormValue("id")
if !isAlphanumeric(crashID) {
http.Error(w, "wrong id", http.StatusBadRequest)
return
}
- desc, err := os.ReadFile(filepath.Join(mgr.crashdir, crashID, "description"))
+ info, err := serv.CrashStore.Report(crashID)
if err != nil {
- http.Error(w, "failed to read description file", http.StatusInternalServerError)
+ http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
return
}
- tag, _ := os.ReadFile(filepath.Join(mgr.crashdir, crashID, "repro.tag"))
- prog, _ := os.ReadFile(filepath.Join(mgr.crashdir, crashID, "repro.prog"))
- cprog, _ := os.ReadFile(filepath.Join(mgr.crashdir, crashID, "repro.cprog"))
- rep, _ := os.ReadFile(filepath.Join(mgr.crashdir, crashID, "repro.report"))
commitDesc := ""
- if len(tag) != 0 {
- commitDesc = fmt.Sprintf(" on commit %s.", trimNewLines(tag))
+ if info.Tag != "" {
+ commitDesc = fmt.Sprintf(" on commit %s.", info.Tag)
}
- fmt.Fprintf(w, "Syzkaller hit '%s' bug%s.\n\n", trimNewLines(desc), commitDesc)
- if len(rep) != 0 {
- fmt.Fprintf(w, "%s\n\n", rep)
+ fmt.Fprintf(w, "Syzkaller hit '%s' bug%s.\n\n", info.Title, commitDesc)
+ if len(info.Report) != 0 {
+ fmt.Fprintf(w, "%s\n\n", info.Report)
}
- if len(prog) == 0 && len(cprog) == 0 {
+ if len(info.Prog) == 0 && len(info.CProg) == 0 {
fmt.Fprintf(w, "The bug is not reproducible.\n")
} else {
- fmt.Fprintf(w, "Syzkaller reproducer:\n%s\n\n", prog)
- if len(cprog) != 0 {
- fmt.Fprintf(w, "C reproducer:\n%s\n\n", cprog)
+ fmt.Fprintf(w, "Syzkaller reproducer:\n%s\n\n", info.Prog)
+ if len(info.CProg) != 0 {
+ fmt.Fprintf(w, "C reproducer:\n%s\n\n", info.CProg)
}
}
}
-func (mgr *Manager) httpRawCover(w http.ResponseWriter, r *http.Request) {
- mgr.httpCoverCover(w, r, DoRawCover)
+func (serv *HTTPServer) httpRawCover(w http.ResponseWriter, r *http.Request) {
+ serv.httpCoverCover(w, r, DoRawCover)
}
-func (mgr *Manager) httpRawCoverFiles(w http.ResponseWriter, r *http.Request) {
- mgr.httpCoverCover(w, r, DoRawCoverFiles)
+func (serv *HTTPServer) httpRawCoverFiles(w http.ResponseWriter, r *http.Request) {
+ serv.httpCoverCover(w, r, DoRawCoverFiles)
}
-func (mgr *Manager) httpFilterPCs(w http.ResponseWriter, r *http.Request) {
- mgr.httpCoverCover(w, r, DoFilterPCs)
+func (serv *HTTPServer) httpFilterPCs(w http.ResponseWriter, r *http.Request) {
+ serv.httpCoverCover(w, r, DoFilterPCs)
}
-func (mgr *Manager) collectCrashes(workdir string) ([]*UICrashType, error) {
- // Note: mu is not locked here.
+func (serv *HTTPServer) collectCrashes(workdir string) ([]*UICrashType, error) {
var repros map[string]bool
- if !mgr.cfg.VMLess && mgr.reproLoop != nil {
- repros = mgr.reproLoop.Reproducing()
+ if reproLoop := serv.ReproLoop.Load(); reproLoop != nil {
+ repros = reproLoop.Reproducing()
}
-
- crashdir := filepath.Join(workdir, "crashes")
- dirs, err := osutil.ListDir(crashdir)
+ list, err := serv.CrashStore.BugList()
if err != nil {
return nil, err
}
- var crashTypes []*UICrashType
- for _, dir := range dirs {
- crash := readCrash(workdir, dir, repros, mgr.firstConnect.Load(), false)
- if crash != nil {
- crashTypes = append(crashTypes, crash)
- }
+ var ret []*UICrashType
+ for _, info := range list {
+ ret = append(ret, makeUICrashType(info, serv.StartTime, repros))
}
- sort.Slice(crashTypes, func(i, j int) bool {
- return strings.ToLower(crashTypes[i].Description) < strings.ToLower(crashTypes[j].Description)
- })
- return crashTypes, nil
+ return ret, nil
}
-func (mgr *Manager) httpJobs(w http.ResponseWriter, r *http.Request) {
+func (serv *HTTPServer) httpJobs(w http.ResponseWriter, r *http.Request) {
var list []*fuzzer.JobInfo
- if fuzzer := mgr.fuzzer.Load(); fuzzer != nil {
+ if fuzzer := serv.Fuzzer.Load(); fuzzer != nil {
list = fuzzer.RunningJobs()
}
if key := r.FormValue("id"); key != "" {
@@ -702,92 +751,6 @@ func (mgr *Manager) httpJobs(w http.ResponseWriter, r *http.Request) {
executeTemplate(w, jobListTemplate, data)
}
-func readCrash(workdir, dir string, repros map[string]bool, start int64, full bool) *UICrashType {
- if len(dir) != 40 {
- return nil
- }
- crashdir := filepath.Join(workdir, "crashes")
- descFile, err := os.Open(filepath.Join(crashdir, dir, "description"))
- if err != nil {
- return nil
- }
- defer descFile.Close()
- descBytes, err := io.ReadAll(descFile)
- if err != nil || len(descBytes) == 0 {
- return nil
- }
- desc := string(trimNewLines(descBytes))
- stat, err := descFile.Stat()
- if err != nil {
- return nil
- }
- modTime := stat.ModTime()
- descFile.Close()
-
- files, err := osutil.ListDir(filepath.Join(crashdir, dir))
- if err != nil {
- return nil
- }
- var crashes []*UICrash
- reproAttempts := 0
- hasRepro, hasCRepro := false, false
- strace := ""
- reports := make(map[string]bool)
- for _, f := range files {
- if strings.HasPrefix(f, "log") {
- index, err := strconv.ParseUint(f[3:], 10, 64)
- if err == nil {
- crashes = append(crashes, &UICrash{
- Index: int(index),
- })
- }
- } else if strings.HasPrefix(f, "report") {
- reports[f] = true
- } else if f == "repro.prog" {
- hasRepro = true
- } else if f == "repro.cprog" {
- hasCRepro = true
- } else if f == "repro.report" {
- } else if f == "repro0" || f == "repro1" || f == "repro2" {
- reproAttempts++
- } else if f == "strace.log" {
- strace = filepath.Join("crashes", dir, f)
- }
- }
-
- if full {
- for _, crash := range crashes {
- index := strconv.Itoa(crash.Index)
- crash.Log = filepath.Join("crashes", dir, "log"+index)
- if stat, err := os.Stat(filepath.Join(workdir, crash.Log)); err == nil {
- crash.Time = stat.ModTime()
- crash.Active = start != 0 && crash.Time.Unix() >= start
- }
- tag, _ := os.ReadFile(filepath.Join(crashdir, dir, "tag"+index))
- crash.Tag = string(tag)
- reportFile := filepath.Join("crashes", dir, "report"+index)
- if osutil.IsExist(filepath.Join(workdir, reportFile)) {
- crash.Report = reportFile
- }
- }
- sort.Slice(crashes, func(i, j int) bool {
- return crashes[i].Time.After(crashes[j].Time)
- })
- }
-
- triaged := reproStatus(hasRepro, hasCRepro, repros[desc], reproAttempts >= maxReproAttempts)
- return &UICrashType{
- Description: desc,
- LastTime: modTime,
- Active: start != 0 && modTime.Unix() >= start,
- ID: dir,
- Count: len(crashes),
- Triaged: triaged,
- Strace: strace,
- Crashes: crashes,
- }
-}
-
func reproStatus(hasRepro, hasCRepro, reproducing, nonReproducible bool) string {
status := ""
if hasRepro {
@@ -813,13 +776,6 @@ func executeTemplate(w http.ResponseWriter, templ *template.Template, data inter
w.Write(buf.Bytes())
}
-func trimNewLines(data []byte) []byte {
- for len(data) > 0 && data[len(data)-1] == '\n' {
- data = data[:len(data)-1]
- }
- return data
-}
-
type UISummaryData struct {
Name string
Revision string
@@ -860,12 +816,8 @@ type UICrashType struct {
}
type UICrash struct {
- Index int
- Time time.Time
+ *CrashInfo
Active bool
- Log string
- Report string
- Tag string
}
type UIStat struct {
diff --git a/syz-manager/hub.go b/syz-manager/hub.go
index 014822bfe..4f0a02de0 100644
--- a/syz-manager/hub.go
+++ b/syz-manager/hub.go
@@ -39,13 +39,13 @@ func pickGetter(key string) keyGetter {
}
}
-func (mgr *Manager) hubSyncLoop(keyGet keyGetter) {
+func (mgr *Manager) hubSyncLoop(keyGet keyGetter, enabledSyscalls map[*prog.Syscall]bool) {
hc := &HubConnector{
mgr: mgr,
cfg: mgr.cfg,
target: mgr.target,
domain: mgr.cfg.TargetOS + "/" + mgr.cfg.HubDomain,
- enabledCalls: mgr.targetEnabledSyscalls,
+ enabledCalls: enabledSyscalls,
leak: mgr.enabledFeatures&flatrpc.FeatureLeak != 0,
fresh: mgr.fresh,
hubReproQueue: mgr.externalReproQueue,
diff --git a/syz-manager/manager.go b/syz-manager/manager.go
index 21df05001..ac451d720 100644
--- a/syz-manager/manager.go
+++ b/syz-manager/manager.go
@@ -31,7 +31,6 @@ import (
"github.com/google/syzkaller/pkg/fuzzer"
"github.com/google/syzkaller/pkg/fuzzer/queue"
"github.com/google/syzkaller/pkg/gce"
- "github.com/google/syzkaller/pkg/hash"
"github.com/google/syzkaller/pkg/log"
"github.com/google/syzkaller/pkg/manager"
"github.com/google/syzkaller/pkg/mgrconfig"
@@ -79,8 +78,9 @@ type Manager struct {
target *prog.Target
sysTarget *targets.Target
reporter *report.Reporter
- crashdir string
+ crashStore *manager.CrashStore
serv rpcserver.Server
+ http *manager.HTTPServer
corpus *corpus.Corpus
corpusDB *db.DB
corpusDBMu sync.Mutex // for concurrent operations on corpusDB
@@ -91,8 +91,6 @@ type Manager struct {
checkDone atomic.Bool
reportGenerator *manager.ReportGeneratorWrapper
fresh bool
- expertMode bool
- modules []*vminfo.KernelModule
coverFilter map[uint64]struct{} // includes only coverage PCs
dash *dashapi.Dashboard
@@ -100,11 +98,10 @@ type Manager struct {
// cfg.DashboardOnlyRepro is set, so that we don't accidentially use dash for anything.
dashRepro *dashapi.Dashboard
- mu sync.Mutex
- fuzzer atomic.Pointer[fuzzer.Fuzzer]
- snapshotSource *queue.Distributor
- phase int
- targetEnabledSyscalls map[*prog.Syscall]bool
+ mu sync.Mutex
+ fuzzer atomic.Pointer[fuzzer.Fuzzer]
+ snapshotSource *queue.Distributor
+ phase int
disabledHashes map[string]struct{}
newRepros [][]byte
@@ -204,8 +201,7 @@ func RunManager(mode Mode, cfg *mgrconfig.Config) {
defer vmPool.Close()
}
- crashdir := filepath.Join(cfg.Workdir, "crashes")
- osutil.MkdirAll(crashdir)
+ osutil.MkdirAll(cfg.Workdir)
reporter, err := report.NewReporter(cfg)
if err != nil {
@@ -222,7 +218,7 @@ func RunManager(mode Mode, cfg *mgrconfig.Config) {
target: cfg.Target,
sysTarget: cfg.SysTarget,
reporter: reporter,
- crashdir: crashdir,
+ crashStore: manager.NewCrashStore(cfg),
crashTypes: make(map[string]bool),
disabledHashes: make(map[string]struct{}),
memoryLeakFrames: make(map[string]bool),
@@ -233,10 +229,15 @@ func RunManager(mode Mode, cfg *mgrconfig.Config) {
saturatedCalls: make(map[string]bool),
reportGenerator: manager.ReportGeneratorCache(cfg),
}
-
if *flagDebug {
mgr.cfg.Procs = 1
}
+ mgr.http = &manager.HTTPServer{
+ Cfg: cfg,
+ StartTime: time.Now(),
+ Corpus: mgr.corpus,
+ CrashStore: mgr.crashStore,
+ }
mgr.initStats()
if mode == ModeFuzzing || mode == ModeCorpusTriage || mode == ModeCorpusRun {
@@ -244,7 +245,7 @@ func RunManager(mode Mode, cfg *mgrconfig.Config) {
} else {
close(mgr.corpusPreload)
}
- mgr.initHTTP() // Creates HTTP server.
+ go mgr.http.Serve()
go mgr.corpusInputHandler(corpusUpdates)
go mgr.trackUsedFiles()
@@ -296,7 +297,10 @@ func RunManager(mode Mode, cfg *mgrconfig.Config) {
return
}
mgr.pool = vm.NewDispatcher(mgr.vmPool, mgr.fuzzerInstance)
+ mgr.http.Pool.Store(mgr.pool)
mgr.reproLoop = manager.NewReproLoop(mgr, mgr.vmPool.Count()-mgr.cfg.FuzzingVMs, mgr.cfg.DashboardOnlyRepro)
+ mgr.http.ReproLoop.Store(mgr.reproLoop)
+
ctx := vm.ShutdownCtx()
go mgr.processFuzzingResults(ctx)
mgr.pool.Loop(ctx)
@@ -480,8 +484,8 @@ func (mgr *Manager) preloadCorpus() {
mgr.corpusPreload <- info.Candidates
}
-func (mgr *Manager) loadCorpus() []fuzzer.Candidate {
- ret := manager.FilterCandidates(<-mgr.corpusPreload, mgr.targetEnabledSyscalls, true)
+func (mgr *Manager) loadCorpus(enabledSyscalls map[*prog.Syscall]bool) []fuzzer.Candidate {
+ ret := manager.FilterCandidates(<-mgr.corpusPreload, enabledSyscalls, true)
if mgr.cfg.PreserveCorpus {
for _, hash := range ret.ModifiedHashes {
// This program contains a disabled syscall.
@@ -674,66 +678,25 @@ func (mgr *Manager) saveCrash(crash *manager.Crash) bool {
return mgr.cfg.Reproduce && resp.NeedRepro
}
}
-
- sig := hash.Hash([]byte(crash.Title))
- id := sig.String()
- dir := filepath.Join(mgr.crashdir, id)
- osutil.MkdirAll(dir)
- if err := osutil.WriteFile(filepath.Join(dir, "description"), []byte(crash.Title+"\n")); err != nil {
- log.Logf(0, "failed to write crash: %v", err)
+ first, err := mgr.crashStore.SaveCrash(crash)
+ if err != nil {
+ log.Logf(0, "failed to save the cash: %v", err)
+ return false
}
-
- // Save up to mgr.cfg.MaxCrashLogs reports, overwrite the oldest once we've reached that number.
- // Newer reports are generally more useful. Overwriting is also needed
- // to be able to understand if a particular bug still happens or already fixed.
- oldestI := 0
- var oldestTime time.Time
- for i := 0; i < mgr.cfg.MaxCrashLogs; i++ {
- info, err := os.Stat(filepath.Join(dir, fmt.Sprintf("log%v", i)))
- if err != nil {
- oldestI = i
- if i == 0 {
- go mgr.emailCrash(crash)
- }
- break
- }
- if oldestTime.IsZero() || info.ModTime().Before(oldestTime) {
- oldestI = i
- oldestTime = info.ModTime()
- }
+ if first {
+ go mgr.emailCrash(crash)
}
- writeOrRemove := func(name string, data []byte) {
- filename := filepath.Join(dir, name+fmt.Sprint(oldestI))
- if len(data) == 0 {
- os.Remove(filename)
- return
- }
- osutil.WriteFile(filename, data)
- }
- writeOrRemove("log", crash.Output)
- writeOrRemove("tag", []byte(mgr.cfg.Tag))
- writeOrRemove("report", crash.Report.Report)
- writeOrRemove("machineInfo", crash.MachineInfo)
return mgr.NeedRepro(crash)
}
-const maxReproAttempts = 3
-
func (mgr *Manager) needLocalRepro(crash *manager.Crash) bool {
if !mgr.cfg.Reproduce || crash.Corrupted || crash.Suppressed {
return false
}
- sig := hash.Hash([]byte(crash.Title))
- dir := filepath.Join(mgr.crashdir, sig.String())
- if osutil.IsExist(filepath.Join(dir, "repro.prog")) {
+ if mgr.crashStore.HasRepro(crash.Title) {
return false
}
- for i := 0; i < maxReproAttempts; i++ {
- if !osutil.IsExist(filepath.Join(dir, fmt.Sprintf("repro%v", i))) {
- return true
- }
- }
- return false
+ return mgr.crashStore.MoreReproAttempts(crash.Title)
}
func (mgr *Manager) NeedRepro(crash *manager.Crash) bool {
@@ -743,7 +706,10 @@ func (mgr *Manager) NeedRepro(crash *manager.Crash) bool {
if crash.FromHub || crash.FromDashboard {
return true
}
- if !mgr.checkDone.Load() || (mgr.enabledFeatures&flatrpc.FeatureLeak != 0 &&
+ mgr.mu.Lock()
+ phase, features := mgr.phase, mgr.enabledFeatures
+ mgr.mu.Unlock()
+ if phase < phaseLoadedCorpus || (features&flatrpc.FeatureLeak != 0 &&
crash.Type != crash_pkg.MemoryLeak) {
// Leak checking is very slow, don't bother reproducing other crashes on leak instance.
return false
@@ -797,14 +763,9 @@ func (mgr *Manager) saveFailedRepro(rep *report.Report, stats *repro.Stats) {
return
}
}
- dir := filepath.Join(mgr.crashdir, hash.String([]byte(rep.Title)))
- osutil.MkdirAll(dir)
- for i := 0; i < maxReproAttempts; i++ {
- name := filepath.Join(dir, fmt.Sprintf("repro%v", i))
- if !osutil.IsExist(name) && len(reproLog) > 0 {
- osutil.WriteFile(name, reproLog)
- break
- }
+ err := mgr.crashStore.SaveFailedRepro(rep.Title, reproLog)
+ if err != nil {
+ log.Logf(0, "failed to save repro log for %q: %v", rep.Title, err)
}
}
@@ -880,45 +841,9 @@ func (mgr *Manager) saveRepro(res *manager.ReproResult) {
return
}
}
-
- rep := repro.Report
- dir := filepath.Join(mgr.crashdir, hash.String([]byte(rep.Title)))
- osutil.MkdirAll(dir)
-
- if err := osutil.WriteFile(filepath.Join(dir, "description"), []byte(rep.Title+"\n")); err != nil {
- log.Logf(0, "failed to write crash: %v", err)
- }
- osutil.WriteFile(filepath.Join(dir, "repro.prog"), append([]byte(opts), progText...))
- if mgr.cfg.Tag != "" {
- osutil.WriteFile(filepath.Join(dir, "repro.tag"), []byte(mgr.cfg.Tag))
- }
- if len(rep.Output) > 0 {
- osutil.WriteFile(filepath.Join(dir, "repro.log"), rep.Output)
- }
- if len(rep.Report) > 0 {
- osutil.WriteFile(filepath.Join(dir, "repro.report"), rep.Report)
- }
- if len(cprogText) > 0 {
- osutil.WriteFile(filepath.Join(dir, "repro.cprog"), cprogText)
- }
- repro.Prog.ForEachAsset(func(name string, typ prog.AssetType, r io.Reader) {
- fileName := filepath.Join(dir, name+".gz")
- if err := osutil.WriteGzipStream(fileName, r); err != nil {
- log.Logf(0, "failed to write crash asset: type %d, write error %v", typ, err)
- }
- })
- if res.Strace != nil {
- // Unlike dashboard reporting, we save strace output separately from the original log.
- if res.Strace.Error != nil {
- osutil.WriteFile(filepath.Join(dir, "strace.error"),
- []byte(fmt.Sprintf("%v", res.Strace.Error)))
- }
- if len(res.Strace.Output) > 0 {
- osutil.WriteFile(filepath.Join(dir, "strace.log"), res.Strace.Output)
- }
- }
- if reproLog := res.Stats.FullLog(); len(reproLog) > 0 {
- osutil.WriteFile(filepath.Join(dir, "repro.stats"), reproLog)
+ err := mgr.crashStore.SaveRepro(res, append([]byte(opts), progText...), cprogText)
+ if err != nil {
+ log.Logf(0, "%s", err)
}
}
@@ -1074,24 +999,6 @@ func setGuiltyFiles(crash *dashapi.Crash, report *report.Report) {
}
}
-func (mgr *Manager) collectSyscallInfo() map[string]*corpus.CallCov {
- mgr.mu.Lock()
- enabledSyscalls := mgr.targetEnabledSyscalls
- mgr.mu.Unlock()
-
- if enabledSyscalls == nil {
- return nil
- }
- calls := mgr.corpus.CallCover()
- // Add enabled, but not yet covered calls.
- for call := range enabledSyscalls {
- if calls[call.Name] == nil {
- calls[call.Name] = new(corpus.CallCov)
- }
- }
- return calls
-}
-
func (mgr *Manager) BugFrames() (leaks, races []string) {
mgr.mu.Lock()
defer mgr.mu.Unlock()
@@ -1121,12 +1028,12 @@ func (mgr *Manager) MachineChecked(features flatrpc.Feature, enabledSyscalls map
panic("MachineChecked called twice")
}
mgr.enabledFeatures = features
- mgr.targetEnabledSyscalls = enabledSyscalls
+ mgr.http.EnabledSyscalls.Store(enabledSyscalls)
mgr.firstConnect.Store(time.Now().Unix())
statSyscalls := stat.New("syscalls", "Number of enabled syscalls",
stat.Simple, stat.NoGraph, stat.Link("/syscalls"))
statSyscalls.Add(len(enabledSyscalls))
- corpus := mgr.loadCorpus()
+ corpus := mgr.loadCorpus(enabledSyscalls)
mgr.setPhaseLocked(phaseLoadedCorpus)
opts := mgr.defaultExecOpts()
@@ -1156,6 +1063,7 @@ func (mgr *Manager) MachineChecked(features flatrpc.Feature, enabledSyscalls map
}, rnd, mgr.target)
fuzzerObj.AddCandidates(corpus)
mgr.fuzzer.Store(fuzzerObj)
+ mgr.http.Fuzzer.Store(fuzzerObj)
go mgr.corpusMinimization()
go mgr.fuzzerLoop(fuzzerObj)
@@ -1301,7 +1209,8 @@ func (mgr *Manager) fuzzerLoop(fuzzer *fuzzer.Fuzzer) {
}
if mgr.cfg.HubClient != "" {
mgr.setPhaseLocked(phaseTriagedCorpus)
- go mgr.hubSyncLoop(pickGetter(mgr.cfg.HubKey))
+ go mgr.hubSyncLoop(pickGetter(mgr.cfg.HubKey),
+ fuzzer.Config.EnabledCalls)
} else {
mgr.setPhaseLocked(phaseTriagedHub)
}
@@ -1452,8 +1361,12 @@ func (mgr *Manager) CoverageFilter(modules []*vminfo.KernelModule) []uint64 {
if err != nil {
log.Fatalf("failed to init coverage filter: %v", err)
}
- mgr.modules = modules
mgr.coverFilter = filter
+ mgr.http.Cover.Store(&manager.CoverageInfo{
+ Modules: modules,
+ ReportGenerator: mgr.reportGenerator,
+ CoverFilter: filter,
+ })
return execFilter
}