github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/prog/encoding_test.go (about) 1 // Copyright 2016 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 "bytes" 8 "fmt" 9 "math/rand" 10 "reflect" 11 "sort" 12 "testing" 13 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 func setToArray(s map[string]struct{}) []string { 19 a := make([]string, 0, len(s)) 20 for c := range s { 21 a = append(a, c) 22 } 23 sort.Strings(a) 24 return a 25 } 26 27 func TestSerializeData(t *testing.T) { 28 t.Parallel() 29 r := rand.New(rand.NewSource(0)) 30 for _, readable := range []bool{false, true} { 31 for i := 0; i < 1e3; i++ { 32 data := make([]byte, r.Intn(4)) 33 for i := range data { 34 data[i] = byte(r.Intn(256)) 35 } 36 buf := new(bytes.Buffer) 37 serializeData(buf, data, readable) 38 p := newParser(nil, buf.Bytes(), true, false) 39 if !p.Scan() { 40 t.Fatalf("parser does not scan") 41 } 42 data1, _, err := p.deserializeData() 43 if err != nil { 44 t.Fatalf("failed to deserialize %q -> %s: %v", data, buf.Bytes(), err) 45 } 46 if !bytes.Equal(data, data1) { 47 t.Fatalf("corrupted data %q -> %s -> %q", data, buf.Bytes(), data1) 48 } 49 } 50 } 51 } 52 53 func TestCallSet(t *testing.T) { 54 t.Parallel() 55 tests := []struct { 56 prog string 57 ok bool 58 calls []string 59 ncalls int 60 }{ 61 { 62 "", 63 false, 64 []string{}, 65 0, 66 }, 67 { 68 "r0 = (foo)", 69 false, 70 []string{}, 71 0, 72 }, 73 { 74 "getpid()", 75 true, 76 []string{"getpid"}, 77 1, 78 }, 79 { 80 "r11 = getpid()", 81 true, 82 []string{"getpid"}, 83 1, 84 }, 85 { 86 "getpid()\n" + 87 "open(0x1, something that this package may not understand)\n" + 88 "getpid()\n" + 89 "#read()\n" + 90 "\n" + 91 "close$foo(&(0x0000) = {})\n", 92 true, 93 []string{"getpid", "open", "close$foo"}, 94 4, 95 }, 96 } 97 for i, test := range tests { 98 t.Run(fmt.Sprint(i), func(t *testing.T) { 99 calls, ncalls, err := CallSet([]byte(test.prog)) 100 if err != nil && test.ok { 101 t.Fatalf("parsing failed: %v", err) 102 } 103 if err == nil && !test.ok { 104 t.Fatalf("parsing did not fail") 105 } 106 callArray := setToArray(calls) 107 sort.Strings(test.calls) 108 if !reflect.DeepEqual(callArray, test.calls) { 109 t.Fatalf("got call set %+v, expect %+v", callArray, test.calls) 110 } 111 if ncalls != test.ncalls { 112 t.Fatalf("got %v calls, expect %v", ncalls, test.ncalls) 113 } 114 }) 115 } 116 } 117 118 func TestCallSetRandom(t *testing.T) { 119 target, rs, iters := initTest(t) 120 ct := target.DefaultChoiceTable() 121 for i := 0; i < iters; i++ { 122 const ncalls = 10 123 p := target.Generate(rs, ncalls, ct) 124 calls0 := make(map[string]struct{}) 125 for _, c := range p.Calls { 126 calls0[c.Meta.Name] = struct{}{} 127 } 128 calls1, ncalls1, err := CallSet(p.Serialize()) 129 if err != nil { 130 t.Fatalf("CallSet failed: %v", err) 131 } 132 callArray0 := setToArray(calls0) 133 callArray1 := setToArray(calls1) 134 if !reflect.DeepEqual(callArray0, callArray1) { 135 t.Fatalf("got call set:\n%+v\nexpect:\n%+v", callArray1, callArray0) 136 } 137 if ncalls1 != ncalls { 138 t.Fatalf("got %v calls, expect %v", ncalls1, ncalls) 139 } 140 } 141 } 142 143 func TestDeserialize(t *testing.T) { 144 TestDeserializeHelper(t, "test", "64", nil, []DeserializeTest{ 145 { 146 In: `test$struct(&(0x7f0000000000)={0x0, {0x0}})`, 147 Out: `test$struct(&(0x7f0000000000))`, 148 }, 149 { 150 In: `test$struct(&(0x7f0000000000)=0x0)`, 151 Out: `test$struct(&(0x7f0000000000))`, 152 StrictErr: "wrong int arg", 153 }, 154 { 155 In: `test$regression1(&(0x7f0000000000)=[{"000000"}, {"0000000000"}])`, 156 Out: `test$regression1(&(0x7f0000000000)=[{}, {}])`, 157 }, 158 { 159 In: `test$regression2(&(0x7f0000000000)=[0x1, 0x2, 0x3, 0x4, 0x5, 0x6])`, 160 Out: `test$regression2(&(0x7f0000000000)=[0x1, 0x2, 0x3, 0x4])`, 161 }, 162 { 163 In: `test_excessive_args1(0x0, 0x1, {0x1, &(0x7f0000000000)=[0x1, 0x2]})`, 164 Out: `test_excessive_args1()`, 165 StrictErr: "excessive syscall arguments", 166 }, 167 { 168 In: `test_excessive_args2(0x0, 0x1, {0x1, &(0x7f0000000000)={0x1, 0x2}})`, 169 Out: `test_excessive_args2(0x0)`, 170 StrictErr: "excessive syscall arguments", 171 }, 172 { 173 In: `test_excessive_args2(0x0, 0x1, {0x1, &(0x7f0000000000)=nil})`, 174 Out: `test_excessive_args2(0x0)`, 175 StrictErr: "excessive syscall arguments", 176 }, 177 { 178 In: `test_excessive_args2(0x0, &(0x7f0000000000), 0x0)`, 179 Out: `test_excessive_args2(0x0)`, 180 StrictErr: "excessive syscall arguments", 181 }, 182 { 183 In: `test$excessive_fields1(&(0x7f0000000000)={0x1, &(0x7f0000000000)=[{0x0}, 0x2]}, {0x1, 0x2, [0x1, 0x2]})`, 184 Out: `test$excessive_fields1(&(0x7f0000000000)={0x1})`, 185 StrictErr: "excessive struct excessive_fields fields", 186 }, 187 { 188 In: `test$excessive_fields1(0x0)`, 189 }, 190 { 191 In: `test$excessive_fields1(r0)`, 192 Out: `test$excessive_fields1(&(0x7f0000000000))`, 193 StrictErr: "undeclared variable r0", 194 }, 195 { 196 In: `test_excessive_args2(r1)`, 197 Out: `test_excessive_args2(0x0)`, 198 StrictErr: "undeclared variable r1", 199 }, 200 { 201 In: `test_excessive_args2({0x0, 0x1})`, 202 Out: `test_excessive_args2(0x0)`, 203 StrictErr: "wrong struct arg", 204 }, 205 { 206 In: `test_excessive_args2([0x0], 0x0)`, 207 Out: `test_excessive_args2(0x0)`, 208 StrictErr: "wrong array arg", 209 }, 210 { 211 In: `test_excessive_args2(@foo)`, 212 Out: `test_excessive_args2(0x0)`, 213 StrictErr: "wrong union arg", 214 }, 215 { 216 In: `test_excessive_args2('foo')`, 217 Out: `test_excessive_args2(0x0)`, 218 StrictErr: "wrong string arg", 219 }, 220 { 221 In: `test_excessive_args2(&(0x7f0000000000)={0x0, 0x1})`, 222 Out: `test_excessive_args2(0x0)`, 223 StrictErr: "wrong addr arg", 224 }, 225 { 226 In: `test_excessive_args2(nil)`, 227 Out: `test_excessive_args2(0x0)`, 228 }, 229 { 230 In: `test$type_confusion1(&(0x7f0000000000)=@unknown)`, 231 Out: `test$type_confusion1(&(0x7f0000000000))`, 232 StrictErr: `wrong option "unknown" of union "type_confusion", available options are: "f1"`, 233 }, 234 { 235 In: `test$type_confusion1(&(0x7f0000000000)=@unknown={0x0, 'abc'}, 0x0)`, 236 Out: `test$type_confusion1(&(0x7f0000000000))`, 237 StrictErr: `wrong option "unknown" of union "type_confusion", available options are: "f1"`, 238 }, 239 { 240 In: `test$excessive_fields1(&(0x7f0000000000)=0x0)`, 241 Out: `test$excessive_fields1(&(0x7f0000000000))`, 242 StrictErr: "wrong int arg", 243 }, 244 { 245 In: `test$excessive_fields1(0xffffffffffffffff)`, 246 }, 247 { 248 In: `test$excessive_fields1(0xfffffffffffffffe)`, 249 }, 250 { 251 In: `test$excessive_fields1(0xfffffffffffffffd)`, 252 Out: `test$excessive_fields1(0x0)`, 253 }, 254 { 255 In: `test$excessive_fields1(0xfffffffffffffffc)`, 256 Out: `test$excessive_fields1(0xffffffffffffffff)`, 257 }, 258 { 259 In: `test$auto0(AUTO, &AUTO={AUTO, AUTO, 0x1}, AUTO, 0x0)`, 260 Out: `test$auto0(0x42, &(0x7f0000000040)={0xc, 0x43, 0x1}, 0xc, 0x0)`, 261 }, 262 { 263 In: `test$auto0(AUTO, &AUTO={AUTO, AUTO, AUTO}, AUTO, 0x0)`, 264 Err: `wrong type *prog.IntType for AUTO`, 265 }, 266 { 267 In: `test$auto1(AUTO, &AUTO=AUTO, AUTO, 0x0)`, 268 Out: `test$auto1(0x42, &(0x7f0000000040)={0xc, 0x43, 0x0}, 0xc, 0x0)`, 269 }, 270 { 271 In: `test$auto2(AUTO, &AUTO=AUTO, AUTO, 0x0)`, 272 Out: `test$auto2(0x42, &(0x7f0000000040)={0x10, {0xc, 0x43, 0x0}}, 0x10, 0x0)`, 273 }, 274 { 275 In: `test$auto0(AUTO, &AUTO=AUTO, AUTO, 0x0)`, 276 Err: `wrong type *prog.IntType for AUTO`, 277 }, 278 { 279 In: `test$bf2(&AUTO={AUTO, 0x10, 0x0, AUTO})`, 280 Out: `test$bf2(&(0x7f0000000040)={0x8, 0x10, 0x0, 0x18})`, 281 }, 282 { 283 In: `test$str0(&AUTO="303100090a0d7022273a")`, 284 Out: `test$str0(&(0x7f0000000040)='01\x00\t\n\rp\"\':')`, 285 }, 286 { 287 In: `test$blob0(&AUTO="303100090a0d7022273a")`, 288 Out: `test$blob0(&(0x7f0000000040)='01\x00\t\n\rp\"\':')`, 289 }, 290 { 291 In: `test$blob0(&AUTO="3031000a0d7022273a01")`, 292 Out: `test$blob0(&(0x7f0000000040)="3031000a0d7022273a01")`, 293 }, 294 { 295 In: `test$out_const(&(0x7f0000000000)=0x2)`, 296 Out: `test$out_const(&(0x7f0000000000))`, 297 StrictErr: `out arg const[1, const] has non-default value: 2`, 298 }, 299 { 300 In: `test$str1(&(0x7f0000000000)='foo\x00')`, 301 Out: `test$str1(&(0x7f0000000000))`, 302 }, 303 { 304 In: `test$str1(&(0x7f0000000000)='bar\x00')`, 305 Out: `test$str1(&(0x7f0000000000))`, 306 StrictErr: `bad string value "bar\x00", expect ["foo\x00"]`, 307 }, 308 { 309 In: `test$str2(&(0x7f0000000000)='bar\x00')`, 310 }, 311 { 312 In: `test$str2(&(0x7f0000000000)='baz\x00')`, 313 Out: `test$str2(&(0x7f0000000000)='foo\x00')`, 314 StrictErr: `bad string value "baz\x00", expect ["foo\x00" "bar\x00"]`, 315 }, 316 { 317 In: `test$opt2(&(0x7f0000000000))`, 318 Out: `test$opt2(0x0)`, 319 }, 320 { 321 In: `test$opt2(&(0x7f0000000001))`, 322 Out: `test$opt2(0x0)`, 323 StrictErr: `unaligned vma address 0x1`, 324 }, 325 { 326 In: `test$opt2(&(0x7f0000000000)=nil)`, 327 Out: `test$opt2(0x0)`, 328 }, 329 { 330 In: `test$opt2(&(0x7f0000000000)='foo')`, 331 Out: `test$opt2(0x0)`, 332 StrictErr: `non-nil argument for nil type`, 333 }, 334 { 335 In: `test$opt2(0x0) (non_existing_prop: 123)`, 336 Out: `test$opt2(0x0)`, 337 StrictErr: `unknown call property: non_existing_prop`, 338 }, 339 { 340 In: `test$opt2(0x0) (fail_nth: zzz)`, 341 Out: `test$opt2(0x0)`, 342 StrictErr: `invalid int value: zzz`, 343 }, 344 { 345 In: `test$opt2(0x0) (non_existing_prop: 123, fail_nth: 1)`, 346 Out: `test$opt2(0x0) (fail_nth: 1)`, 347 StrictErr: `unknown call property: non_existing_prop`, 348 }, 349 { 350 In: `test$opt2(0x0) (fail_nth: 0)`, 351 Out: `test$opt2(0x0)`, 352 }, 353 { 354 In: `test$str2(&(0x7f0000000000)="$eJwqrqzKTszJSS0CBAAA//8TyQPi`, 355 Err: `want ", got EOF`, 356 }, 357 { 358 In: `mutate9(&(0x7f0000000000)='./local/filename\x00')`, 359 }, 360 { 361 In: `mutate9(&(0x7f0000000000)='/escaping/filename\x00')`, 362 Err: `escaping filename`, 363 }, 364 }) 365 } 366 367 func TestSerializeDeserialize(t *testing.T) { 368 TestDeserializeHelper(t, "test", "64", nil, []DeserializeTest{ 369 { 370 In: `serialize0(&(0x7f0000408000)={"6861736800000000000000000000", "48490000"})`, 371 Out: `serialize0(&(0x7f0000408000)={'hash\x00', 'HI\x00'})`, 372 }, 373 { 374 In: `serialize1(&(0x7f0000000000)="0000000000000000", 0x8)`, 375 Out: `serialize1(&(0x7f0000000000)=""/8, 0x8)`, 376 }, 377 { 378 In: `serialize2(&(0x7f0000000000)="$c3l6a2FsbGVy")`, 379 Out: `serialize2(&(0x7f0000000000)='syzkaller')`, 380 }, 381 { 382 In: `serialize3(&(0x7f0000000000)="$eJwqrqzKTszJSS0CBAAA//8TyQPi")`, 383 }, 384 { 385 In: `foo$any_filename(&(0x7f0000000000)=ANY=[@ANYBLOB='/'])`, 386 Out: `foo$any_filename(&(0x7f0000000000)=@complex={0x0, 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, []})`, 387 StrictErr: `wrong array arg`, 388 }, 389 }) 390 } 391 392 func TestDeserializeDataMmapProg(t *testing.T) { 393 testEachTarget(t, func(t *testing.T, target *Target) { 394 p := target.DataMmapProg() 395 data := p.Serialize() 396 p2, err := target.Deserialize(data, StrictUnsafe) 397 assert.NoError(t, err) 398 data2 := p2.Serialize() 399 assert.Equal(t, string(data), string(data2)) 400 p.SerializeForExec() 401 }) 402 } 403 404 func TestSerializeDeserializeRandom(t *testing.T) { 405 testEachTargetRandom(t, func(t *testing.T, target *Target, rs rand.Source, iters int) { 406 ct := target.DefaultChoiceTable() 407 for i := 0; i < iters; i++ { 408 p0 := target.Generate(rs, 10, ct) 409 if _, _, ok := testSerializeDeserialize(t, p0); ok { 410 continue 411 } 412 p0, _ = Minimize(p0, -1, MinimizeCorpus, func(p1 *Prog, _ int) bool { 413 _, _, ok := testSerializeDeserialize(t, p1) 414 return !ok 415 }) 416 data0, data1, ok := testSerializeDeserialize(t, p0) 417 if ok { 418 t.Log("flaky?") 419 } 420 decoded0, err := target.DeserializeExec(data0, nil) 421 if err != nil { 422 t.Fatal(err) 423 } 424 decoded1, err := target.DeserializeExec(data1, nil) 425 if err != nil { 426 t.Fatal(err) 427 } 428 t.Logf("decoded0: %+v", decoded0) 429 t.Logf("decoded1: %+v", decoded1) 430 t.Fatalf("was: %q\ngot: %q\nprogram:\n%s", data0, data1, p0.Serialize()) 431 } 432 }) 433 } 434 435 func testSerializeDeserialize(t *testing.T, p0 *Prog) ([]byte, []byte, bool) { 436 data0, err := p0.SerializeForExec() 437 if err != nil { 438 t.Fatal(err) 439 } 440 serialized := p0.Serialize() 441 p1, err := p0.Target.Deserialize(serialized, NonStrict) 442 if err != nil { 443 t.Fatal(err) 444 } 445 data1, err := p1.SerializeForExec() 446 if err != nil { 447 t.Fatal(err) 448 } 449 if !bytes.Equal(data0, data1) { 450 t.Logf("PROG0:\n%s", p0.Serialize()) 451 t.Logf("PROG1:\n%s", p1.Serialize()) 452 return data0, data1, false 453 } 454 return nil, nil, true 455 } 456 457 func TestSerializeCallProps(t *testing.T) { 458 target := initTargetTest(t, "test", "64") 459 type SerializeCallPropsTest struct { 460 prog string 461 props []CallProps 462 } 463 464 tests := []SerializeCallPropsTest{ 465 { 466 "serialize0(0x0)\n", 467 []CallProps{{}}, 468 }, 469 { 470 "serialize0(0x0) ()\n", 471 []CallProps{{}}, 472 }, 473 { 474 "serialize0(0x0) (fail_nth: 5)\n", 475 []CallProps{{5, false, 0}}, 476 }, 477 { 478 "serialize0(0x0) (fail_nth)\n", 479 nil, 480 }, 481 { 482 "serialize0(0x0) (fail_nth: \"5\")\n", 483 nil, 484 }, 485 { 486 "serialize0(0x0) (async)\n", 487 []CallProps{{0, true, 0}}, 488 }, 489 { 490 "serialize0(0x0) (async, rerun: 10)\n", 491 []CallProps{{0, true, 10}}, 492 }, 493 } 494 495 for _, test := range tests { 496 p, err := target.Deserialize([]byte(test.prog), Strict) 497 if test.props != nil && err != nil { 498 t.Fatal(err) 499 } else if test.props == nil && err == nil { 500 t.Errorf("expected an error, but got none\n%s", test.prog) 501 } 502 503 for i, props := range test.props { 504 if !reflect.DeepEqual(props, p.Calls[i].Props) { 505 t.Errorf("%v-th call props differ: %v != %v", i, props, p.Calls[i].Props) 506 } 507 } 508 } 509 } 510 511 func TestDeserializeComments(t *testing.T) { 512 target := initTargetTest(t, "test", "64") 513 p, err := target.Deserialize([]byte(` 514 # comment1 515 # comment2 516 serialize0(0x0) 517 serialize0(0x0) 518 # comment3 519 serialize0(0x0) 520 # comment4 521 serialize0(0x0) # comment5 522 #comment6 523 524 serialize0(0x0) 525 #comment7 526 `), Strict) 527 if err != nil { 528 t.Fatal(err) 529 } 530 for i, want := range []string{ 531 "comment2", 532 "", 533 "comment3", 534 "comment5", 535 "", 536 } { 537 if got := p.Calls[i].Comment; got != want { 538 t.Errorf("bad call %v comment: %q, want %q", i, got, want) 539 } 540 } 541 wantComments := []string{ 542 "comment1", 543 "comment4", 544 "comment6", 545 "comment7", 546 } 547 if !reflect.DeepEqual(p.Comments, wantComments) { 548 t.Errorf("bad program comments %q\nwant: %q", p.Comments, wantComments) 549 } 550 } 551 552 func TestHasNext(t *testing.T) { 553 testCases := []struct { 554 input string 555 expected bool 556 }{ 557 {"abcdef", true}, 558 {"xyz", false}, 559 {"ab", false}, 560 {"abc", true}, 561 } 562 for _, testCase := range testCases { 563 p := newParser(nil, []byte(testCase.input), true, false) 564 if !p.Scan() { 565 t.Fatalf("parser does not scan") 566 } 567 result := p.HasNext("abc") 568 if result != testCase.expected { 569 t.Errorf("expected HasNext(\"abc\") on input %q to be %v, but got %v", 570 testCase.input, testCase.expected, result) 571 } 572 } 573 } 574 575 // nolint: lll 576 func TestDeserializeSkipImage(t *testing.T) { 577 target := initTargetTest(t, "linux", "amd64") 578 p, err := target.Deserialize([]byte(` 579 syz_mount_image$ext4(&(0x7f0000000080)='ext4\x00', &(0x7f00000000c0)='./file0\x00', 0x0, &(0x7f0000000100), 0x1, 0x71, &(0x7f0000000140)="$eJzszrENAVAUBdDrLyASnUIYwA5GESWdiljJDiYwgg0UWs1XfArfABI5J3kvue827/I4Tc6zpA6T2tntD5vVtu3wl0qSUZJxkum85duydYNXf70f1+/59b8AAAAAAAAAwLeSRZ8/Ds8AAAD//9ZiI98=") 580 `), Strict) 581 require.NoError(t, err) 582 assert.Equal(t, `syz_mount_image$ext4(&(0x7f0000000080)='ext4\x00', &(0x7f00000000c0)='./file0\x00', 0x0, &(0x7f0000000100), 0x1, 0x71, &(0x7f0000000140)="<<IMAGE>>") 583 `, 584 string(p.Serialize(SkipImages))) 585 }