aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2024-10-24 10:22:25 +0200
committerTaras Madan <tarasmadan@google.com>2024-10-25 12:08:02 +0000
commit9a199dec544ca9f1560a7352d0d003cf9206d8c5 (patch)
tree589d3c30946513dc5d1349d781ef82b67dcd4279 /pkg
parent479141f703a43adab07cba7f8b3c99399fbbeb68 (diff)
pkg/mgrconfig, syz-manager: support focus areas
Switch from the CoverageFilter to the more flexible mechanism of focus areas.
Diffstat (limited to 'pkg')
-rw-r--r--pkg/manager/covfilter.go68
-rw-r--r--pkg/mgrconfig/config.go39
-rw-r--r--pkg/mgrconfig/load.go38
3 files changed, 118 insertions, 27 deletions
diff --git a/pkg/manager/covfilter.go b/pkg/manager/covfilter.go
index 420eb40b6..03f3064af 100644
--- a/pkg/manager/covfilter.go
+++ b/pkg/manager/covfilter.go
@@ -11,19 +11,19 @@ import (
"sort"
"strconv"
+ "github.com/google/syzkaller/pkg/corpus"
"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) {
+func CoverageFilter(source *ReportGeneratorWrapper, covCfg mgrconfig.CovFilterCfg) (map[uint64]struct{}, error) {
if covCfg.Empty() {
- return nil, nil, nil
+ return nil, nil
}
rg, err := source.Get()
if err != nil {
- return nil, nil, err
+ return nil, err
}
pcs := make(map[uint64]struct{})
foreachSymbol := func(apply func(*backend.ObjectUnit)) {
@@ -32,7 +32,7 @@ func CreateCoverageFilter(source *ReportGeneratorWrapper, covCfg mgrconfig.CovFi
}
}
if err := covFilterAddFilter(pcs, covCfg.Functions, foreachSymbol); err != nil {
- return nil, nil, err
+ return nil, err
}
foreachUnit := func(apply func(*backend.ObjectUnit)) {
for _, unit := range rg.Units {
@@ -40,23 +40,13 @@ func CreateCoverageFilter(source *ReportGeneratorWrapper, covCfg mgrconfig.CovFi
}
}
if err := covFilterAddFilter(pcs, covCfg.Files, foreachUnit); err != nil {
- return nil, nil, err
+ return nil, err
}
if err := covFilterAddRawPCs(pcs, covCfg.RawPCs); err != nil {
- return nil, nil, err
+ return 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
+ // Note that pcs may include both comparison and block/edge coverage callbacks.
+ return pcs, nil
}
func covFilterAddFilter(pcs map[uint64]struct{}, filters []string, foreach func(func(*backend.ObjectUnit))) error {
@@ -138,3 +128,43 @@ func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) {
}
return regexps, nil
}
+
+type CoverageFilters struct {
+ Areas []corpus.FocusArea
+ ExecutorFilter map[uint64]struct{}
+}
+
+func PrepareCoverageFilters(source *ReportGeneratorWrapper, cfg *mgrconfig.Config) (CoverageFilters, error) {
+ var ret CoverageFilters
+ needExecutorFilter := len(cfg.Experimental.FocusAreas) > 0
+ for _, area := range cfg.Experimental.FocusAreas {
+ pcs, err := CoverageFilter(source, area.Filter)
+ if err != nil {
+ return ret, err
+ }
+ // KCOV will point to the next instruction, so we need to adjust the map.
+ covPCs := make(map[uint64]struct{})
+ for pc := range pcs {
+ next := backend.NextInstructionPC(cfg.SysTarget, cfg.Type, pc)
+ covPCs[next] = struct{}{}
+ }
+ ret.Areas = append(ret.Areas, corpus.FocusArea{
+ Name: area.Name,
+ CoverPCs: covPCs,
+ Weight: area.Weight,
+ })
+ if area.Filter.Empty() {
+ // An empty cover filter indicates that the user is interested in all the coverage.
+ needExecutorFilter = false
+ }
+ }
+ if needExecutorFilter {
+ ret.ExecutorFilter = map[uint64]struct{}{}
+ for _, area := range ret.Areas {
+ for pc := range area.CoverPCs {
+ ret.ExecutorFilter[pc] = struct{}{}
+ }
+ }
+ }
+ return ret, nil
+}
diff --git a/pkg/mgrconfig/config.go b/pkg/mgrconfig/config.go
index f4857e613..b475c4eed 100644
--- a/pkg/mgrconfig/config.go
+++ b/pkg/mgrconfig/config.go
@@ -144,14 +144,9 @@ 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 used to restrict the area of the kernel visible to syzkaller.
+ // DEPRECATED! Use the FocusAreas parameter instead.
CovFilter CovFilterCfg `json:"cover_filter,omitempty"`
// For each prog in the corpus, remember the raw array of PCs obtained from the kernel.
@@ -235,6 +230,34 @@ type Experimental struct {
// Use automatically (auto) generated or manually (manual) written descriptions or any (any) (default: manual)
DescriptionsMode string `json:"descriptions_mode"`
+
+ // FocusAreas configures what attention syzkaller should pay to the specific areas of the kernel.
+ // The probability of selecting a program from an area is at least `Weight / sum of weights`.
+ // If FocusAreas is non-empty, by default all kernel code not covered by any filter will be ignored.
+ // To focus fuzzing on some areas, but to consider the rest of the code as well, add a record
+ // with an empty Filter, but non-empty weight.
+ // E.g. "focus_areas": [ {"filter": {"files": ["^net"]}, "weight": 10.0}, {"weight": 1.0"} ].
+ FocusAreas []FocusArea `json:"focus_areas,omitempty"`
+}
+
+type FocusArea struct {
+ // Name allows to display detailed statistics for every focus area.
+ Name string `json:"name"`
+
+ // A coverage filter.
+ // Supported filter types:
+ // "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"
+ // If empty, it's assumed to match the whole kernel.
+ Filter CovFilterCfg `json:"filter,omitempty"`
+
+ // Weight is a positive number that determines how much focus should be put on this area.
+ Weight float64 `json:"weight"`
}
type Subsystem struct {
diff --git a/pkg/mgrconfig/load.go b/pkg/mgrconfig/load.go
index 792802e60..4529f8ec0 100644
--- a/pkg/mgrconfig/load.go
+++ b/pkg/mgrconfig/load.go
@@ -198,6 +198,9 @@ func Complete(cfg *Config) error {
if err != nil {
return err
}
+ if err := cfg.completeFocusAreas(); err != nil {
+ return err
+ }
cfg.initTimeouts()
cfg.VMLess = cfg.Type == "none"
return nil
@@ -324,6 +327,41 @@ func (cfg *Config) completeBinaries() error {
return nil
}
+func (cfg *Config) completeFocusAreas() error {
+ names := map[string]bool{}
+ seenEmptyFilter := false
+ for i, area := range cfg.Experimental.FocusAreas {
+ if area.Name != "" {
+ if names[area.Name] {
+ return fmt.Errorf("duplicate focus area name: %q", area.Name)
+ }
+ names[area.Name] = true
+ }
+ if area.Weight <= 0 {
+ return fmt.Errorf("focus area #%d: negative weight", i)
+ }
+ if area.Filter.Empty() {
+ if seenEmptyFilter {
+ return fmt.Errorf("there must be only one focus area with an empty filter")
+ }
+ seenEmptyFilter = true
+ }
+ }
+ if !cfg.CovFilter.Empty() {
+ if len(cfg.Experimental.FocusAreas) > 0 {
+ return fmt.Errorf("you cannot use both cov_filter and focus_areas")
+ }
+ cfg.Experimental.FocusAreas = []FocusArea{
+ {
+ Name: "filtered",
+ Filter: cfg.CovFilter,
+ Weight: 1.0,
+ },
+ }
+ }
+ return nil
+}
+
func splitTarget(target string) (string, string, string, error) {
if target == "" {
return "", "", "", fmt.Errorf("target is empty")