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