github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/cover/canonicalizer.go (about) 1 // Copyright 2023 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 cover 5 6 import ( 7 "fmt" 8 "sort" 9 10 "github.com/google/syzkaller/pkg/log" 11 "github.com/google/syzkaller/pkg/vminfo" 12 ) 13 14 type Canonicalizer struct { 15 // Map of modules stored as module name:kernel module. 16 modules map[string]*vminfo.KernelModule 17 18 // Contains a sorted list of the canonical module addresses. 19 moduleKeys []uint64 20 } 21 22 type CanonicalizerInstance struct { 23 canonical Canonicalizer 24 25 // Contains the canonicalize and decanonicalize conversion maps. 26 canonicalize *Convert 27 decanonicalize *Convert 28 } 29 30 // Contains the current conversion maps used. 31 type Convert struct { 32 conversionHash map[uint64]*canonicalizerModule 33 moduleKeys []uint64 34 } 35 36 type convertContext struct { 37 errCount int 38 errPC uint64 39 convert *Convert 40 } 41 42 // Contains the offset and final address of each module. 43 type canonicalizerModule struct { 44 offset int64 45 name string 46 endAddr uint64 47 // Discard coverage from current module. 48 // Set to true if module is not present in canonical. 49 discard bool 50 } 51 52 func NewCanonicalizer(modules []*vminfo.KernelModule, flagSignal bool) *Canonicalizer { 53 // Return if not using canonicalization. 54 if len(modules) == 0 || !flagSignal { 55 return &Canonicalizer{} 56 } 57 // Create a map of canonical module offsets by name. 58 canonicalModules := make(map[string]*vminfo.KernelModule) 59 for _, module := range modules { 60 canonicalModules[module.Name] = module 61 } 62 63 // Store sorted canonical address keys. 64 canonicalModuleKeys := make([]uint64, len(modules)) 65 setModuleKeys(canonicalModuleKeys, modules) 66 return &Canonicalizer{ 67 modules: canonicalModules, 68 moduleKeys: canonicalModuleKeys, 69 } 70 } 71 72 func (can *Canonicalizer) NewInstance(modules []*vminfo.KernelModule) *CanonicalizerInstance { 73 if can.moduleKeys == nil { 74 return &CanonicalizerInstance{} 75 } 76 // Save sorted list of module offsets. 77 moduleKeys := make([]uint64, len(modules)) 78 setModuleKeys(moduleKeys, modules) 79 80 // Create a hash between the "canonical" module addresses and each VM instance. 81 instToCanonicalMap := make(map[uint64]*canonicalizerModule) 82 canonicalToInstMap := make(map[uint64]*canonicalizerModule) 83 for _, module := range modules { 84 discard := false 85 canonicalAddr := uint64(0) 86 canonicalModule, found := can.modules[module.Name] 87 if !found || canonicalModule.Size != module.Size { 88 log.Errorf("kernel build has changed; instance module %v differs from canonical", module.Name) 89 discard = true 90 } 91 if found { 92 canonicalAddr = canonicalModule.Addr 93 } 94 95 instAddr := module.Addr 96 97 canonicalToInstMap[canonicalAddr] = &canonicalizerModule{ 98 offset: int64(instAddr - canonicalAddr), 99 name: module.Name, 100 endAddr: module.Size + canonicalAddr, 101 discard: discard, 102 } 103 104 instToCanonicalMap[instAddr] = &canonicalizerModule{ 105 offset: int64(canonicalAddr - instAddr), 106 name: module.Name, 107 endAddr: module.Size + instAddr, 108 discard: discard, 109 } 110 } 111 112 return &CanonicalizerInstance{ 113 canonical: *can, 114 canonicalize: &Convert{ 115 conversionHash: instToCanonicalMap, 116 moduleKeys: moduleKeys, 117 }, 118 decanonicalize: &Convert{ 119 conversionHash: canonicalToInstMap, 120 moduleKeys: can.moduleKeys, 121 }, 122 } 123 } 124 125 func (ci *CanonicalizerInstance) Canonicalize(elems []uint64) []uint64 { 126 return ci.canonicalize.convertPCs(elems) 127 } 128 129 func (ci *CanonicalizerInstance) Decanonicalize(elems []uint64) []uint64 { 130 return ci.decanonicalize.convertPCs(elems) 131 } 132 133 // Store sorted list of addresses. Used to binary search when converting PCs. 134 func setModuleKeys(moduleKeys []uint64, modules []*vminfo.KernelModule) { 135 for idx, module := range modules { 136 moduleKeys[idx] = module.Addr 137 } 138 139 // Sort modules by address. 140 sort.Slice(moduleKeys, func(i, j int) bool { return moduleKeys[i] < moduleKeys[j] }) 141 } 142 143 func findModule(pc uint64, moduleKeys []uint64) (moduleIdx int) { 144 moduleIdx, _ = sort.Find(len(moduleKeys), func(moduleIdx int) int { 145 if pc < moduleKeys[moduleIdx] { 146 return -1 147 } 148 return +1 149 }) 150 // Sort.Find returns the index above the correct module. 151 return moduleIdx - 1 152 } 153 154 func (convert *Convert) convertPCs(pcs []uint64) []uint64 { 155 if convert == nil { 156 return pcs 157 } 158 var ret []uint64 159 convCtx := &convertContext{convert: convert} 160 for _, pc := range pcs { 161 if newPC, ok := convert.convertPC(pc); ok { 162 ret = append(ret, newPC) 163 } else { 164 convCtx.discard(pc) 165 } 166 } 167 if msg := convCtx.discarded(); msg != "" { 168 log.Logf(4, "error in PC/signal conversion: %v", msg) 169 } 170 return ret 171 } 172 173 func (convert *Convert) convertPC(pc uint64) (uint64, bool) { 174 moduleIdx := findModule(pc, convert.moduleKeys) 175 // Check if address is above the first module offset. 176 if moduleIdx >= 0 { 177 module, found := convert.conversionHash[convert.moduleKeys[moduleIdx]] 178 if !found { 179 return pc, false 180 } 181 // If the address is within the found module add the offset. 182 if pc < module.endAddr { 183 if module.discard { 184 return pc, false 185 } 186 if module.name != "" { 187 pc = uint64(int64(pc) + module.offset) 188 } 189 } 190 } 191 return pc, true 192 } 193 194 func (cc *convertContext) discarded() string { 195 if cc.errCount == 0 { 196 return "" 197 } 198 errMsg := fmt.Sprintf("discarded 0x%x (and %v other PCs) during conversion", cc.errPC, cc.errCount) 199 return fmt.Sprintf("%v; not found in module map", errMsg) 200 } 201 202 func (cc *convertContext) discard(pc uint64) { 203 cc.errCount += 1 204 if cc.errPC == 0 { 205 cc.errPC = pc 206 } 207 }