diff options
| author | Mara Mihali <maramihali@google.com> | 2021-07-02 16:44:35 +0000 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2021-07-06 12:37:42 +0200 |
| commit | 9cac178ab43e4a07f6ba76c029a02417fe5f55c8 (patch) | |
| tree | d3af1c0344bc7f195b816f3504162ac9d2a7a527 /syz-verifier/main.go | |
| parent | 501aead18d8f3c3a89430bcaf87f0126fba048fe (diff) | |
syz-verifier: add result processing functionality
The main utility saves reports in a file for further inspection.
Diffstat (limited to 'syz-verifier/main.go')
| -rwxr-xr-x | syz-verifier/main.go | 111 |
1 files changed, 84 insertions, 27 deletions
diff --git a/syz-verifier/main.go b/syz-verifier/main.go index bf24d241e..2c35e1224 100755 --- a/syz-verifier/main.go +++ b/syz-verifier/main.go @@ -4,9 +4,11 @@ package main import ( "flag" + "fmt" "log" "math/rand" "net" + "os" "path/filepath" "strconv" "sync" @@ -23,6 +25,10 @@ import ( "github.com/google/syzkaller/vm" ) +const ( + maxResultReports = 100 +) + // Verifier TODO. type Verifier struct { pools map[int]*poolInfo @@ -35,6 +41,7 @@ type Verifier struct { // grouped by OS/Arch workdir string crashdir string + resultsdir string target *prog.Target runnerBin string executorBin string @@ -62,7 +69,7 @@ type poolInfo struct { cfg *mgrconfig.Config pool *vm.Pool Reporter report.Reporter - // vmRunners keep track of what programs have been sent to each Runner. + // vmRunners keeps track of what programs have been sent to each Runner. // There is one Runner executing per VM instance. vmRunners map[int][]*progInfo // progs stores the programs that haven't been sent to this kernel yet but @@ -141,6 +148,9 @@ func main() { osutil.MkdirAll(filepath.Join(crashdir, targetPath)) } + resultsdir := filepath.Join(workdir, "results") + osutil.MkdirAll(resultsdir) + for idx, pi := range pools { var err error pi.Reporter, err = report.NewReporter(pi.cfg) @@ -159,6 +169,7 @@ func main() { vrf := &Verifier{ workdir: workdir, crashdir: crashdir, + resultsdir: resultsdir, pools: pools, target: target, choiceTable: target.BuildChoiceTable(nil, calls), @@ -250,15 +261,11 @@ func (srv *RPCServer) NextExchange(a *rpctype.NextExchangeArgs, r *rpctype.NextE Hanged: a.Hanged, Info: a.Info, } - if srv.newResult(res, a.ProgIdx) { - for idx, prog := range srv.progs { - if a.ProgIdx == prog.idx { - if !verf.Verify(prog.res, prog.prog) { - log.Printf("mismatch found for %+v", prog.prog) - } - delete(srv.progs, idx) - } - } + + prog := srv.progs[a.ProgIdx] + if srv.newResult(res, prog) { + srv.vrf.processResults(prog.res, prog.prog) + delete(srv.progs, a.ProgIdx) } } @@ -267,6 +274,72 @@ func (srv *RPCServer) NextExchange(a *rpctype.NextExchangeArgs, r *rpctype.NextE return nil } +// newResult is called when a Runner sends a new Result. It returns true if all +// Results from the corresponding programs have been received and they can be +// sent for verification. Otherwise, it returns false. +func (srv *RPCServer) newResult(res *verf.Result, prog *progInfo) bool { + prog.res = append(prog.res, res) + delete(prog.left, res.Pool) + return len(prog.left) == 0 +} + +// processResults will send a set of complete results for verification and, in +// case differences are found, it will store a result report highlighting those +// in th workdir/results directory. If writing the results fails, it returns an +// error. +func (vrf *Verifier) processResults(res []*verf.Result, prog *prog.Prog) { + rr := verf.Verify(res, prog) + if rr == nil { + return + } + + oldest := 0 + var oldestTime time.Time + for i := 0; i < maxResultReports; i++ { + info, err := os.Stat(filepath.Join(vrf.resultsdir, fmt.Sprintf("result-%d", i))) + if err != nil { + // There are only i-1 report files so the i-th one + // can be created. + oldest = i + break + } + + // Otherwise, search for the oldest report file to + // overwrite as newer result reports are more useful. + if oldestTime.IsZero() || info.ModTime().Before(oldestTime) { + oldest = i + oldestTime = info.ModTime() + } + } + + err := osutil.WriteFile(filepath.Join(vrf.resultsdir, fmt.Sprintf("result-%d", oldest)), createReport(rr)) + if err != nil { + log.Printf("failed to write result-%d file, err %v", oldest, err) + } + + log.Printf("result-%d written successfully", oldest) +} + +func createReport(rr *verf.ResultReport) []byte { + data := fmt.Sprintf("Errno mismatches found for program:\n%s\n", rr.Prog) + data += "CALL REPORTS FOUND BELOW\n" + + for _, cr := range rr.Reports { + data += fmt.Sprintf("Report for call: %s", cr.Call) + + if cr.Mismatch { + data += " - MISMATCH FOUND" + } + data += "\n" + for pool, errno := range cr.Errnos { + data += fmt.Sprintf("Pool: %d, Errno: %d, Flag: %d\n", pool, errno, cr.Flags[pool]) + } + data += "\n" + } + + return []byte(data) +} + // newProgram returns a new program for the Runner identified by poolIdx and // vmIdx and the program's index. func (srv *RPCServer) newProgram(poolIdx, vmIdx int) ([]byte, int) { @@ -298,20 +371,6 @@ func (vrf *Verifier) generate() (*prog.Prog, int) { return vrf.target.Generate(vrf.rnd, prog.RecommendedCalls, vrf.choiceTable), vrf.progIdx } -// newResult is called when a Runner sends a new Result. It returns true if all -// Results from the corresponding programs have been received and they can be -// sent for verification. Otherwise, it returns false. -func (srv *RPCServer) newResult(res *verf.Result, idx int) bool { - for _, prog := range srv.progs { - if prog.idx == idx { - prog.res = append(prog.res, res) - delete(prog.left, res.Pool) - return len(prog.left) == 0 - } - } - return false -} - // cleanup is called when a vm.Instance crashes. func (srv *RPCServer) cleanup(poolIdx, vmIdx int) { srv.mu.Lock() @@ -321,9 +380,7 @@ func (srv *RPCServer) cleanup(poolIdx, vmIdx int) { for idx, prog := range progs { delete(prog.left, poolIdx) if len(prog.left) == 0 { - if !verf.Verify(prog.res, prog.prog) { - log.Printf("mismatch found for %+v", prog.prog) - } + srv.vrf.processResults(prog.res, prog.prog) delete(srv.progs, idx) continue } |
