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