diff options
| -rw-r--r-- | executor/executor.cc | 5 | ||||
| -rw-r--r-- | executor/executor_linux.h | 12 | ||||
| -rw-r--r-- | pkg/flatrpc/flatrpc.fbs | 1 | ||||
| -rw-r--r-- | pkg/flatrpc/flatrpc.go | 27 | ||||
| -rw-r--r-- | pkg/flatrpc/flatrpc.h | 20 | ||||
| -rw-r--r-- | pkg/fuzzer/fuzzer.go | 22 | ||||
| -rw-r--r-- | pkg/fuzzer/stats.go | 20 | ||||
| -rw-r--r-- | pkg/manager/html/syscalls.html | 6 | ||||
| -rw-r--r-- | pkg/manager/http.go | 36 | ||||
| -rw-r--r-- | prog/prog.go | 4 |
10 files changed, 118 insertions, 35 deletions
diff --git a/executor/executor.cc b/executor/executor.cc index 5b5e06422..603f23f73 100644 --- a/executor/executor.cc +++ b/executor/executor.cc @@ -362,6 +362,8 @@ struct cover_t { // offset (VM_MIN_KERNEL_ADDRESS for AMD64) and then truncates the result to // uint32_t. We get this from the 'offset' member in ksancov_trace. intptr_t pc_offset; + // The coverage buffer has overflowed and we have truncated coverage. + bool overflow; }; struct thread_t { @@ -1177,6 +1179,7 @@ uint32 write_comparisons(flatbuffers::FlatBufferBuilder& fbb, cover_t* cov) kcov_comparison_t* cov_start = (kcov_comparison_t*)(cov->data + sizeof(uint64)); if ((char*)(cov_start + ncomps) > cov->data_end) failmsg("too many comparisons", "ncomps=%llu", ncomps); + cov->overflow = ((char*)(cov_start + ncomps + 1) > cov->data_end); rpc::ComparisonRaw* start = (rpc::ComparisonRaw*)cov_start; rpc::ComparisonRaw* end = start; // We will convert kcov_comparison_t to ComparisonRaw inplace. @@ -1295,6 +1298,8 @@ void write_output(int index, cover_t* cov, rpc::CallFlag flags, uint32 error, bo } rpc::CallInfoRawBuilder builder(*output_builder); + if (cov->overflow) + flags |= rpc::CallFlag::CoverageOverflow; builder.add_flags(flags); builder.add_error(error); if (signal_off) diff --git a/executor/executor_linux.h b/executor/executor_linux.h index 82a1d559a..f2e9d4df6 100644 --- a/executor/executor_linux.h +++ b/executor/executor_linux.h @@ -180,14 +180,22 @@ static void cover_reset(cover_t* cov) cover_unprotect(cov); *(uint64*)cov->data = 0; cover_protect(cov); + cov->overflow = false; +} + +template <typename cover_data_t> +static void cover_collect_impl(cover_t* cov) +{ + cov->size = *(cover_data_t*)cov->data; + cov->overflow = (cov->data + (cov->size + 2) * sizeof(cover_data_t)) > cov->data_end; } static void cover_collect(cover_t* cov) { if (is_kernel_64_bit) - cov->size = *(uint64*)cov->data; + cover_collect_impl<uint64>(cov); else - cov->size = *(uint32*)cov->data; + cover_collect_impl<uint32>(cov); } // One does not simply exit. diff --git a/pkg/flatrpc/flatrpc.fbs b/pkg/flatrpc/flatrpc.fbs index 51066d9a4..0bd32b743 100644 --- a/pkg/flatrpc/flatrpc.fbs +++ b/pkg/flatrpc/flatrpc.fbs @@ -202,6 +202,7 @@ enum CallFlag : uint8 (bit_flags) { Finished, // finished executing (rather than blocked forever) Blocked, // finished but blocked during execution FaultInjected, // fault was injected into this call + CoverageOverflow, // coverage buffer has overflowed so we have truncated coverage } table CallInfoRaw { diff --git a/pkg/flatrpc/flatrpc.go b/pkg/flatrpc/flatrpc.go index 0a9c0e0d0..8445cde2d 100644 --- a/pkg/flatrpc/flatrpc.go +++ b/pkg/flatrpc/flatrpc.go @@ -392,24 +392,27 @@ func (v ExecFlag) String() string { type CallFlag byte const ( - CallFlagExecuted CallFlag = 1 - CallFlagFinished CallFlag = 2 - CallFlagBlocked CallFlag = 4 - CallFlagFaultInjected CallFlag = 8 + CallFlagExecuted CallFlag = 1 + CallFlagFinished CallFlag = 2 + CallFlagBlocked CallFlag = 4 + CallFlagFaultInjected CallFlag = 8 + CallFlagCoverageOverflow CallFlag = 16 ) var EnumNamesCallFlag = map[CallFlag]string{ - CallFlagExecuted: "Executed", - CallFlagFinished: "Finished", - CallFlagBlocked: "Blocked", - CallFlagFaultInjected: "FaultInjected", + CallFlagExecuted: "Executed", + CallFlagFinished: "Finished", + CallFlagBlocked: "Blocked", + CallFlagFaultInjected: "FaultInjected", + CallFlagCoverageOverflow: "CoverageOverflow", } var EnumValuesCallFlag = map[string]CallFlag{ - "Executed": CallFlagExecuted, - "Finished": CallFlagFinished, - "Blocked": CallFlagBlocked, - "FaultInjected": CallFlagFaultInjected, + "Executed": CallFlagExecuted, + "Finished": CallFlagFinished, + "Blocked": CallFlagBlocked, + "FaultInjected": CallFlagFaultInjected, + "CoverageOverflow": CallFlagCoverageOverflow, } func (v CallFlag) String() string { diff --git a/pkg/flatrpc/flatrpc.h b/pkg/flatrpc/flatrpc.h index 5232114b3..94e1c15f6 100644 --- a/pkg/flatrpc/flatrpc.h +++ b/pkg/flatrpc/flatrpc.h @@ -649,23 +649,25 @@ enum class CallFlag : uint8_t { Finished = 2, Blocked = 4, FaultInjected = 8, + CoverageOverflow = 16, NONE = 0, - ANY = 15 + ANY = 31 }; FLATBUFFERS_DEFINE_BITMASK_OPERATORS(CallFlag, uint8_t) -inline const CallFlag (&EnumValuesCallFlag())[4] { +inline const CallFlag (&EnumValuesCallFlag())[5] { static const CallFlag values[] = { CallFlag::Executed, CallFlag::Finished, CallFlag::Blocked, - CallFlag::FaultInjected + CallFlag::FaultInjected, + CallFlag::CoverageOverflow }; return values; } inline const char * const *EnumNamesCallFlag() { - static const char * const names[9] = { + static const char * const names[17] = { "Executed", "Finished", "", @@ -674,13 +676,21 @@ inline const char * const *EnumNamesCallFlag() { "", "", "FaultInjected", + "", + "", + "", + "", + "", + "", + "", + "CoverageOverflow", nullptr }; return names; } inline const char *EnumNameCallFlag(CallFlag e) { - if (flatbuffers::IsOutRange(e, CallFlag::Executed, CallFlag::FaultInjected)) return ""; + if (flatbuffers::IsOutRange(e, CallFlag::Executed, CallFlag::CoverageOverflow)) return ""; const size_t index = static_cast<size_t>(e) - static_cast<size_t>(CallFlag::Executed); return EnumNamesCallFlag()[index]; } diff --git a/pkg/fuzzer/fuzzer.go b/pkg/fuzzer/fuzzer.go index 3dac022ad..3e5f94fc3 100644 --- a/pkg/fuzzer/fuzzer.go +++ b/pkg/fuzzer/fuzzer.go @@ -50,7 +50,7 @@ func NewFuzzer(ctx context.Context, cfg *Config, rnd *rand.Rand, } } f := &Fuzzer{ - Stats: newStats(), + Stats: newStats(target), Config: cfg, Cover: newCover(), @@ -170,6 +170,10 @@ func (fuzzer *Fuzzer) processResult(req *queue.Request, res *queue.Result, flags if res.Info != nil { fuzzer.statExecTime.Add(int(res.Info.Elapsed / 1e6)) + for call, info := range res.Info.Calls { + fuzzer.handleCallInfo(req, info, call) + } + fuzzer.handleCallInfo(req, res.Info.Extra, -1) } // Corpus candidates may have flaky coverage, so we give them a second chance. @@ -231,6 +235,22 @@ func (fuzzer *Fuzzer) triageProgCall(p *prog.Prog, info *flatrpc.CallInfo, call } } +func (fuzzer *Fuzzer) handleCallInfo(req *queue.Request, info *flatrpc.CallInfo, call int) { + if info == nil || info.Flags&flatrpc.CallFlagCoverageOverflow == 0 { + return + } + syscallIdx := len(fuzzer.Syscalls) - 1 + if call != -1 { + syscallIdx = req.Prog.Calls[call].Meta.ID + } + stat := &fuzzer.Syscalls[syscallIdx] + if req.ExecOpts.ExecFlags&flatrpc.ExecFlagCollectComps != 0 { + stat.CompsOverflows.Add(1) + } else { + stat.CoverOverflows.Add(1) + } +} + func signalPrio(p *prog.Prog, info *flatrpc.CallInfo, call int) (prio uint8) { if call == -1 { return 0 diff --git a/pkg/fuzzer/stats.go b/pkg/fuzzer/stats.go index 7990f8b13..40c71d309 100644 --- a/pkg/fuzzer/stats.go +++ b/pkg/fuzzer/stats.go @@ -3,9 +3,17 @@ package fuzzer -import "github.com/google/syzkaller/pkg/stat" +import ( + "sync/atomic" + + "github.com/google/syzkaller/pkg/stat" + "github.com/google/syzkaller/prog" +) type Stats struct { + // Indexed by prog.Syscall.ID + the last element for extra/remote. + Syscalls []SyscallStats + statCandidates *stat.Val statNewInputs *stat.Val statJobs *stat.Val @@ -27,8 +35,16 @@ type Stats struct { statExecCollide *stat.Val } -func newStats() Stats { +type SyscallStats struct { + // Number of times coverage buffer for this syscall has overflowed. + CoverOverflows atomic.Uint64 + // Number of times comparisons buffer for this syscall has overflowed. + CompsOverflows atomic.Uint64 +} + +func newStats(target *prog.Target) Stats { return Stats{ + Syscalls: make([]SyscallStats, len(target.Syscalls)+1), statCandidates: stat.New("candidates", "Number of candidate programs in triage queue", stat.Console, stat.Graph("corpus")), statNewInputs: stat.New("new inputs", "Potential untriaged corpus candidates", diff --git a/pkg/manager/html/syscalls.html b/pkg/manager/html/syscalls.html index e3edfcabf..d16337978 100644 --- a/pkg/manager/html/syscalls.html +++ b/pkg/manager/html/syscalls.html @@ -9,7 +9,9 @@ Use of this source code is governed by Apache 2 LICENSE that can be found in the <th><a onclick="return sortTable(this, 'Syscall', textSort)" href="#">Syscall</a></th> <th><a onclick="return sortTable(this, 'Inputs', numSort)" href="#" title="Number of inputs in the corpus added because of this syscall">Inputs</a></th> <th><a onclick="return sortTable(this, 'Total', numSort)" href="#" title="Total number of inputs in the corpus that contain this syscall">Total</a></th> - <th><a onclick="return sortTable(this, 'Coverage', numSort)" href="#">Coverage</a></th> + <th><a onclick="return sortTable(this, 'Coverage', numSort)" href="#" title="Coverage achieved by this syscall">Coverage</a></th> + <th><a onclick="return sortTable(this, 'Cover overflows', numSort)" href="#" title="Number of times coverage buffer has overflowed on this syscall">Cover overflows</a></th> + <th><a onclick="return sortTable(this, 'Comps overflows', numSort)" href="#" title="Number of times comparisons buffer has overflowed on this syscall">Comps overflows</a></th> <th>Prio</th> </tr> {{range $c := $.Calls}} @@ -18,6 +20,8 @@ Use of this source code is governed by Apache 2 LICENSE that can be found in the <td><a href='/corpus?call={{$c.Name}}'>{{$c.Inputs}}</a></td> <td>{{$c.Total}}</td> <td><a href='/cover?call={{$c.Name}}'>{{$c.Cover}}</a></td> + <td>{{$c.CoverOverflows}}</td> + <td>{{$c.CompsOverflows}}</td> <td><a href='/prio?call={{$c.Name}}'>prio</a></td> </tr> {{end}} diff --git a/pkg/manager/http.go b/pkg/manager/http.go index 81affcf7e..cbde6d151 100644 --- a/pkg/manager/http.go +++ b/pkg/manager/http.go @@ -187,6 +187,7 @@ func (serv *HTTPServer) textPage(w http.ResponseWriter, r *http.Request, title s func (serv *HTTPServer) httpSyscalls(w http.ResponseWriter, r *http.Request) { var calls map[string]*corpus.CallCov total := make(map[string]int) + fuzzerObj := serv.Fuzzer.Load() syscallsObj := serv.EnabledSyscalls.Load() corpusObj := serv.Corpus.Load() if corpusObj != nil && syscallsObj != nil { @@ -217,12 +218,23 @@ func (serv *HTTPServer) httpSyscalls(w http.ResponseWriter, r *http.Request) { if syscall, ok := serv.Cfg.Target.SyscallMap[c]; ok { syscallID = &syscall.ID } + coverOverflows, compsOverflows := 0, 0 + if fuzzerObj != nil { + idx := len(serv.Cfg.Target.Syscalls) + if c != prog.ExtraCallName { + idx = serv.Cfg.Target.SyscallMap[c].ID + } + coverOverflows = int(fuzzerObj.Syscalls[idx].CoverOverflows.Load()) + compsOverflows = int(fuzzerObj.Syscalls[idx].CompsOverflows.Load()) + } data.Calls = append(data.Calls, UICallType{ - Name: c, - ID: syscallID, - Inputs: cc.Count, - Total: total[c], - Cover: len(cc.Cover), + Name: c, + ID: syscallID, + Inputs: cc.Count, + Total: total[c], + Cover: len(cc.Cover), + CoverOverflows: coverOverflows, + CompsOverflows: compsOverflows, }) } sort.Slice(data.Calls, func(i, j int) bool { @@ -702,7 +714,7 @@ func (serv *HTTPServer) httpDebugInput(w http.ResponseWriter, r *http.Request) { if len(extraIDs) > 0 { calls = append(calls, UIRawCallCover{ Sig: r.FormValue("sig"), - Call: ".extra", + Call: prog.ExtraCallName, UpdateIDs: extraIDs, }) } @@ -963,11 +975,13 @@ type UIStat struct { } type UICallType struct { - Name string - ID *int - Inputs int - Total int - Cover int + Name string + ID *int + Inputs int + Total int + Cover int + CoverOverflows int + CompsOverflows int } type UICorpusPage struct { diff --git a/prog/prog.go b/prog/prog.go index de3d87778..ec899e35f 100644 --- a/prog/prog.go +++ b/prog/prog.go @@ -17,12 +17,14 @@ type Prog struct { isUnsafe bool } +const ExtraCallName = ".extra" + func (p *Prog) CallName(call int) string { if call >= len(p.Calls) || call < -1 { panic(fmt.Sprintf("bad call index %v/%v", call, len(p.Calls))) } if call == -1 { - return ".extra" + return ExtraCallName } return p.Calls[call].Meta.Name } |
