github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/manager/covfilter.go (about) 1 // Copyright 2020 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package manager 5 6 import ( 7 "bufio" 8 "fmt" 9 "os" 10 "regexp" 11 "sort" 12 "strconv" 13 14 "github.com/google/syzkaller/pkg/corpus" 15 "github.com/google/syzkaller/pkg/cover/backend" 16 "github.com/google/syzkaller/pkg/log" 17 "github.com/google/syzkaller/pkg/mgrconfig" 18 ) 19 20 func CoverageFilter(source *ReportGeneratorWrapper, covCfg mgrconfig.CovFilterCfg, 21 strict bool) (map[uint64]struct{}, error) { 22 if covCfg.Empty() { 23 return nil, nil 24 } 25 rg, err := source.Get() 26 if err != nil { 27 return nil, err 28 } 29 pcs := make(map[uint64]struct{}) 30 foreachSymbol := func(apply func(*backend.ObjectUnit)) { 31 for _, sym := range rg.Symbols { 32 apply(&sym.ObjectUnit) 33 } 34 } 35 if err := covFilterAddFilter(pcs, covCfg.Functions, foreachSymbol, strict); err != nil { 36 return nil, err 37 } 38 foreachUnit := func(apply func(*backend.ObjectUnit)) { 39 for _, unit := range rg.Units { 40 apply(&unit.ObjectUnit) 41 } 42 } 43 if err := covFilterAddFilter(pcs, covCfg.Files, foreachUnit, strict); err != nil { 44 return nil, err 45 } 46 if err := covFilterAddRawPCs(pcs, covCfg.RawPCs); err != nil { 47 return nil, err 48 } 49 // Note that pcs may include both comparison and block/edge coverage callbacks. 50 return pcs, nil 51 } 52 53 func covFilterAddFilter(pcs map[uint64]struct{}, filters []string, foreach func(func(*backend.ObjectUnit)), 54 strict bool) error { 55 res, err := compileRegexps(filters) 56 if err != nil { 57 return err 58 } 59 used := make(map[*regexp.Regexp][]string) 60 foreach(func(unit *backend.ObjectUnit) { 61 for _, re := range res { 62 if re.MatchString(unit.Name) { 63 // We add both coverage points and comparison interception points 64 // because executor filters comparisons as well. 65 for _, pc := range unit.PCs { 66 pcs[pc] = struct{}{} 67 } 68 for _, pc := range unit.CMPs { 69 pcs[pc] = struct{}{} 70 } 71 used[re] = append(used[re], unit.Name) 72 break 73 } 74 } 75 }) 76 for _, re := range res { 77 sort.Strings(used[re]) 78 log.Logf(0, "coverage filter: %v: %v", re, used[re]) 79 } 80 if strict && len(res) != len(used) { 81 return fmt.Errorf("some filters don't match anything") 82 } 83 return nil 84 } 85 86 func covFilterAddRawPCs(pcs map[uint64]struct{}, rawPCsFiles []string) error { 87 re := regexp.MustCompile(`(0x[0-9a-f]+)(?:: (0x[0-9a-f]+))?`) 88 for _, f := range rawPCsFiles { 89 rawFile, err := os.Open(f) 90 if err != nil { 91 return fmt.Errorf("failed to open raw PCs file: %w", err) 92 } 93 defer rawFile.Close() 94 s := bufio.NewScanner(rawFile) 95 for s.Scan() { 96 match := re.FindStringSubmatch(s.Text()) 97 if match == nil { 98 return fmt.Errorf("bad line: %q", s.Text()) 99 } 100 pc, err := strconv.ParseUint(match[1], 0, 64) 101 if err != nil { 102 return err 103 } 104 weight, err := strconv.ParseUint(match[2], 0, 32) 105 if match[2] != "" && err != nil { 106 return err 107 } 108 // If no weight is detected, set the weight to 0x1 by default. 109 if match[2] == "" || weight < 1 { 110 weight = 1 111 } 112 _ = weight // currently unused 113 pcs[pc] = struct{}{} 114 } 115 if err := s.Err(); err != nil { 116 return err 117 } 118 } 119 return nil 120 } 121 122 func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) { 123 var regexps []*regexp.Regexp 124 for _, rs := range regexpStrings { 125 r, err := regexp.Compile(rs) 126 if err != nil { 127 return nil, fmt.Errorf("failed to compile regexp: %w", err) 128 } 129 regexps = append(regexps, r) 130 } 131 return regexps, nil 132 } 133 134 type CoverageFilters struct { 135 Areas []corpus.FocusArea 136 ExecutorFilter map[uint64]struct{} 137 } 138 139 func PrepareCoverageFilters(source *ReportGeneratorWrapper, cfg *mgrconfig.Config, 140 strict bool) (CoverageFilters, error) { 141 var ret CoverageFilters 142 needExecutorFilter := len(cfg.Experimental.FocusAreas) > 0 143 for _, area := range cfg.Experimental.FocusAreas { 144 pcs, err := CoverageFilter(source, area.Filter, strict) 145 if err != nil { 146 return ret, err 147 } 148 // KCOV will point to the next instruction, so we need to adjust the map. 149 covPCs := make(map[uint64]struct{}) 150 for pc := range pcs { 151 next := backend.NextInstructionPC(cfg.SysTarget, cfg.Type, pc) 152 covPCs[next] = struct{}{} 153 } 154 ret.Areas = append(ret.Areas, corpus.FocusArea{ 155 Name: area.Name, 156 CoverPCs: covPCs, 157 Weight: area.Weight, 158 }) 159 if area.Filter.Empty() { 160 // An empty cover filter indicates that the user is interested in all the coverage. 161 needExecutorFilter = false 162 } 163 } 164 if needExecutorFilter { 165 ret.ExecutorFilter = map[uint64]struct{}{} 166 for _, area := range ret.Areas { 167 for pc := range area.CoverPCs { 168 ret.ExecutorFilter[pc] = struct{}{} 169 } 170 } 171 } 172 return ret, nil 173 }