aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/manager/covfilter.go140
-rw-r--r--pkg/manager/report_generator.go70
-rw-r--r--pkg/mgrconfig/config.go4
-rw-r--r--pkg/mgrconfig/load.go4
4 files changed, 214 insertions, 4 deletions
diff --git a/pkg/manager/covfilter.go b/pkg/manager/covfilter.go
new file mode 100644
index 000000000..420eb40b6
--- /dev/null
+++ b/pkg/manager/covfilter.go
@@ -0,0 +1,140 @@
+// 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 manager
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "regexp"
+ "sort"
+ "strconv"
+
+ "github.com/google/syzkaller/pkg/cover/backend"
+ "github.com/google/syzkaller/pkg/log"
+ "github.com/google/syzkaller/pkg/mgrconfig"
+)
+
+func CreateCoverageFilter(source *ReportGeneratorWrapper, covCfg mgrconfig.CovFilterCfg) ([]uint64,
+ map[uint64]struct{}, error) {
+ if covCfg.Empty() {
+ return nil, nil, nil
+ }
+ rg, err := source.Get()
+ if err != nil {
+ return nil, nil, err
+ }
+ pcs := make(map[uint64]struct{})
+ foreachSymbol := func(apply func(*backend.ObjectUnit)) {
+ for _, sym := range rg.Symbols {
+ apply(&sym.ObjectUnit)
+ }
+ }
+ if err := covFilterAddFilter(pcs, covCfg.Functions, foreachSymbol); err != nil {
+ return nil, nil, err
+ }
+ foreachUnit := func(apply func(*backend.ObjectUnit)) {
+ for _, unit := range rg.Units {
+ apply(&unit.ObjectUnit)
+ }
+ }
+ if err := covFilterAddFilter(pcs, covCfg.Files, foreachUnit); err != nil {
+ return nil, nil, err
+ }
+ if err := covFilterAddRawPCs(pcs, covCfg.RawPCs); err != nil {
+ return nil, nil, err
+ }
+ // Copy pcs into execPCs. This is used to filter coverage in the executor.
+ execPCs := make([]uint64, 0, len(pcs))
+ for pc := range pcs {
+ execPCs = append(execPCs, pc)
+ }
+ // PCs from CMPs are deleted to calculate `filtered coverage` statistics.
+ for _, sym := range rg.Symbols {
+ for _, pc := range sym.CMPs {
+ delete(pcs, pc)
+ }
+ }
+ return execPCs, pcs, nil
+}
+
+func covFilterAddFilter(pcs map[uint64]struct{}, filters []string, foreach func(func(*backend.ObjectUnit))) error {
+ res, err := compileRegexps(filters)
+ if err != nil {
+ return err
+ }
+ used := make(map[*regexp.Regexp][]string)
+ foreach(func(unit *backend.ObjectUnit) {
+ for _, re := range res {
+ if re.MatchString(unit.Name) {
+ // We add both coverage points and comparison interception points
+ // because executor filters comparisons as well.
+ for _, pc := range unit.PCs {
+ pcs[pc] = struct{}{}
+ }
+ for _, pc := range unit.CMPs {
+ pcs[pc] = struct{}{}
+ }
+ used[re] = append(used[re], unit.Name)
+ break
+ }
+ }
+ })
+ for _, re := range res {
+ sort.Strings(used[re])
+ log.Logf(0, "coverage filter: %v: %v", re, used[re])
+ }
+ if len(res) != len(used) {
+ return fmt.Errorf("some filters don't match anything")
+ }
+ return nil
+}
+
+func covFilterAddRawPCs(pcs map[uint64]struct{}, rawPCsFiles []string) error {
+ re := regexp.MustCompile(`(0x[0-9a-f]+)(?:: (0x[0-9a-f]+))?`)
+ for _, f := range rawPCsFiles {
+ rawFile, err := os.Open(f)
+ if err != nil {
+ return fmt.Errorf("failed to open raw PCs file: %w", err)
+ }
+ defer rawFile.Close()
+ s := bufio.NewScanner(rawFile)
+ 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
+ }
+ _ = weight // currently unused
+ pcs[pc] = struct{}{}
+ }
+ if err := s.Err(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func compileRegexps(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: %w", err)
+ }
+ regexps = append(regexps, r)
+ }
+ return regexps, nil
+}
diff --git a/pkg/manager/report_generator.go b/pkg/manager/report_generator.go
new file mode 100644
index 000000000..b3d293ef1
--- /dev/null
+++ b/pkg/manager/report_generator.go
@@ -0,0 +1,70 @@
+// Copyright 2015 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 manager
+
+import (
+ "fmt"
+ "sync"
+
+ "github.com/google/syzkaller/pkg/cover"
+ "github.com/google/syzkaller/pkg/cover/backend"
+ "github.com/google/syzkaller/pkg/log"
+ "github.com/google/syzkaller/pkg/mgrconfig"
+ "github.com/google/syzkaller/pkg/vminfo"
+)
+
+type ReportGeneratorWrapper struct {
+ cfg *mgrconfig.Config
+ modules []*vminfo.KernelModule
+
+ mu sync.Mutex
+ initialized bool
+ cached *cover.ReportGenerator
+}
+
+func ReportGeneratorCache(cfg *mgrconfig.Config) *ReportGeneratorWrapper {
+ return &ReportGeneratorWrapper{cfg: cfg}
+}
+
+func (w *ReportGeneratorWrapper) Get() (*cover.ReportGenerator, error) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ if !w.initialized {
+ return nil, fmt.Errorf("report generator creation before Init() is called")
+ }
+ if w.cached == nil {
+ log.Logf(0, "initializing coverage information...")
+ rg, err := cover.MakeReportGenerator(w.cfg, w.cfg.KernelSubsystem, w.modules, w.cfg.RawCover)
+ if err != nil {
+ return nil, err
+ }
+ w.cached = rg
+ }
+ return w.cached, nil
+}
+
+func (w *ReportGeneratorWrapper) Init(modules []*vminfo.KernelModule) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ if w.initialized {
+ panic("Init() called twice")
+ }
+ w.initialized = true
+ w.modules = modules
+}
+
+func (w *ReportGeneratorWrapper) Reset() {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ w.cached = nil
+}
+
+func CoverToPCs(cfg *mgrconfig.Config, cov []uint64) []uint64 {
+ pcs := make([]uint64, 0, len(cov))
+ for _, pc := range cov {
+ prev := backend.PreviousInstructionPC(cfg.SysTarget, cfg.Type, pc)
+ pcs = append(pcs, prev)
+ }
+ return pcs
+}
diff --git a/pkg/mgrconfig/config.go b/pkg/mgrconfig/config.go
index 2ea0d8ad9..f4857e613 100644
--- a/pkg/mgrconfig/config.go
+++ b/pkg/mgrconfig/config.go
@@ -152,7 +152,7 @@ type Config struct {
// "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,omitempty"`
+ CovFilter CovFilterCfg `json:"cover_filter,omitempty"`
// For each prog in the corpus, remember the raw array of PCs obtained from the kernel.
// It can be useful for debugging syzkaller descriptions and syzkaller itself.
@@ -242,7 +242,7 @@ type Subsystem struct {
Paths []string `json:"path"`
}
-type covFilterCfg struct {
+type CovFilterCfg struct {
Files []string `json:"files,omitempty"`
Functions []string `json:"functions,omitempty"`
RawPCs []string `json:"pcs,omitempty"`
diff --git a/pkg/mgrconfig/load.go b/pkg/mgrconfig/load.go
index aa22b3ea8..76a0bcf14 100644
--- a/pkg/mgrconfig/load.go
+++ b/pkg/mgrconfig/load.go
@@ -260,8 +260,8 @@ func checkNonEmpty(fields ...string) error {
return nil
}
-func (cfg *Config) HasCovFilter() bool {
- return len(cfg.CovFilter.Functions)+len(cfg.CovFilter.Files)+len(cfg.CovFilter.RawPCs) != 0
+func (cov *CovFilterCfg) Empty() bool {
+ return len(cov.Functions)+len(cov.Files)+len(cov.RawPCs) == 0
}
func (cfg *Config) CompleteKernelDirs() {