github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/prog/hints_test.go (about) 1 // Copyright 2017 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 prog 5 6 import ( 7 "encoding/hex" 8 "fmt" 9 "math/rand" 10 "reflect" 11 "sort" 12 "strings" 13 "testing" 14 15 "github.com/google/go-cmp/cmp" 16 "github.com/google/syzkaller/pkg/image" 17 "github.com/stretchr/testify/assert" 18 ) 19 20 type ConstArgTest struct { 21 name string 22 in uint64 23 size uint64 24 bitsize uint64 25 comps CompMap 26 res []uint64 27 } 28 29 type DataArgTest struct { 30 name string 31 in string 32 comps CompMap 33 res map[string]bool 34 } 35 36 // Tests checkConstArg(). Is not intended to check correctness of any mutations. 37 // Mutation are checked in their own tests. 38 func TestHintsCheckConstArg(t *testing.T) { 39 target := initTargetTest(t, "test", "64") 40 var tests = []ConstArgTest{ 41 { 42 name: "one-replacer-test", 43 in: 0xdeadbeef, 44 size: 4, 45 comps: CompMap{0xdeadbeef: compSet(0xdeadbeef, 0xcafebabe)}, 46 res: []uint64{0xcafebabe}, 47 }, 48 // Test for cases when there's multiple comparisons (op1, op2), (op1, op3), ... 49 // Checks that for every such operand a program is generated. 50 { 51 name: "multiple-replacers-test", 52 in: 0xabcd, 53 size: 2, 54 comps: CompMap{0xabcd: compSet(0x32, 0x33)}, 55 res: []uint64{0x32, 0x33}, 56 }, 57 // Checks that special ints are not used. 58 { 59 name: "special-ints-test", 60 in: 0xabcd, 61 size: 2, 62 comps: CompMap{0xabcd: compSet(0x1, 0x2, 0x42)}, 63 res: []uint64{0x42}, 64 }, 65 66 // The following tests check the size limits for each replacer and for the initial value 67 // of the argument. The checks are made for positive and negative values and also for bitfields. 68 { 69 name: "int8-invalid-positive-value", 70 in: 0x1234, 71 size: 1, 72 comps: CompMap{ 73 // void test8(i8 el) { 74 // i16 w = (i16) el 75 // if (w == 0x88) {...} 76 // i16 other = 0xfffe 77 // if (w == other) 78 // }; test8(i8(0x1234)); 79 0x34: compSet(0x88, 0x1122, 0xfffffffffffffffe, 0xffffffffffffff0a), 80 // This following args should be iggnored. 81 0x1234: compSet(0xa1), 82 0xffffffffffffff34: compSet(0xaa), 83 }, 84 res: []uint64{0x88, 0xfe}, 85 }, 86 { 87 name: "int8-invalid-negative-value", 88 in: 0x12ab, 89 size: 1, 90 comps: CompMap{ 91 0xab: compSet(0xab, 0xac, 0xabcd), 92 0xffffffffffffffab: compSet(0x11, 0x22, 0xffffffffffffff34), 93 }, 94 res: []uint64{0x11, 0x22, 0xac}, 95 }, 96 { 97 name: "int16-valid-value-bitsize-12", 98 in: 0x3ab, 99 size: 2, 100 bitsize: 12, 101 comps: CompMap{ 102 0x3ab: compSet(0x11, 0x1234, 0xfffffffffffffffe), 103 0x13ab: compSet(0xab, 0xffa), 104 0xffffffffffffffab: compSet(0xfffffffffffffff1), 105 0xfffffffffffff3ab: compSet(0xff1, 0x12), 106 }, 107 res: []uint64{0x11, 0x3f1, 0xffe}, 108 }, 109 { 110 name: "int16-invalid-value-bitsize-12", 111 in: 0x71ab, 112 size: 2, 113 bitsize: 12, 114 comps: CompMap{ 115 0x1ab: compSet(0x11, 0x1234, 0xfffffffffffffffe), 116 }, 117 res: []uint64{0x11, 0xffe}, 118 }, 119 { 120 name: "int16-negative-valid-value-bitsize-12", 121 in: 0x8ab, 122 size: 2, 123 bitsize: 12, 124 comps: CompMap{ 125 0x8ab: compSet(0x11), 126 0xffffffffffffffab: compSet(0x12, 0xffffffffffffff0a), 127 0xfffffffffffff8ab: compSet(0x13, 0xffffffffffffff00), 128 }, 129 res: []uint64{0x11, 0x13, 0x812, 0xf00}, 130 }, 131 { 132 name: "int16-negative-invalid-value-bitsize-12", 133 in: 0x88ab, 134 size: 2, 135 bitsize: 12, 136 comps: CompMap{ 137 0x8ab: compSet(0x13), 138 0xfffffffffffff8ab: compSet(0x11, 0xffffffffffffff11), 139 }, 140 res: []uint64{0x11, 0x13, 0xf11}, 141 }, 142 { 143 name: "int32-invalid-value", 144 in: 0xaabaddcafe, 145 size: 4, 146 comps: CompMap{0xbaddcafe: compSet(0xab, 0xabcd, 0xbaddcafe, 147 0xdeadbeef, 0xaabbccddeeff1122)}, 148 res: []uint64{0xab, 0xabcd, 0xdeadbeef}, 149 }, 150 { 151 name: "int64-valid-value", 152 in: 0xdeadc0debaddcafe, 153 size: 8, 154 comps: CompMap{0xdeadc0debaddcafe: compSet(0xab, 0xabcd, 0xdeadbeef, 0xdeadbeefdeadbeef)}, 155 res: []uint64{0xab, 0xabcd, 0xdeadbeef, 0xdeadbeefdeadbeef}, 156 }, 157 } 158 meta := target.SyscallMap["test$hint_int"] 159 structType := meta.Args[0].Type.(*PtrType).Elem.(*StructType) 160 types := make(map[string]Type) 161 for _, field := range structType.Fields { 162 types[field.Name] = field.Type 163 } 164 for _, test := range tests { 165 t.Run(fmt.Sprintf("%v", test.name), func(t *testing.T) { 166 var res []uint64 167 typ := types[fmt.Sprintf("int%v_%v", test.size, test.bitsize)] 168 constArg := MakeConstArg(typ, DirIn, test.in) 169 checkConstArg(constArg, nil, test.comps, func() bool { 170 res = append(res, constArg.Val) 171 return true 172 }) 173 if !reflect.DeepEqual(res, test.res) { 174 t.Fatalf("\ngot : %v\nwant: %v", res, test.res) 175 } 176 }) 177 } 178 } 179 180 // Tests checkDataArg(). Is not intended to check correctness of any mutations. 181 // Mutation are checked in their own tests. 182 func TestHintsCheckDataArg(t *testing.T) { 183 target := initTargetTest(t, "test", "64") 184 // All inputs are in Little-Endian. 185 var tests = []DataArgTest{ 186 { 187 "one-replacer-test", 188 "\xef\xbe\xad\xde", 189 CompMap{ 190 0xdeadbeef: compSet(0xcafebabe, 0xdeadbeef), 191 0xbeef: compSet(0xbeef), 192 0xef: compSet(0xef), 193 }, 194 map[string]bool{ 195 "\xbe\xba\xfe\xca": true, 196 }, 197 }, 198 // Test for cases when there's multiple comparisons (op1, op2), (op1, op3), ... 199 // Checks that for every such operand a program is generated. 200 { 201 "multiple-replacers-test", 202 "\xcd\xab\x42\x42", 203 CompMap{0xabcd: compSet(0x44, 0x45)}, 204 map[string]bool{ 205 "\x44\x00\x42\x42": true, "\x45\x00\x42\x42": true, 206 }, 207 }, 208 // Checks that special ints are not used. 209 { 210 "special-ints-test", 211 "\xcd\xab\x42\x42", 212 CompMap{0xabcd: compSet(0x1, 0x45)}, 213 map[string]bool{ 214 "\x45\x00\x42\x42": true, 215 }, 216 }, 217 // Checks that ints of various sizes are extracted. 218 { 219 "different-sizes-test", 220 "\xef\xcd\xab\x90\x78\x56\x34\x12", 221 CompMap{ 222 0xef: compSet(0x11), 223 0xcdef: compSet(0x2222), 224 0x90abcdef: compSet(0x33333333), 225 0x1234567890abcdef: compSet(0x4444444444444444), 226 }, 227 map[string]bool{ 228 "\x11\xcd\xab\x90\x78\x56\x34\x12": true, 229 "\x22\x22\xab\x90\x78\x56\x34\x12": true, 230 "\x33\x33\x33\x33\x78\x56\x34\x12": true, 231 "\x44\x44\x44\x44\x44\x44\x44\x44": true, 232 }, 233 }, 234 // Checks that values with different offsets are extracted. 235 { 236 "different-offsets-test", 237 "\xab\xab\xab\xab\xab\xab\xab\xab\xab", 238 CompMap{ 239 0xab: compSet(0x11), 240 0xabab: compSet(0x2222), 241 0xabababab: compSet(0x33333333), 242 0xabababababababab: compSet(0x4444444444444444), 243 }, 244 map[string]bool{ 245 "\x11\xab\xab\xab\xab\xab\xab\xab\xab": true, 246 "\xab\x11\xab\xab\xab\xab\xab\xab\xab": true, 247 "\xab\xab\x11\xab\xab\xab\xab\xab\xab": true, 248 "\xab\xab\xab\x11\xab\xab\xab\xab\xab": true, 249 "\xab\xab\xab\xab\x11\xab\xab\xab\xab": true, 250 "\xab\xab\xab\xab\xab\x11\xab\xab\xab": true, 251 "\xab\xab\xab\xab\xab\xab\x11\xab\xab": true, 252 "\xab\xab\xab\xab\xab\xab\xab\x11\xab": true, 253 "\xab\xab\xab\xab\xab\xab\xab\xab\x11": true, 254 "\x22\x22\xab\xab\xab\xab\xab\xab\xab": true, 255 "\xab\x22\x22\xab\xab\xab\xab\xab\xab": true, 256 "\xab\xab\x22\x22\xab\xab\xab\xab\xab": true, 257 "\xab\xab\xab\x22\x22\xab\xab\xab\xab": true, 258 "\xab\xab\xab\xab\x22\x22\xab\xab\xab": true, 259 "\xab\xab\xab\xab\xab\x22\x22\xab\xab": true, 260 "\xab\xab\xab\xab\xab\xab\x22\x22\xab": true, 261 "\xab\xab\xab\xab\xab\xab\xab\x22\x22": true, 262 "\x33\x33\x33\x33\xab\xab\xab\xab\xab": true, 263 "\xab\x33\x33\x33\x33\xab\xab\xab\xab": true, 264 "\xab\xab\x33\x33\x33\x33\xab\xab\xab": true, 265 "\xab\xab\xab\x33\x33\x33\x33\xab\xab": true, 266 "\xab\xab\xab\xab\x33\x33\x33\x33\xab": true, 267 "\xab\xab\xab\xab\xab\x33\x33\x33\x33": true, 268 "\x44\x44\x44\x44\x44\x44\x44\x44\xab": true, 269 "\xab\x44\x44\x44\x44\x44\x44\x44\x44": true, 270 }, 271 }, 272 { 273 "replace-in-the-middle-of-a-larger-blob", 274 "\xef\xcd\xab\x90\x78\x56\x34\x12", 275 CompMap{0xffffffffffff90ab: compSet(0xffffffffffffaabb)}, 276 map[string]bool{ 277 "\xef\xcd\xbb\xaa\x78\x56\x34\x12": true, 278 }, 279 }, 280 { 281 282 "big-endian-replace", 283 "\xef\xcd\xab\x90\x78\x56\x34\x12", 284 CompMap{ 285 // 0xff07 is reversed special int. 286 0xefcd: compSet(0xaabb, 0xff07), 287 0x3412: compSet(0xaabb, 0xff07), 288 0x9078: compSet(0xaabb, 0x11223344, 0xff07), 289 0x90785634: compSet(0xaabbccdd, 0x11223344), 290 0xefcdab9078563412: compSet(0x1122334455667788), 291 }, 292 map[string]bool{ 293 "\xaa\xbb\xab\x90\x78\x56\x34\x12": true, 294 "\xef\xcd\xab\x90\x78\x56\xaa\xbb": true, 295 "\xef\xcd\xab\xaa\xbb\x56\x34\x12": true, 296 "\xef\xcd\xab\xaa\xbb\xcc\xdd\x12": true, 297 "\xef\xcd\xab\x11\x22\x33\x44\x12": true, 298 "\x11\x22\x33\x44\x55\x66\x77\x88": true, 299 }, 300 }, 301 } 302 meta := target.SyscallMap["test$hint_data"] 303 typ := meta.Args[0].Type.(*PtrType).Elem // array[int8] 304 for _, test := range tests { 305 t.Run(fmt.Sprintf("%v", test.name), func(t *testing.T) { 306 res := make(map[string]bool) 307 dataArg := MakeDataArg(typ, DirIn, []byte(test.in)) 308 checkDataArg(dataArg, test.comps, func() bool { 309 res[string(dataArg.Data())] = true 310 return true 311 }) 312 if !reflect.DeepEqual(res, test.res) { 313 s := "\ngot: [" 314 for x := range res { 315 s += fmt.Sprintf("0x%x, ", x) 316 } 317 s += "]\nwant: [" 318 for x := range test.res { 319 s += fmt.Sprintf("0x%x, ", x) 320 } 321 s += "]\n" 322 t.Fatal(s) 323 } 324 }) 325 } 326 } 327 328 func TestHintsCompressedImage(t *testing.T) { 329 target := initTargetTest(t, "test", "64") 330 type Test struct { 331 input string 332 comps CompMap 333 output []string 334 } 335 var tests = []Test{ 336 { 337 "\x00\x11\x22\x33\x44\x55\x66\x77", 338 CompMap{ 339 // 1/2-bytes must not be replaced. 340 0x00: compSet(0xaa), 341 0x11: compSet(0xaa), 342 0x1122: compSet(0xaabb), 343 0x4455: compSet(0xaabb), 344 // Aligned 4-byte values are replaced in both little/big endian. 345 0x00112233: compSet(0xaabbccdd), 346 0x33221100: compSet(0xaabbccdd), 347 0x44556677: compSet(0xaabbccdd), 348 0x77665544: compSet(0xaabbccdd), 349 // Same for 8-byte values. 350 0x0011223344556677: compSet(0xaabbccddeeff9988), 351 0x7766554433221100: compSet(0xaabbccddeeff9988), 352 // Unaligned 4-bytes are not replaced. 353 0x11223344: compSet(0xaabbccdd), 354 0x22334455: compSet(0xaabbccdd), 355 }, 356 []string{ 357 // Mutants for 4-byte values. 358 "\xaa\xbb\xcc\xdd\x44\x55\x66\x77", 359 "\xdd\xcc\xbb\xaa\x44\x55\x66\x77", 360 "\x00\x11\x22\x33\xaa\xbb\xcc\xdd", 361 "\x00\x11\x22\x33\xdd\xcc\xbb\xaa", 362 // Mutants for 8-byte values. 363 "\xaa\xbb\xcc\xdd\xee\xff\x99\x88", 364 "\x88\x99\xff\xee\xdd\xcc\xbb\xaa", 365 }, 366 }, 367 { 368 "\x00\x11\x22\x33\x44\x55\x66\x77", 369 CompMap{ 370 // Special values are used as replacers. 371 0x00112233: compSet(0, 0xffffffff), 372 }, 373 []string{ 374 // Mutants for 4-byte values. 375 "\x00\x00\x00\x00\x44\x55\x66\x77", 376 "\xff\xff\xff\xff\x44\x55\x66\x77", 377 }, 378 }, 379 { 380 // All 0s and 0xff must not be replaced. 381 "\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00", 382 CompMap{ 383 0: compSet(0xaabbccdd), 384 0xffffffffffffffff: compSet(0xaabbccddaabbccdd), 385 }, 386 nil, 387 }, 388 } 389 typ := target.SyscallMap["serialize3"].Args[0].Type.(*PtrType).Elem.(*BufferType) 390 if typ.Kind != BufferCompressed { 391 panic("wrong arg type") 392 } 393 for i, test := range tests { 394 t.Run(fmt.Sprintf("%v", i), func(t *testing.T) { 395 var res []string 396 arg := MakeDataArg(typ, DirIn, image.Compress([]byte(test.input))) 397 generateHints(test.comps, arg, nil, func() bool { 398 res = append(res, string(arg.Data())) 399 return true 400 }) 401 for i, compressed := range res { 402 data, dtor := image.MustDecompress([]byte(compressed)) 403 res[i] = string(data) 404 dtor() 405 } 406 sort.Strings(res) 407 sort.Strings(test.output) 408 if diff := cmp.Diff(test.output, res); diff != "" { 409 t.Fatalf("got wrong mutants: %v", diff) 410 } 411 data, dtor := image.MustDecompress(arg.Data()) 412 defer dtor() 413 if diff := cmp.Diff(test.input, string(data)); diff != "" { 414 t.Fatalf("argument got changed afterwards: %v", diff) 415 } 416 }) 417 } 418 } 419 420 func TestHintsShrinkExpand(t *testing.T) { 421 t.Parallel() 422 // Naming conventions: 423 // b - byte variable (i8 or u8) 424 // w - word variable (i16 or u16) 425 // dw - dword variable (i32 or u32) 426 // qw - qword variable (i64 or u64) 427 // ----------------------------------------------------------------- 428 // Shrink tests: 429 var tests = []ConstArgTest{ 430 { 431 // Models the following code: 432 // void f(u16 w) { 433 // u8 b = (u8) w; 434 // if (b == 0xab) {...} 435 // if (w == 0xcdcd) {...} 436 // }; f(0x1234); 437 name: "shrink-16-test", 438 in: 0x1234, 439 comps: CompMap{ 440 0x34: compSet(0xab), 441 0x1234: compSet(0xcdcd), 442 }, 443 res: []uint64{0x12ab, 0xcdcd}, 444 }, 445 { 446 // Models the following code: 447 // void f(u32 dw) { 448 // u8 b = (u8) dw 449 // i16 w = (i16) dw 450 // if (b == 0xab) {...} 451 // if (w == 0xcdcd) {...} 452 // if (dw == 0xefefefef) {...} 453 // }; f(0x12345678); 454 name: "shrink-32-test", 455 in: 0x12345678, 456 comps: CompMap{ 457 0x78: compSet(0xab), 458 0x5678: compSet(0xcdcd), 459 0x12345678: compSet(0xefefefef), 460 }, 461 res: []uint64{0x123456ab, 0x1234cdcd, 0xefefefef}, 462 }, 463 { 464 // Models the following code: 465 // void f(u64 qw) { 466 // u8 b = (u8) qw 467 // u16 w = (u16) qw 468 // u32 dw = (u32) qw 469 // if (b == 0xab) {...} 470 // if (w == 0xcdcd) {...} 471 // if (dw == 0xefefefef) {...} 472 // if (qw == 0x0101010101010101) {...} 473 // }; f(0x1234567890abcdef); 474 name: "shrink-64-test", 475 in: 0x1234567890abcdef, 476 comps: CompMap{ 477 0xef: compSet(0xab, 0xef), 478 0xcdef: compSet(0xcdcd), 479 0x90abcdef: compSet(0xefefefef), 480 0x1234567890abcdef: compSet(0x0101010101010101), 481 }, 482 res: []uint64{ 483 0x0101010101010101, 484 0x1234567890abcdab, 485 0x1234567890abcdcd, 486 0x12345678efefefef, 487 }, 488 }, 489 { 490 // Models the following code: 491 // void f(i16 w) { 492 // i8 b = (i8) w; 493 // i16 other = 0xabab; 494 // if (b == other) {...} 495 // }; f(0x1234); 496 // In such code the comparison will never be true, so we don't 497 // generate a hint for it. 498 name: "shrink-with-a-wider-replacer-test1", 499 in: 0x1234, 500 comps: CompMap{0x34: compSet(0x1bab)}, 501 res: nil, 502 }, 503 { 504 // Models the following code: 505 // void f(i16 w) { 506 // i8 b = (i8) w; 507 // i16 other = 0xfffd; 508 // if (b == other) {...} 509 // }; f(0x1234); 510 // In such code b will be sign extended to 0xff34 and, if we replace 511 // the lower byte, then the if statement will be true. 512 // Note that executor sign extends all the comparison operands to 513 // int64, so we model this accordingly. 514 name: "shrink-with-a-wider-replacer-test2", 515 in: 0x1234, 516 comps: CompMap{0x34: compSet(0xfffffffffffffffd)}, 517 res: []uint64{0x12fd}, 518 }, 519 // ----------------------------------------------------------------- 520 // Extend tests: 521 // Note that executor sign extends all the comparison operands to int64, 522 // so we model this accordingly. 523 { 524 // Models the following code: 525 // void f(i8 b) { 526 // i64 qw = (i64) b; 527 // if (qw == -2) {...}; 528 // }; f(-1); 529 name: "extend-8-test", 530 in: 0xff, 531 comps: CompMap{0xffffffffffffffff: compSet(0xfffffffffffffffe)}, 532 res: []uint64{0xfe}, 533 }, 534 { 535 // Models the following code: 536 // void f(i16 w) { 537 // i64 qw = (i64) w; 538 // if (qw == -2) {...}; 539 // }; f(-1); 540 name: "extend-16-test", 541 in: 0xffff, 542 comps: CompMap{0xffffffffffffffff: compSet(0xfffffffffffffffe)}, 543 res: []uint64{0xfffe}, 544 }, 545 { 546 // Models the following code: 547 // void f(i32 dw) { 548 // i64 qw = (i32) dw; 549 // if (qw == -2) {...}; 550 // }; f(-1); 551 name: "extend-32-test", 552 in: 0xffffffff, 553 comps: CompMap{0xffffffffffffffff: compSet(0xfffffffffffffffe)}, 554 res: []uint64{0xfffffffe}, 555 }, 556 { 557 // Models the following code: 558 // void f(i8 b) { 559 // i16 w = (i16) b; 560 // if (w == (i16) 0xfeff) {...}; 561 // }; f(-1); 562 // There's no value for b that will make the comparison true, 563 // so we don't generate hints. 564 name: "extend-with-a-wider-replacer-test", 565 in: 0xff, 566 comps: CompMap{0xffffffffffffffff: compSet(0xfffffffffffffeff)}, 567 res: nil, 568 }, 569 } 570 for _, test := range tests { 571 t.Run(fmt.Sprintf("%v", test.name), func(t *testing.T) { 572 res := shrinkExpand(test.in, test.comps, 64, false) 573 if !reflect.DeepEqual(res, test.res) { 574 t.Fatalf("\ngot : %v\nwant: %v", res, test.res) 575 } 576 }) 577 } 578 } 579 580 func TestHintsCall(t *testing.T) { 581 target := initTargetTest(t, "test", "64") 582 type Test struct { 583 in string 584 comps CompMap 585 out []string 586 } 587 tests := []Test{ 588 { 589 in: `ioctl$1(0x0, 0x111, 0x0)`, 590 comps: CompMap{0x111: compSet(0x0, 0x111, 0x222, 0x333, 0x444, 0x666)}, 591 out: []string{ 592 `ioctl$1(0x0, 0x666, 0x0)`, 593 }, 594 }, 595 { 596 // For the generic syscall mutations should not be restricted by related calls. 597 // But we won't have 0x1000 and 0x10000 because they are special ints. 598 in: `socket$generic(0x1, 0x2, 0x3)`, 599 comps: CompMap{ 600 0x1: compSet(0x111, 0x333), 601 0x2: compSet(0x1000, 0x1100, 0x1200), 602 0x3: compSet(0x10000, 0x10100, 0x10200), 603 }, 604 out: []string{ 605 `socket$generic(0x333, 0x2, 0x3)`, 606 }, 607 }, 608 { 609 in: `socket$inet6(0x111, 0x222, 0x333)`, 610 comps: CompMap{ 611 0x111: compSet(0x211), 612 0x222: compSet(0x1100, 0x1200, 0x1300), 613 0x333: compSet(0x10000, 0x10100, 0x10200, 0x10300), 614 }, 615 out: []string{ 616 `socket$inet6(0x111, 0x222, 0x10100)`, 617 `socket$inet6(0x111, 0x222, 0x10200)`, 618 `socket$inet6(0x111, 0x222, 0x10300)`, 619 }, 620 }, 621 { 622 in: `socket$netlink(0x111, 0x222, 0x333)`, 623 comps: CompMap{ 624 0x111: compSet(0x211), 625 0x222: compSet(0x1100, 0x1200, 0x1300), 626 0x333: compSet(0x10000, 0x10100, 0x10200, 0x10300), 627 }, 628 out: []string{ 629 `socket$netlink(0x111, 0x1100, 0x333)`, 630 `socket$netlink(0x111, 0x1200, 0x333)`, 631 `socket$netlink(0x111, 0x1300, 0x333)`, 632 }, 633 }, 634 } 635 for i, test := range tests { 636 t.Run(fmt.Sprint(i), func(t *testing.T) { 637 p, err := target.Deserialize([]byte(test.in), Strict) 638 if err != nil { 639 t.Fatal(err) 640 } 641 var got []string 642 p.MutateWithHints(0, test.comps, func(newP *Prog) bool { 643 got = append(got, strings.TrimSpace(string(newP.Serialize()))) 644 return true 645 }) 646 assert.ElementsMatch(t, test.out, got) 647 }) 648 } 649 } 650 651 func TestHintsRandom(t *testing.T) { 652 target, rs, iters := initTest(t) 653 ct := target.DefaultChoiceTable() 654 iters /= 10 // the test takes long 655 r := newRand(target, rs) 656 for i := 0; i < iters; i++ { 657 p := target.Generate(rs, 5, ct) 658 for j, c := range p.Calls { 659 vals := extractValues(c) 660 for k := 0; k < 5; k++ { 661 vals[r.randInt64()] = true 662 } 663 // In the test mode, MutateWithHints is essentially quadratic over the number of values 664 // since we run full prog validation on each run. 665 // To avoid consuming too much time, let's just skip all calls that are too big. 666 const valsCutOff = 10000 667 if len(vals) > valsCutOff { 668 t.Logf("iter %d: skipping call %d - too big", i, j) 669 continue 670 } 671 comps := make(CompMap) 672 for v := range vals { 673 comps.Add(1, v, r.randInt64(), true) 674 } 675 p.MutateWithHints(j, comps, func(p1 *Prog) bool { return true }) 676 } 677 } 678 } 679 680 func extractValues(c *Call) map[uint64]bool { 681 vals := make(map[uint64]bool) 682 ForeachArg(c, func(arg Arg, _ *ArgCtx) { 683 if arg.Dir() == DirOut { 684 return 685 } 686 switch a := arg.(type) { 687 case *ConstArg: 688 vals[a.Val] = true 689 case *DataArg: 690 data := a.Data() 691 for i := range data { 692 vals[uint64(data[i])] = true 693 if i < len(data)-1 { 694 v := uint64(data[i]) | uint64(data[i+1])<<8 695 vals[v] = true 696 } 697 if i < len(data)-3 { 698 v := uint64(data[i]) | uint64(data[i+1])<<8 | 699 uint64(data[i+2])<<16 | uint64(data[i+3])<<24 700 vals[v] = true 701 } 702 if i < len(data)-7 { 703 v := uint64(data[i]) | uint64(data[i+1])<<8 | 704 uint64(data[i+2])<<16 | uint64(data[i+3])<<24 | 705 uint64(data[i+4])<<32 | uint64(data[i+5])<<40 | 706 uint64(data[i+6])<<48 | uint64(data[i+7])<<56 707 vals[v] = true 708 } 709 } 710 } 711 }) 712 delete(vals, 0) // replacing 0 can yield too many condidates 713 return vals 714 } 715 716 func TestHintsData(t *testing.T) { 717 target := initTargetTest(t, "test", "64") 718 type Test struct { 719 in string 720 comps CompMap 721 out []string 722 } 723 tests := []Test{ 724 { 725 in: "0809101112131415", 726 comps: CompMap{0x12111009: compSet(0x42)}, 727 out: []string{"0842000000131415"}, 728 }, 729 } 730 for _, test := range tests { 731 p, err := target.Deserialize([]byte(fmt.Sprintf("test$hint_data(&AUTO=\"%v\")", test.in)), Strict) 732 if err != nil { 733 t.Fatal(err) 734 } 735 var got []string 736 p.MutateWithHints(0, test.comps, func(newP *Prog) bool { 737 got = append(got, hex.EncodeToString( 738 newP.Calls[0].Args[0].(*PointerArg).Res.(*DataArg).Data())) 739 return true 740 }) 741 sort.Strings(test.out) 742 sort.Strings(got) 743 if !reflect.DeepEqual(got, test.out) { 744 t.Fatalf("comps: %v\ninput: %v\ngot : %+v\nwant: %+v", 745 test.comps, test.in, got, test.out) 746 } 747 } 748 } 749 750 func TestInplaceIntersect(t *testing.T) { 751 m1 := CompMap{ 752 0xdead: compSet(0x1, 0x2), 753 0xbeef: compSet(0x3, 0x4), 754 0xffff: compSet(0x5), 755 } 756 m2 := CompMap{ 757 0xdead: compSet(0x2), 758 0xbeef: compSet(0x3, 0x6), 759 0xeeee: compSet(0x6), 760 } 761 m1.InplaceIntersect(m2) 762 assert.Equal(t, CompMap{ 763 0xdead: compSet(0x2), 764 0xbeef: compSet(0x3), 765 }, m1) 766 } 767 768 func BenchmarkHints(b *testing.B) { 769 target, cleanup := initBench(b) 770 defer cleanup() 771 rs := rand.NewSource(0) 772 r := newRand(target, rs) 773 ct := target.DefaultChoiceTable() 774 p := target.Generate(rs, 30, ct) 775 comps := make([]CompMap, len(p.Calls)) 776 for i, c := range p.Calls { 777 vals := extractValues(c) 778 for j := 0; j < 5; j++ { 779 vals[r.randInt64()] = true 780 } 781 comps[i] = make(CompMap) 782 for v := range vals { 783 comps[i].Add(1, v, r.randInt64(), true) 784 } 785 } 786 b.RunParallel(func(pb *testing.PB) { 787 for pb.Next() { 788 for i := range p.Calls { 789 p.MutateWithHints(i, comps[i], func(p1 *Prog) bool { return true }) 790 } 791 } 792 }) 793 } 794 795 func TestHintsLimiter(t *testing.T) { 796 var limiter HintsLimiter 797 798 // Base case. 799 comps := make(CompMap) 800 comps.Add(1000, 1000, 1100, true) 801 for i := uint64(0); i < 9; i++ { 802 comps.Add(2000, 2000+i, 2100+i, true) 803 } 804 for i := uint64(0); i < 10; i++ { 805 comps.Add(3000, 3000+i, 3100+i, true) 806 } 807 for i := uint64(0); i < 11; i++ { 808 comps.Add(4000, 4000+i, 4100+i, true) 809 } 810 for i := uint64(0); i < 20; i++ { 811 comps.Add(5000, 5000+i, 5100+i, true) 812 } 813 assert.Equal(t, perPCCount(comps), map[uint64]int{ 814 1000: 1, 815 2000: 9, 816 3000: 10, 817 4000: 11, 818 5000: 20, 819 }) 820 limiter.Limit(comps) 821 assert.Equal(t, perPCCount(comps), map[uint64]int{ 822 1000: 1, 823 2000: 9, 824 3000: 10, 825 4000: 10, 826 5000: 10, 827 }) 828 829 // Test that counts are accumulated in the limiter. 830 comps = make(CompMap) 831 for i := uint64(0); i < 3; i++ { 832 comps.Add(1000, 1000+i, 1100+i, true) 833 } 834 for i := uint64(0); i < 3; i++ { 835 comps.Add(2000, 2000+i, 2100+i, true) 836 } 837 for i := uint64(0); i < 3; i++ { 838 comps.Add(3000, 3000+i, 3100+i, true) 839 } 840 assert.Equal(t, perPCCount(comps), map[uint64]int{ 841 1000: 3, 842 2000: 3, 843 3000: 3, 844 }) 845 limiter.Limit(comps) 846 assert.Equal(t, perPCCount(comps), map[uint64]int{ 847 1000: 3, 848 2000: 1, 849 }) 850 } 851 852 func perPCCount(comps CompMap) map[uint64]int { 853 res := make(map[uint64]int) 854 for _, ops2 := range comps { 855 for _, pcs := range ops2 { 856 for pc := range pcs { 857 res[pc]++ 858 } 859 } 860 } 861 return res 862 } 863 864 func compSet(vals ...uint64) map[uint64]map[uint64]bool { 865 m := make(map[uint64]map[uint64]bool) 866 for _, v := range vals { 867 if m[v] == nil { 868 m[v] = make(map[uint64]bool) 869 } 870 m[v][1] = true 871 } 872 return m 873 }