From ee339263ba6b1a08006ea3e8e1862e15181a640d Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Wed, 23 Mar 2022 16:16:33 +0000 Subject: syz-manager: display raw coverage --- pkg/cover/html.go | 46 +++++++++++------ pkg/cover/report.go | 23 +++++---- pkg/cover/report_test.go | 2 +- syz-manager/cover.go | 2 +- syz-manager/html.go | 115 +++++++++++++++++++++++++++++++++++++++---- tools/syz-cover/syz-cover.go | 2 +- 6 files changed, 154 insertions(+), 36 deletions(-) diff --git a/pkg/cover/html.go b/pkg/cover/html.go index 4e6e89cef..98572b7f8 100644 --- a/pkg/cover/html.go +++ b/pkg/cover/html.go @@ -63,7 +63,8 @@ func (rg *ReportGenerator) DoHTML(w io.Writer, progs []Prog, coverFilter map[uin return err } d := &templateData{ - Root: new(templateDir), + Root: new(templateDir), + RawCover: rg.rawCoverEnabled, } haveProgs := len(progs) > 1 || progs[0].Data != "" fileOpenErr := fmt.Errorf("failed to open/locate any source file") @@ -133,7 +134,10 @@ func (rg *ReportGenerator) DoHTML(w io.Writer, progs []Prog, coverFilter map[uin return fileOpenErr } for _, prog := range progs { - d.Progs = append(d.Progs, template.HTML(html.EscapeString(prog.Data))) + d.Progs = append(d.Progs, templateProg{ + Sig: prog.Sig, + Content: template.HTML(html.EscapeString(prog.Data)), + }) } processDir(d.Root) @@ -163,19 +167,23 @@ func (rg *ReportGenerator) DoRawCoverFiles(w http.ResponseWriter, progs []Prog, func (rg *ReportGenerator) DoRawCover(w http.ResponseWriter, progs []Prog, coverFilter map[uint32]uint32) { progs = fixUpPCs(rg.target.Arch, progs, coverFilter) var pcs []uint64 - uniquePCs := make(map[uint64]bool) - for _, prog := range progs { - for _, pc := range prog.PCs { - if uniquePCs[pc] { - continue + if len(progs) == 1 && rg.rawCoverEnabled { + pcs = append([]uint64{}, progs[0].PCs...) + } else { + uniquePCs := make(map[uint64]bool) + for _, prog := range progs { + for _, pc := range prog.PCs { + if uniquePCs[pc] { + continue + } + uniquePCs[pc] = true + pcs = append(pcs, pc) } - uniquePCs[pc] = true - pcs = append(pcs, pc) } + sort.Slice(pcs, func(i, j int) bool { + return pcs[i] < pcs[j] + }) } - sort.Slice(pcs, func(i, j int) bool { - return pcs[i] < pcs[j] - }) w.Header().Set("Content-Type", "text/plain; charset=utf-8") buf := bufio.NewWriter(w) @@ -718,8 +726,14 @@ func parseFile(fn string) ([][]byte, error) { type templateData struct { Root *templateDir Contents []template.HTML - Progs []template.HTML + Progs []templateProg Functions []template.HTML + RawCover bool +} + +type templateProg struct { + Sig string + Content template.HTML } type templateBase struct { @@ -852,8 +866,12 @@ var coverTemplate = template.Must(template.New("").Parse(` {{range $i, $f := .Contents}}
{{$f}}
{{end}} + {{$base := .}} {{range $i, $p := .Progs}} -
{{$p}}
+
+{{if $base.RawCover}}[raw coverage]
{{end}} +{{$p.Content}} +
{{end}} {{range $i, $p := .Functions}}
{{$p}}
diff --git a/pkg/cover/report.go b/pkg/cover/report.go index 788603119..3423d9b09 100644 --- a/pkg/cover/report.go +++ b/pkg/cover/report.go @@ -14,14 +14,16 @@ import ( ) type ReportGenerator struct { - target *targets.Target - srcDir string - buildDir string - subsystem []mgrconfig.Subsystem + target *targets.Target + srcDir string + buildDir string + subsystem []mgrconfig.Subsystem + rawCoverEnabled bool *backend.Impl } type Prog struct { + Sig string Data string PCs []uint64 } @@ -29,7 +31,7 @@ type Prog struct { var RestorePC = backend.RestorePC func MakeReportGenerator(target *targets.Target, vm, objDir, srcDir, buildDir string, subsystem []mgrconfig.Subsystem, - moduleObj []string, modules []host.KernelModule) (*ReportGenerator, error) { + moduleObj []string, modules []host.KernelModule, rawCover bool) (*ReportGenerator, error) { impl, err := backend.Make(target, vm, objDir, srcDir, buildDir, moduleObj, modules) if err != nil { return nil, err @@ -39,11 +41,12 @@ func MakeReportGenerator(target *targets.Target, vm, objDir, srcDir, buildDir st Paths: []string{""}, }) rg := &ReportGenerator{ - target: target, - srcDir: srcDir, - buildDir: buildDir, - subsystem: subsystem, - Impl: impl, + target: target, + srcDir: srcDir, + buildDir: buildDir, + subsystem: subsystem, + rawCoverEnabled: rawCover, + Impl: impl, } return rg, nil } diff --git a/pkg/cover/report_test.go b/pkg/cover/report_test.go index a08fe9d1c..0f44c6e80 100644 --- a/pkg/cover/report_test.go +++ b/pkg/cover/report_test.go @@ -260,7 +260,7 @@ func generateReport(t *testing.T, target *targets.Target, test Test) ([]byte, [] }, } - rg, err := MakeReportGenerator(target, "", dir, dir, dir, subsystem, nil, nil) + rg, err := MakeReportGenerator(target, "", dir, dir, dir, subsystem, nil, nil, false) if err != nil { return nil, nil, err } diff --git a/syz-manager/cover.go b/syz-manager/cover.go index 8ddee085f..bc8d48e10 100644 --- a/syz-manager/cover.go +++ b/syz-manager/cover.go @@ -21,7 +21,7 @@ var getReportGenerator = func() func(cfg *mgrconfig.Config, once.Do(func() { log.Logf(0, "initializing coverage information...") rg, err = cover.MakeReportGenerator(cfg.SysTarget, cfg.Type, cfg.KernelObj, cfg.KernelSrc, - cfg.KernelBuildSrc, cfg.KernelSubsystem, cfg.ModuleObj, modules) + cfg.KernelBuildSrc, cfg.KernelSubsystem, cfg.ModuleObj, modules, cfg.RawCover) }) return rg, err } diff --git a/syz-manager/html.go b/syz-manager/html.go index dc3e54519..781ba3279 100644 --- a/syz-manager/html.go +++ b/syz-manager/html.go @@ -54,6 +54,7 @@ func (mgr *Manager) initHTTP() { mux.HandleFunc("/funccover", mgr.httpFuncCover) mux.HandleFunc("/filecover", mgr.httpFileCover) mux.HandleFunc("/input", mgr.httpInput) + mux.HandleFunc("/debuginput", mgr.httpDebugInput) // Browsers like to request this, without special handler this goes to / handler. mux.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {}) @@ -197,7 +198,8 @@ func (mgr *Manager) httpCorpus(w http.ResponseWriter, r *http.Request) { defer mgr.mu.Unlock() data := UICorpus{ - Call: r.FormValue("call"), + Call: r.FormValue("call"), + RawCover: mgr.cfg.RawCover, } for sig, inp := range mgr.corpus { if data.Call != "" && data.Call != inp.Call { @@ -293,17 +295,31 @@ func (mgr *Manager) httpCoverCover(w http.ResponseWriter, r *http.Request, funcF var progs []cover.Prog if sig := r.FormValue("input"); sig != "" { inp := mgr.corpus[sig] - progs = append(progs, cover.Prog{ - Data: string(inp.Prog), - PCs: coverToPCs(rg, inp.Cover), - }) + if r.FormValue("update_id") != "" { + updateID, err := strconv.Atoi(r.FormValue("update_id")) + if err != nil || updateID < 0 || updateID >= len(inp.Updates) { + http.Error(w, "bad call_id", http.StatusBadRequest) + } + progs = append(progs, cover.Prog{ + Sig: sig, + Data: string(inp.Prog), + PCs: coverToPCs(rg, inp.Updates[updateID].RawCover), + }) + } else { + progs = append(progs, cover.Prog{ + Sig: sig, + Data: string(inp.Prog), + PCs: coverToPCs(rg, inp.Cover), + }) + } } else { call := r.FormValue("call") - for _, inp := range mgr.corpus { + for sig, inp := range mgr.corpus { if call != "" && call != inp.Call { continue } progs = append(progs, cover.Prog{ + Sig: sig, Data: string(inp.Prog), PCs: coverToPCs(rg, inp.Cover), }) @@ -451,6 +467,46 @@ func (mgr *Manager) httpInput(w http.ResponseWriter, r *http.Request) { w.Write(inp.Prog) } +func (mgr *Manager) httpDebugInput(w http.ResponseWriter, r *http.Request) { + mgr.mu.Lock() + defer mgr.mu.Unlock() + inp, ok := mgr.corpus[r.FormValue("sig")] + if !ok { + http.Error(w, "can't find the input", http.StatusInternalServerError) + return + } + getIDs := func(callID int) []int { + ret := []int{} + for id, update := range inp.Updates { + if update.CallID == callID { + ret = append(ret, id) + } + } + return ret + } + data := []UIRawCallCover{} + for pos, line := range strings.Split(string(inp.Prog), "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + data = append(data, UIRawCallCover{ + Sig: r.FormValue("sig"), + Call: line, + UpdateIDs: getIDs(pos), + }) + } + extraIDs := getIDs(-1) + if len(extraIDs) > 0 { + data = append(data, UIRawCallCover{ + Sig: r.FormValue("sig"), + Call: ".extra", + UpdateIDs: extraIDs, + }) + } + executeTemplate(w, rawCoverTemplate, data) +} + func (mgr *Manager) httpReport(w http.ResponseWriter, r *http.Request) { mgr.mu.Lock() defer mgr.mu.Unlock() @@ -683,8 +739,9 @@ type UICallType struct { } type UICorpus struct { - Call string - Inputs []*UIInput + Call string + RawCover bool + Inputs []*UIInput } type UIInput struct { @@ -839,7 +896,12 @@ var corpusTemplate = html.CreatePage(` {{range $inp := $.Inputs}} - {{$inp.Cover}} + + {{$inp.Cover}} + {{if $.RawCover}} + / [raw] + {{end}} + {{$inp.Short}} {{end}} @@ -915,3 +977,38 @@ var fallbackCoverTemplate = html.CreatePage(` `) + +type UIRawCallCover struct { + Sig string + Call string + UpdateIDs []int +} + +var rawCoverTemplate = html.CreatePage(` + + + + syzkaller raw cover + {{HEAD}} + + + + + + + + + + {{range $line := .}} + + + + + {{end}} +
Raw cover
LineLinks
{{$line.Call}} + {{range $id := $line.UpdateIDs}} + [{{$id}}] + {{end}} +
+ +`) diff --git a/tools/syz-cover/syz-cover.go b/tools/syz-cover/syz-cover.go index 42e592bd9..5aa183ae1 100644 --- a/tools/syz-cover/syz-cover.go +++ b/tools/syz-cover/syz-cover.go @@ -68,7 +68,7 @@ func main() { tool.Fail(err) } rg, err := cover.MakeReportGenerator(target, *flagVM, *flagKernelObj, - *flagKernelSrc, *flagKernelBuildSrc, nil, nil, nil) + *flagKernelSrc, *flagKernelBuildSrc, nil, nil, nil, false) if err != nil { tool.Fail(err) } -- cgit mrf-deployment