diff options
| -rw-r--r-- | executor/cov_filter.h | 58 | ||||
| -rw-r--r-- | executor/executor.cc | 11 | ||||
| -rw-r--r-- | executor/test.h | 34 | ||||
| -rw-r--r-- | pkg/cover/report.go | 62 | ||||
| -rw-r--r-- | pkg/ipc/ipc.go | 13 | ||||
| -rw-r--r-- | pkg/mgrconfig/config.go | 16 | ||||
| -rw-r--r-- | pkg/rpctype/rpctype.go | 15 | ||||
| -rw-r--r-- | syz-fuzzer/fuzzer.go | 4 | ||||
| -rw-r--r-- | syz-manager/covfilter.go | 213 | ||||
| -rw-r--r-- | syz-manager/covfilter_test.go | 47 | ||||
| -rw-r--r-- | syz-manager/manager.go | 18 | ||||
| -rw-r--r-- | syz-manager/rpc.go | 5 |
12 files changed, 465 insertions, 31 deletions
diff --git a/executor/cov_filter.h b/executor/cov_filter.h new file mode 100644 index 000000000..66cb0bcdc --- /dev/null +++ b/executor/cov_filter.h @@ -0,0 +1,58 @@ +// Copyright 2020 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. + +#include <fcntl.h> +#include <stdio.h> + +#if GOOS_linux || GOOS_freebsd || GOOS_netbsd || GOOS_openbsd || GOOS_akaros +#include <sys/mman.h> +#include <sys/stat.h> +#endif + +struct cov_filter_t { + uint32 pcstart; + uint32 pcsize; + uint8 bitmap[]; +}; + +static cov_filter_t* cov_filter; + +static void init_coverage_filter() +{ +#if SYZ_EXECUTOR_USES_SHMEM + int f = open("/syz-cover-bitmap", O_RDONLY); + if (f < 0) { + // We don't fail here because we don't know yet if we should use coverage filter or not. + // We will receive the flag only in execute flags and will fail in coverage_filter if necessary. + debug("bitmap is no found, coverage filter disabled\n"); + return; + } + struct stat st; + if (fstat(f, &st)) + fail("faied to stat coverage filter"); + // A random address for bitmap. Don't corrupt output_data. + void* preferred = (void*)0x110f230000ull; + cov_filter = (cov_filter_t*)mmap(preferred, st.st_size, PROT_READ, MAP_PRIVATE, f, 0); + if (cov_filter != preferred) + fail("failed to initialize coverage filter bitmap at %p", preferred); + if ((uint32)st.st_size != sizeof(uint32) * 2 + ((cov_filter->pcsize >> 4) + 7) / 8) + fail("bad coverage filter bitmap size"); + close(f); +#endif +} + +static bool coverage_filter(uint64 pc) +{ + if (cov_filter == NULL) + fail("coverage filter was enabled but bitmap initialization failed"); + // Prevent out of bound while searching bitmap. + uint32 pc32 = (uint32)(pc & 0xffffffff); + if (pc32 < cov_filter->pcstart || pc32 > cov_filter->pcstart + cov_filter->pcsize) + return false; + // For minimizing the size of bitmap, the lowest 4-bit will be dropped. + pc32 -= cov_filter->pcstart; + pc32 = pc32 >> 4; + uint32 idx = pc32 / 8; + uint32 shift = pc32 % 8; + return (cov_filter->bitmap[idx] & (1 << shift)) > 0; +} diff --git a/executor/executor.cc b/executor/executor.cc index 2f90e4092..7c31f9cc2 100644 --- a/executor/executor.cc +++ b/executor/executor.cc @@ -141,6 +141,7 @@ static bool flag_collect_cover; static bool flag_dedup_cover; static bool flag_threaded; static bool flag_collide; +static bool flag_coverage_filter; // If true, then executor should write the comparisons data to fuzzer. static bool flag_comparisons; @@ -350,6 +351,8 @@ static void setup_features(char** enable, int n); #error "unknown OS" #endif +#include "cov_filter.h" + #include "test.h" int main(int argc, char** argv) @@ -428,6 +431,7 @@ int main(int argc, char** argv) // Don't enable comps because we don't use them in the fuzzer yet. cover_enable(&extra_cov, false, true); } + init_coverage_filter(); } int status = 0; @@ -547,14 +551,15 @@ void receive_execute() flag_comparisons = req.exec_flags & (1 << 3); flag_threaded = req.exec_flags & (1 << 4); flag_collide = req.exec_flags & (1 << 5); + flag_coverage_filter = req.exec_flags & (1 << 6); flag_fault_call = req.fault_call; flag_fault_nth = req.fault_nth; if (!flag_threaded) flag_collide = false; - debug("[%llums] exec opts: procid=%llu threaded=%d collide=%d cover=%d comps=%d dedup=%d fault=%d/%d/%d prog=%llu\n", + debug("[%llums] exec opts: procid=%llu threaded=%d collide=%d cover=%d comps=%d dedup=%d fault=%d/%d/%d prog=%llu filter=%d\n", current_time_ms() - start_time_ms, procid, flag_threaded, flag_collide, flag_collect_cover, flag_comparisons, flag_dedup_cover, flag_fault, - flag_fault_call, flag_fault_nth, req.prog_size); + flag_fault_call, flag_fault_nth, req.prog_size, flag_coverage_filter); if (SYZ_EXECUTOR_USES_SHMEM) { if (req.prog_size) fail("need_prog: no program"); @@ -873,6 +878,8 @@ void write_coverage_signal(cover_t* cov, uint32* signal_count_pos, uint32* cover } cover_data_t sig = pc ^ prev; prev = hash(pc); + if (flag_coverage_filter && !coverage_filter((uint64)pc)) + continue; if (dedup(sig)) continue; write_output(sig); diff --git a/executor/test.h b/executor/test.h index 37d03b65a..c8492ed73 100644 --- a/executor/test.h +++ b/executor/test.h @@ -201,6 +201,39 @@ static int test_csum_inet_acc() return 0; } +static int test_coverage_filter() +{ + struct tmp_cov_filter_t { + uint32 pcstart; + uint32 pcsize; + uint8 bitmap[((0x1000 >> 4) + 7) / 8]; + }; + static struct tmp_cov_filter_t tmp_cov_filter; + tmp_cov_filter.pcstart = 0x81000000; + tmp_cov_filter.pcsize = 0x1000; + memset(tmp_cov_filter.bitmap, 0, ((0x1000 >> 4) + 7) / 8); + cov_filter = (cov_filter_t*)&tmp_cov_filter; + + uint64 full_enable_pc = 0xffffffff81000765; + uint64 full_disable_pc = 0xffffffff81000627; + uint64 full_out_pc = 0xffffffff82000000; + + uint32 enable_pc = (uint32)full_enable_pc & 0xffffffff; + uint32 idx = ((enable_pc - cov_filter->pcstart) >> 4) / 8; + uint32 shift = ((enable_pc - cov_filter->pcstart) >> 4) % 8; + cov_filter->bitmap[idx] |= (1 << shift); + + if (!coverage_filter(full_enable_pc)) + return 1; + if (coverage_filter(full_disable_pc)) + return 1; + if (coverage_filter(full_out_pc)) + return 1; + + cov_filter = NULL; + return 0; +} + static struct { const char* name; int (*f)(); @@ -211,6 +244,7 @@ static struct { #if GOOS_linux && (GOARCH_amd64 || GOARCH_ppc64 || GOARCH_ppc64le) {"test_kvm", test_kvm}, #endif + {"test_coverage_filter", test_coverage_filter}, }; static int run_tests() diff --git a/pkg/cover/report.go b/pkg/cover/report.go index 53184955b..163013d4f 100644 --- a/pkg/cover/report.go +++ b/pkg/cover/report.go @@ -326,6 +326,24 @@ func (rg *ReportGenerator) lazySymbolize(files map[string]*file, progs []Prog) e return nil } +type Symbol struct { + Name string + File string + PCs []uint64 +} + +func (rg *ReportGenerator) GetSymbolsInfo() []Symbol { + retSymbols := make([]Symbol, 0) + for _, sym := range rg.symbols { + retSymbols = append(retSymbols, Symbol{ + Name: sym.name, + File: sym.unit.name, + PCs: sym.pcs, + }) + } + return retSymbols +} + func getFile(files map[string]*file, name, filename string) *file { f := files[name] if f == nil { @@ -829,27 +847,47 @@ func parseFile(fn string) ([][]byte, error) { } func PreviousInstructionPC(target *targets.Target, pc uint64) uint64 { - switch target.Arch { + offset := instructionLen(target.Arch) + pc -= offset + // THUMB instructions are 2 or 4 bytes with low bit set. + // ARM instructions are always 4 bytes. + if target.Arch == targets.ARM { + return pc & ^uint64(1) + } + return pc +} + +func NextInstructionPC(target *targets.Target, pc uint64) uint64 { + offset := instructionLen(target.Arch) + pc += offset + // THUMB instructions are 2 or 4 bytes with low bit set. + // ARM instructions are always 4 bytes. + if target.Arch == targets.ARM { + return pc & ^uint64(1) + } + return pc +} + +func instructionLen(arch string) uint64 { + switch arch { case targets.AMD64: - return pc - 5 + return 5 case targets.I386: - return pc - 5 + return 5 case targets.ARM64: - return pc - 4 + return 4 case targets.ARM: - // THUMB instructions are 2 or 4 bytes with low bit set. - // ARM instructions are always 4 bytes. - return (pc - 3) & ^uint64(1) + return 3 case targets.PPC64LE: - return pc - 4 + return 4 case targets.MIPS64LE: - return pc - 8 + return 8 case targets.S390x: - return pc - 6 + return 6 case targets.RiscV64: - return pc - 4 + return 4 default: - panic(fmt.Sprintf("unknown arch %q", target.Arch)) + panic(fmt.Sprintf("unknown arch %q", arch)) } } diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go index 19bc89183..cb6dcd8e9 100644 --- a/pkg/ipc/ipc.go +++ b/pkg/ipc/ipc.go @@ -48,12 +48,13 @@ const ( type ExecFlags uint64 const ( - FlagCollectCover ExecFlags = 1 << iota // collect coverage - FlagDedupCover // deduplicate coverage in executor - FlagInjectFault // inject a fault in this execution (see ExecOpts) - FlagCollectComps // collect KCOV comparisons - FlagThreaded // use multiple threads to mitigate blocked syscalls - FlagCollide // collide syscalls to provoke data races + FlagCollectCover ExecFlags = 1 << iota // collect coverage + FlagDedupCover // deduplicate coverage in executor + FlagInjectFault // inject a fault in this execution (see ExecOpts) + FlagCollectComps // collect KCOV comparisons + FlagThreaded // use multiple threads to mitigate blocked syscalls + FlagCollide // collide syscalls to provoke data races + FlagEnableCoverageFilter // setup and use bitmap to do coverage filter ) type ExecOpts struct { diff --git a/pkg/mgrconfig/config.go b/pkg/mgrconfig/config.go index b03d16d67..a7476ac8e 100644 --- a/pkg/mgrconfig/config.go +++ b/pkg/mgrconfig/config.go @@ -106,6 +106,16 @@ type Config struct { // Use KCOV coverage (default: true). Cover bool `json:"cover"` + // Use coverage filter. Supported types of filter: + // "files": support specifying kernel source files, support regular expression. + // eg. "files": ["^net/core/tcp.c$", "^net/sctp/", "tcp"]. + // "functions": support specifying kernel functions, support regular expression. + // eg. "functions": ["^foo$", "^bar", "baz"]. + // "pcs": specify raw PC table files name. + // Each line of the file should be: "64-bit-pc:32-bit-weight\n". + // eg. "0xffffffff81000000:0x10\n" + CovFilter covFilterCfg `json:"cover_filter"` + // Reproduce, localize and minimize crashers (default: true). Reproduce bool `json:"reproduce"` @@ -131,3 +141,9 @@ type Config struct { // Implementation details beyond this point. Filled after parsing. Derived `json:"-"` } + +type covFilterCfg struct { + Files []string `json:"files"` + Functions []string `json:"functions"` + RawPCs []string `json:"pcs"` +} diff --git a/pkg/rpctype/rpctype.go b/pkg/rpctype/rpctype.go index 5dc85e0e2..c87334cd5 100644 --- a/pkg/rpctype/rpctype.go +++ b/pkg/rpctype/rpctype.go @@ -30,13 +30,14 @@ type ConnectArgs struct { } type ConnectRes struct { - EnabledCalls []int - GitRevision string - TargetRevision string - AllSandboxes bool - CheckResult *CheckArgs - MemoryLeakFrames []string - DataRaceFrames []string + EnabledCalls []int + GitRevision string + TargetRevision string + AllSandboxes bool + CheckResult *CheckArgs + MemoryLeakFrames []string + DataRaceFrames []string + EnabledCoverFilter bool } type CheckArgs struct { diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go index e6cd41d8b..4df208d1b 100644 --- a/syz-fuzzer/fuzzer.go +++ b/syz-fuzzer/fuzzer.go @@ -269,6 +269,10 @@ func main() { } fuzzer.choiceTable = target.BuildChoiceTable(fuzzer.corpus, calls) + if r.EnabledCoverFilter { + fuzzer.execOpts.Flags |= ipc.FlagEnableCoverageFilter + } + for pid := 0; pid < *flagProcs; pid++ { proc, err := newProc(fuzzer, pid) if err != nil { diff --git a/syz-manager/covfilter.go b/syz-manager/covfilter.go new file mode 100644 index 000000000..5d9f47825 --- /dev/null +++ b/syz-manager/covfilter.go @@ -0,0 +1,213 @@ +// Copyright 2020 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 + +import ( + "bufio" + "encoding/binary" + "fmt" + "os" + "regexp" + "strconv" + + "github.com/google/syzkaller/pkg/cover" + "github.com/google/syzkaller/pkg/log" + "github.com/google/syzkaller/pkg/mgrconfig" + "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/sys/targets" +) + +type CoverFilter struct { + pcStart uint32 + pcSize uint32 + pcEnd uint32 + weightedPCs map[uint32]uint32 + + bitmapFilename string + target *targets.Target +} + +func createCoverageFilter(cfg *mgrconfig.Config) (covFilterFilename string, err error) { + files := cfg.CovFilter.Files + funcs := cfg.CovFilter.Functions + rawPCs := cfg.CovFilter.RawPCs + if len(files) == 0 && len(funcs) == 0 && len(rawPCs) == 0 { + return "", nil + } + filesRegexp, err := getRegexps(files) + if err != nil { + return "", err + } + funcsRegexp, err := getRegexps(funcs) + if err != nil { + return "", err + } + + covFilter := CoverFilter{ + weightedPCs: make(map[uint32]uint32), + target: cfg.SysTarget, + bitmapFilename: cfg.Workdir + "/" + "syz-cover-bitmap", + } + + if len(filesRegexp) > 0 || len(funcsRegexp) > 0 { + log.Logf(0, "initialize coverage information...") + if err = initCover(cfg.SysTarget, cfg.KernelObj, cfg.KernelSrc, cfg.KernelBuildSrc); err != nil { + return "", err + } + symbols := reportGenerator.GetSymbolsInfo() + if err = covFilter.initFilesFuncs(filesRegexp, funcsRegexp, symbols); err != nil { + return "", err + } + } + + if err = covFilter.initWeightedPCs(rawPCs); err != nil { + return "", err + } + + covFilter.detectRegion() + if covFilter.pcSize > 0 { + log.Logf(0, "coverage filter from 0x%x to 0x%x, size 0x%x", + covFilter.pcStart, covFilter.pcEnd, covFilter.pcSize) + } else { + return "", fmt.Errorf("coverage filter is enabled but nothing will be filtered") + } + + if err = osutil.WriteFile(covFilter.bitmapFilename, covFilter.bitmapBytes()); err != nil { + return "", err + } + return covFilter.bitmapFilename, nil +} + +func getRegexps(regexpStrings []string) ([]*regexp.Regexp, error) { + var regexps []*regexp.Regexp + for _, rs := range regexpStrings { + r, err := regexp.Compile(rs) + if err != nil { + return nil, fmt.Errorf("failed to compile regexp: %v", err) + } + regexps = append(regexps, r) + } + return regexps, nil +} + +func (covFilter *CoverFilter) initFilesFuncs(filesRegexp, funcsRegexp []*regexp.Regexp, symbols []cover.Symbol) error { + fileDedup := make(map[string]bool) + used := make(map[*regexp.Regexp][]string) + for _, sym := range symbols { + matched := false + for _, re := range funcsRegexp { + if re.MatchString(sym.Name) { + matched = true + used[re] = append(used[re], sym.Name) + break + } + } + for _, re := range filesRegexp { + if re.MatchString(sym.File) { + matched = true + if !fileDedup[sym.File] { + fileDedup[sym.File] = true + used[re] = append(used[re], sym.File) + } + break + } + } + if matched { + for _, pc := range sym.PCs { + covFilter.weightedPCs[uint32(pc)] = 1 + } + } + } + + for _, re := range filesRegexp { + if _, ok := used[re]; !ok { + log.Logf(0, "coverage file filter doesn't match anything: %v", re.String()) + } else { + log.Logf(1, "coverage file filter: %v: %v", re.String(), used[re]) + } + } + for _, re := range funcsRegexp { + if _, ok := used[re]; !ok { + log.Logf(0, "coverage func filter doesn't match anything: %v", re.String()) + } else { + log.Logf(1, "coverage func filter: %v: %v", re.String(), used[re]) + } + } + // TODO: do we want to error on this? or logging it enough? + if len(filesRegexp)+len(funcsRegexp) != len(used) { + return fmt.Errorf("some coverage filters don't match anything") + } + return nil +} + +func (covFilter *CoverFilter) initWeightedPCs(rawPCsFiles []string) error { + for _, f := range rawPCsFiles { + rawFile, err := os.Open(f) + if err != nil { + return fmt.Errorf("failed to open raw PCs file: %v", err) + } + defer rawFile.Close() + + s := bufio.NewScanner(rawFile) + re := regexp.MustCompile(`(0x[0-9a-f]+)(?:: (0x[0-9a-f]+))?`) + for s.Scan() { + match := re.FindStringSubmatch(s.Text()) + if match == nil { + return fmt.Errorf("bad line: %q", s.Text()) + } + pc, err := strconv.ParseUint(match[1], 0, 64) + if err != nil { + return err + } + weight, err := strconv.ParseUint(match[2], 0, 32) + if match[2] != "" && err != nil { + return err + } + // If no weight is detected, set the weight to 0x1 by default. + if match[2] == "" || weight < 1 { + weight = 1 + } + covFilter.weightedPCs[uint32(pc)] = uint32(weight) + } + } + return nil +} + +func (covFilter *CoverFilter) detectRegion() { + covFilter.pcStart = ^uint32(0) + covFilter.pcEnd = 0x0 + for pc := range covFilter.weightedPCs { + if pc < covFilter.pcStart { + covFilter.pcStart = pc + } + if pc > covFilter.pcEnd { + covFilter.pcEnd = pc + } + } + // align + covFilter.pcStart &= ^uint32(0xf) + covFilter.pcEnd = (covFilter.pcEnd + 0xf) &^ uint32(0xf) + covFilter.pcSize = covFilter.pcEnd - covFilter.pcStart +} + +func (covFilter *CoverFilter) bitmapBytes() []byte { + // The file starts with two uint32: covFilterStart and covFilterSize, + // and a bitmap with size ((covFilterSize>>4) + 7)/8 bytes follow them. + // 8-bit = 1-byte, additional 1-byte to prevent overflow + data := make([]byte, 8+((covFilter.pcSize>>4)+7)/8) + order := binary.ByteOrder(binary.BigEndian) + if covFilter.target.LittleEndian { + order = binary.LittleEndian + } + order.PutUint32(data, covFilter.pcStart) + order.PutUint32(data[4:], covFilter.pcSize) + + bitmap := data[8:] + for pc := range covFilter.weightedPCs { + // The lowest 4-bit is dropped. + pc = (pc - covFilter.pcStart) >> 4 + bitmap[pc/8] |= (1 << (pc % 8)) + } + return data +} diff --git a/syz-manager/covfilter_test.go b/syz-manager/covfilter_test.go new file mode 100644 index 000000000..71df26845 --- /dev/null +++ b/syz-manager/covfilter_test.go @@ -0,0 +1,47 @@ +// Copyright 2020 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 + +import ( + "testing" + + "github.com/google/syzkaller/sys/targets" +) + +func TestCreateBitmap(t *testing.T) { + target := targets.Get("test", "64") + filter := &CoverFilter{ + weightedPCs: make(map[uint32]uint32), + target: target, + } + enablePCStart := uint32(0x81000002) + enablePCEnd := uint32(0x8120001d) + filter.weightedPCs[enablePCStart] = 1 + filter.weightedPCs[enablePCEnd] = 1 + + filter.detectRegion() + if filter.pcStart != 0x81000000 || + filter.pcEnd != 0x81200020 || + filter.pcSize != 0x200020 { + t.Fatalf("filte.detectReigion test failed %x %x %x", + filter.pcStart, filter.pcEnd, filter.pcSize) + } + bitmap := filter.bitmapBytes() + bitmap = bitmap[8:] + for i, byte := range bitmap { + if i == 0 { + if byte != 0x1 { + t.Fatalf("filter.bitmapByte enable PC failed") + } + } else if i == (0x20001 / 0x8) { + if byte != byte&(1<<(0x20001%0x8)) { + t.Fatalf("filter.bitmapByte enable PC failed") + } + } else { + if byte != 0x0 { + t.Fatalf("filter.bitmapByte disable PC failed") + } + } + } +} diff --git a/syz-manager/manager.go b/syz-manager/manager.go index 47cf16734..6307f2c84 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -87,6 +87,8 @@ type Manager struct { // For checking that files that we are using are not changing under us. // Maps file name to modification time. usedFiles map[string]time.Time + + coverFilterFilename string } const ( @@ -174,6 +176,11 @@ func RunManager(cfg *mgrconfig.Config) { mgr.initHTTP() // Creates HTTP server. mgr.collectUsedFiles() + mgr.coverFilterFilename, err = createCoverageFilter(mgr.cfg) + if err != nil { + log.Fatalf("failed to create coverage filter: %v", err) + } + // Create RPC server for fuzzers. mgr.serv, err = startRPCServer(mgr) if err != nil { @@ -585,6 +592,13 @@ func (mgr *Manager) runInstanceInner(index int, instanceName string) (*report.Re return nil, fmt.Errorf("failed to setup port forwarding: %v", err) } + if mgr.coverFilterFilename != "" { + _, err = inst.Copy(mgr.coverFilterFilename) + if err != nil { + return nil, fmt.Errorf("failed to copy coverage filter bitmap: %v", err) + } + } + fuzzerBin, err := inst.Copy(mgr.cfg.FuzzerBin) if err != nil { return nil, fmt.Errorf("failed to copy binary: %v", err) @@ -1023,7 +1037,7 @@ func (mgr *Manager) collectSyscallInfoUnlocked() map[string]*CallCov { return calls } -func (mgr *Manager) fuzzerConnect() ([]rpctype.RPCInput, BugFrames) { +func (mgr *Manager) fuzzerConnect() ([]rpctype.RPCInput, BugFrames, bool) { mgr.mu.Lock() defer mgr.mu.Unlock() @@ -1040,7 +1054,7 @@ func (mgr *Manager) fuzzerConnect() ([]rpctype.RPCInput, BugFrames) { for frame := range mgr.dataRaceFrames { dataRaceFrames = append(dataRaceFrames, frame) } - return corpus, BugFrames{memoryLeaks: memoryLeakFrames, dataRaces: dataRaceFrames} + return corpus, BugFrames{memoryLeaks: memoryLeakFrames, dataRaces: dataRaceFrames}, mgr.coverFilterFilename != "" } func (mgr *Manager) machineChecked(a *rpctype.CheckArgs, enabledSyscalls map[*prog.Syscall]bool) { diff --git a/syz-manager/rpc.go b/syz-manager/rpc.go index 7d595342b..87d17b72a 100644 --- a/syz-manager/rpc.go +++ b/syz-manager/rpc.go @@ -53,7 +53,7 @@ type BugFrames struct { // RPCManagerView restricts interface between RPCServer and Manager. type RPCManagerView interface { - fuzzerConnect() ([]rpctype.RPCInput, BugFrames) + fuzzerConnect() ([]rpctype.RPCInput, BugFrames, bool) machineChecked(result *rpctype.CheckArgs, enabledSyscalls map[*prog.Syscall]bool) newInput(inp rpctype.RPCInput, sign signal.Signal) bool candidateBatch(size int) []rpctype.RPCCandidate @@ -88,7 +88,7 @@ func (serv *RPCServer) Connect(a *rpctype.ConnectArgs, r *rpctype.ConnectRes) er log.Logf(1, "fuzzer %v connected", a.Name) serv.stats.vmRestarts.inc() - corpus, bugFrames := serv.mgr.fuzzerConnect() + corpus, bugFrames, enabledCoverFilter := serv.mgr.fuzzerConnect() serv.mu.Lock() defer serv.mu.Unlock() @@ -103,6 +103,7 @@ func (serv *RPCServer) Connect(a *rpctype.ConnectArgs, r *rpctype.ConnectRes) er r.EnabledCalls = serv.configEnabledSyscalls r.GitRevision = prog.GitRevision r.TargetRevision = serv.target.Revision + r.EnabledCoverFilter = enabledCoverFilter if serv.mgr.rotateCorpus() && serv.rnd.Intn(5) == 0 { // We do rotation every other time because there are no objective // proofs regarding its efficiency either way. |
