aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--executor/cov_filter.h58
-rw-r--r--executor/executor.cc11
-rw-r--r--executor/test.h34
-rw-r--r--pkg/cover/report.go62
-rw-r--r--pkg/ipc/ipc.go13
-rw-r--r--pkg/mgrconfig/config.go16
-rw-r--r--pkg/rpctype/rpctype.go15
-rw-r--r--syz-fuzzer/fuzzer.go4
-rw-r--r--syz-manager/covfilter.go213
-rw-r--r--syz-manager/covfilter_test.go47
-rw-r--r--syz-manager/manager.go18
-rw-r--r--syz-manager/rpc.go5
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.