github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/cover/canonicalizer_test.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 // Tests the translation of coverage pcs between fuzzer instances with differing module offsets. 5 6 package cover 7 8 import ( 9 "fmt" 10 "reflect" 11 "strconv" 12 "testing" 13 14 "github.com/google/syzkaller/pkg/vminfo" 15 ) 16 17 type RPCServer struct { 18 canonicalModules *Canonicalizer 19 modulesInitialized bool 20 fuzzers map[string]*Fuzzer 21 } 22 23 type Fuzzer struct { 24 instModules *CanonicalizerInstance 25 cov []uint64 26 goalCov []uint64 27 bitmap []uint64 28 goalBitmap []uint64 29 sign []uint64 30 goalSign []uint64 31 } 32 33 type canonicalizeValue int 34 35 const ( 36 Canonicalize canonicalizeValue = iota 37 Decanonicalize 38 ) 39 40 // Confirms there is no change to coverage if modules aren't instantiated. 41 func TestNilModules(t *testing.T) { 42 serv := &RPCServer{ 43 fuzzers: make(map[string]*Fuzzer), 44 } 45 serv.connect("f1", nil, true) 46 serv.connect("f2", nil, true) 47 48 serv.fuzzers["f1"].cov = []uint64{0x00010000, 0x00020000, 0x00030000, 0x00040000} 49 serv.fuzzers["f1"].goalCov = []uint64{0x00010000, 0x00020000, 0x00030000, 0x00040000} 50 51 serv.fuzzers["f2"].cov = []uint64{0x00010000, 0x00020000, 0x00030000, 0x00040000} 52 serv.fuzzers["f2"].goalCov = []uint64{0x00010000, 0x00020000, 0x00030000, 0x00040000} 53 54 serv.fuzzers["f1"].bitmap = []uint64{ 55 0x00010011, 56 0x00020FFF, 57 0x00030000, 58 0x00040000, 59 } 60 serv.fuzzers["f1"].goalBitmap = []uint64{ 61 0x00010011, 62 0x00020FFF, 63 0x00030000, 64 0x00040000, 65 } 66 serv.fuzzers["f2"].bitmap = []uint64{ 67 0x00010011, 68 0x00020FFF, 69 0x00030000, 70 0x00040000, 71 } 72 serv.fuzzers["f2"].goalBitmap = []uint64{ 73 0x00010011, 74 0x00020FFF, 75 0x00030000, 76 0x00040000, 77 } 78 79 if err := serv.runTest(Canonicalize); err != "" { 80 t.Fatalf("failed in canonicalization: %v", err) 81 } 82 83 serv.fuzzers["f1"].goalCov = []uint64{0x00010000, 0x00020000, 0x00030000, 0x00040000} 84 serv.fuzzers["f1"].goalSign = serv.fuzzers["f1"].goalCov 85 serv.fuzzers["f2"].goalCov = []uint64{0x00010000, 0x00020000, 0x00030000, 0x00040000} 86 serv.fuzzers["f2"].goalSign = serv.fuzzers["f2"].goalCov 87 if err := serv.runTest(Decanonicalize); err != "" { 88 t.Fatalf("failed in decanonicalization: %v", err) 89 } 90 } 91 92 // Confirms there is no change to PCs if coverage is disabled and fallback signals are used. 93 func TestDisabledSignals(t *testing.T) { 94 serv := &RPCServer{ 95 fuzzers: make(map[string]*Fuzzer), 96 } 97 // Create modules at the specified address offsets. 98 f1ModuleAddresses := []uint64{0x00015000, 0x00020000, 0x00030000, 0x00040000, 0x00045000} 99 f1ModuleSizes := []uint64{0x5000, 0x5000, 0x10000, 0x5000, 0x10000} 100 f1Modules := initModules(f1ModuleAddresses, f1ModuleSizes) 101 serv.connect("f1", f1Modules, false) 102 103 f2ModuleAddresses := []uint64{0x00015000, 0x00040000, 0x00045000, 0x00020000, 0x00030000} 104 f2ModuleSizes := []uint64{0x5000, 0x5000, 0x10000, 0x5000, 0x10000} 105 f2Modules := initModules(f2ModuleAddresses, f2ModuleSizes) 106 serv.connect("f2", f2Modules, false) 107 108 pcs := []uint64{0x00010000, 0x00020000, 0x00030000, 0x00040000} 109 serv.fuzzers["f1"].cov = pcs 110 serv.fuzzers["f1"].goalCov = pcs 111 112 serv.fuzzers["f2"].sign = pcs 113 serv.fuzzers["f2"].goalSign = pcs 114 115 if err := serv.runTest(Canonicalize); err != "" { 116 t.Fatalf("failed in canonicalization: %v", err) 117 } 118 119 serv.fuzzers["f1"].goalSign = pcs 120 serv.fuzzers["f2"].goalSign = pcs 121 if err := serv.runTest(Decanonicalize); err != "" { 122 t.Fatalf("failed in decanonicalization: %v", err) 123 } 124 } 125 126 // Tests coverage conversion when modules are instantiated. 127 func TestModules(t *testing.T) { 128 serv := &RPCServer{ 129 fuzzers: make(map[string]*Fuzzer), 130 } 131 132 // Create modules at the specified address offsets. 133 f1ModuleAddresses := []uint64{0x00015000, 0x00020000, 0x00030000, 0x00040000, 0x00045000} 134 f1ModuleSizes := []uint64{0x5000, 0x5000, 0x10000, 0x5000, 0x10000} 135 f1Modules := initModules(f1ModuleAddresses, f1ModuleSizes) 136 serv.connect("f1", f1Modules, true) 137 138 f2ModuleAddresses := []uint64{0x00015000, 0x00040000, 0x00045000, 0x00020000, 0x00030000} 139 f2ModuleSizes := []uint64{0x5000, 0x5000, 0x10000, 0x5000, 0x10000} 140 f2Modules := initModules(f2ModuleAddresses, f2ModuleSizes) 141 serv.connect("f2", f2Modules, true) 142 143 // f1 is the "canonical" fuzzer as it is first one instantiated. 144 // This means that all coverage output should be the same as the inputs. 145 serv.fuzzers["f1"].cov = []uint64{0x00010000, 0x00015000, 0x00020000, 0x00025000, 0x00030000, 146 0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000} 147 serv.fuzzers["f1"].goalCov = []uint64{0x00010000, 0x00015000, 0x00020000, 0x00025000, 0x00030000, 148 0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000} 149 150 // The modules addresss are inverted between: (2 and 4), (3 and 5), 151 // affecting the output canonical coverage values in these ranges. 152 serv.fuzzers["f2"].cov = []uint64{0x00010000, 0x00015000, 0x00020000, 0x00025000, 0x00030000, 153 0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000} 154 serv.fuzzers["f2"].goalCov = []uint64{0x00010000, 0x00015000, 0x00040000, 0x00025000, 0x00045000, 155 0x0004a000, 0x00020000, 0x00030000, 0x0003b000, 0x00055000} 156 157 serv.fuzzers["f1"].bitmap = []uint64{ 158 0x00010011, 159 0x00020FFF, 160 0x00030000, 161 0x00040000, 162 } 163 serv.fuzzers["f1"].goalBitmap = []uint64{ 164 0x00010011, 165 0x00020FFF, 166 0x00030000, 167 0x00040000, 168 } 169 serv.fuzzers["f2"].bitmap = []uint64{ 170 0x00010011, 171 0x00020FFF, 172 0x00030000, 173 0x00040000, 174 } 175 serv.fuzzers["f2"].goalBitmap = []uint64{ 176 0x00010011, 177 0x00040FFF, 178 0x00045000, 179 0x00020000, 180 } 181 182 if err := serv.runTest(Canonicalize); err != "" { 183 t.Fatalf("failed in canonicalization: %v", err) 184 } 185 186 serv.fuzzers["f1"].goalCov = []uint64{0x00010000, 0x00015000, 0x00020000, 0x00025000, 0x00030000, 187 0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000} 188 serv.fuzzers["f2"].goalCov = []uint64{0x00010000, 0x00015000, 0x00020000, 0x00025000, 0x00030000, 189 0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000} 190 if err := serv.runTest(Decanonicalize); err != "" { 191 t.Fatalf("failed in decanonicalization: %v", err) 192 } 193 } 194 195 // Tests coverage conversion when modules are added after initialization. 196 func TestChangingModules(t *testing.T) { 197 serv := &RPCServer{ 198 fuzzers: make(map[string]*Fuzzer), 199 } 200 201 // Create modules at the specified address offsets. 202 f1ModuleAddresses := []uint64{0x00015000} 203 f1ModuleSizes := []uint64{0x5000} 204 f1Modules := initModules(f1ModuleAddresses, f1ModuleSizes) 205 serv.connect("f1", f1Modules, true) 206 207 f2ModuleAddresses := []uint64{0x00015000, 0x00020000} 208 f2ModuleSizes := []uint64{0x5000, 0x5000} 209 f2Modules := initModules(f2ModuleAddresses, f2ModuleSizes) 210 serv.connect("f2", f2Modules, true) 211 212 // Module 2 is not present in the "canonical" fuzzer, so coverage values 213 // in this range should be deleted. 214 serv.fuzzers["f2"].cov = []uint64{0x00010000, 0x00015000, 0x00020000, 0x00025000} 215 serv.fuzzers["f2"].goalCov = []uint64{0x00010000, 0x00015000, 0x00025000} 216 217 if err := serv.runTest(Canonicalize); err != "" { 218 t.Fatalf("failed in canonicalization: %v", err) 219 } 220 221 serv.fuzzers["f2"].goalCov = []uint64{0x00010000, 0x00015000, 0x00025000} 222 if err := serv.runTest(Decanonicalize); err != "" { 223 t.Fatalf("failed in decanonicalization: %v", err) 224 } 225 } 226 227 func (serv *RPCServer) runTest(val canonicalizeValue) string { 228 var cov []uint64 229 for name, fuzzer := range serv.fuzzers { 230 if val == Canonicalize { 231 cov = fuzzer.instModules.Canonicalize(fuzzer.cov) 232 } else { 233 cov = fuzzer.instModules.Decanonicalize(fuzzer.cov) 234 instBitmap := fuzzer.instModules.Decanonicalize(fuzzer.bitmap) 235 if !reflect.DeepEqual(instBitmap, fuzzer.goalBitmap) { 236 return fmt.Sprintf("failed in bitmap conversion. Fuzzer %v.\nExpected: 0x%x.\nReturned: 0x%x", 237 name, fuzzer.goalBitmap, instBitmap) 238 } 239 } 240 if !reflect.DeepEqual(cov, fuzzer.goalCov) { 241 return fmt.Sprintf("failed in coverage conversion. Fuzzer %v.\nExpected: 0x%x.\nReturned: 0x%x", 242 name, fuzzer.goalCov, cov) 243 } 244 fuzzer.cov = cov 245 } 246 return "" 247 } 248 249 func (serv *RPCServer) connect(name string, modules []*vminfo.KernelModule, flagSignal bool) { 250 if !serv.modulesInitialized { 251 serv.canonicalModules = NewCanonicalizer(modules, flagSignal) 252 serv.modulesInitialized = true 253 } 254 255 serv.fuzzers[name] = &Fuzzer{ 256 instModules: serv.canonicalModules.NewInstance(modules), 257 } 258 } 259 260 func initModules(addrs, sizes []uint64) []*vminfo.KernelModule { 261 var modules []*vminfo.KernelModule 262 for idx, addr := range addrs { 263 modules = append(modules, &vminfo.KernelModule{ 264 Name: strconv.FormatInt(int64(idx), 10), 265 Addr: addr, 266 Size: sizes[idx], 267 }) 268 } 269 return modules 270 }