vitess.io/vitess@v0.16.2/go/test/fuzzing/vttablet_fuzzer.go (about) 1 //go:build gofuzz 2 // +build gofuzz 3 4 /* 5 Copyright 2021 The Vitess Authors. 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 http://www.apache.org/licenses/LICENSE-2.0 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package fuzzing 18 19 import ( 20 "context" 21 "fmt" 22 "net" 23 "testing" 24 25 "google.golang.org/grpc" 26 27 "vitess.io/vitess/go/vt/vttablet/grpctmclient" 28 "vitess.io/vitess/go/vt/vttablet/grpctmserver" 29 "vitess.io/vitess/go/vt/vttablet/tmrpctest" 30 31 fuzz "github.com/AdaLogics/go-fuzz-headers" 32 33 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 34 ) 35 36 func onceInit() { 37 testing.Init() 38 } 39 40 // createTablets creates a slice of Tablets that can 41 // be used by the fuzz targets. 42 func (fs *fuzzStore) createTablets(f *fuzz.ConsumeFuzzer) error { 43 tabletCount, err := f.GetInt() 44 if err != nil { 45 return err 46 } 47 tabletCount = tabletCount % 40 48 if tabletCount == 0 { 49 return fmt.Errorf("We don't need a nil-len list") 50 } 51 tablets := make([]*topodatapb.Tablet, 0) 52 for i := 0; i < tabletCount; i++ { 53 tablet := &topodatapb.Tablet{} 54 err = f.GenerateStruct(tablet) 55 if err != nil { 56 return err 57 } 58 tablets = append(tablets, tablet) 59 } 60 fs.tablets = tablets 61 return nil 62 } 63 64 // createTabletAliases creates a slice of TabletAliases that can 65 // be used by the fuzz targets. 66 func (fs *fuzzStore) createTabletAliases(f *fuzz.ConsumeFuzzer) error { 67 tabletAliasCount, err := f.GetInt() 68 if err != nil { 69 return err 70 } 71 tabletAliasCount = tabletAliasCount % 40 72 if tabletAliasCount == 0 { 73 return fmt.Errorf("We don't need a nil-len list") 74 } 75 tabletAliases := make([]*topodatapb.TabletAlias, 0) 76 for i := 0; i < tabletAliasCount; i++ { 77 tabletAlias := &topodatapb.TabletAlias{} 78 err = f.GenerateStruct(tabletAlias) 79 if err != nil { 80 return err 81 } 82 tabletAliases = append(tabletAliases, tabletAlias) 83 } 84 fs.tabletAliases = tabletAliases 85 return nil 86 } 87 88 // createStrings creates a slice of strings that can be used 89 // by the fuzz tagets. 90 func (fs *fuzzStore) createStrings(f *fuzz.ConsumeFuzzer) error { 91 stringCount, err := f.GetInt() 92 if err != nil { 93 return err 94 } 95 stringCount = stringCount % 100 96 if stringCount == 0 { 97 return fmt.Errorf("We don't need a nil-len list") 98 } 99 stringSlice := make([]string, 0) 100 for i := 0; i < stringCount; i++ { 101 newString, err := f.GetString() 102 if err != nil { 103 return err 104 } 105 stringSlice = append(stringSlice, newString) 106 } 107 fs.miscStrings = stringSlice 108 return nil 109 } 110 111 // createBytes creates a slice of byte slices that can be used 112 // by the fuzz targets. 113 func (fs *fuzzStore) createBytes(f *fuzz.ConsumeFuzzer) error { 114 bytesCount, err := f.GetInt() 115 if err != nil { 116 return err 117 } 118 bytesCount = bytesCount % 40 119 if bytesCount == 0 { 120 return fmt.Errorf("We don't need a nil-len list") 121 } 122 byteSlice := make([][]byte, 0) 123 for i := 0; i < bytesCount; i++ { 124 newBytes, err := f.GetBytes() 125 if err != nil { 126 return err 127 } 128 byteSlice = append(byteSlice, newBytes) 129 } 130 fs.miscBytes = byteSlice 131 return nil 132 } 133 134 // createInts creates a list of ints that can be used 135 // by the fuzz targets. 136 func (fs *fuzzStore) createInts(f *fuzz.ConsumeFuzzer) error { 137 intCount, err := f.GetInt() 138 if err != nil { 139 return err 140 } 141 intCount = intCount % 40 142 if intCount == 0 { 143 return fmt.Errorf("We don't need a nil-len list") 144 } 145 intSlice := make([]int, 0) 146 for i := 0; i < intCount; i++ { 147 newInt, err := f.GetInt() 148 if err != nil { 149 return err 150 } 151 intSlice = append(intSlice, newInt) 152 } 153 fs.miscInts = intSlice 154 return nil 155 } 156 157 // createExecutionOrder creates an array of ints that will later 158 // be used to determine the order in which we call our fuzz targets. 159 func (fs *fuzzStore) createExecutionOrder(f *fuzz.ConsumeFuzzer) error { 160 intCount, err := f.GetInt() 161 if err != nil { 162 return err 163 } 164 intCount = intCount % 60 165 if intCount == 0 { 166 return fmt.Errorf("We don't need a nil-len list") 167 } 168 executionOrder := make([]int, 0) 169 for i := 0; i < intCount; i++ { 170 newInt, err := f.GetInt() 171 if err != nil { 172 return err 173 } 174 executionOrder = append(executionOrder, newInt) 175 } 176 fs.executionOrder = executionOrder 177 return nil 178 } 179 180 type fuzzStore struct { 181 tablets []*topodatapb.Tablet 182 tabletAliases []*topodatapb.TabletAlias 183 miscStrings []string 184 miscBytes [][]byte 185 miscInts []int 186 client *grpctmclient.Client 187 executionOrder []int 188 } 189 190 // newFuzzStore creates a store of the data that is needed 191 // for the fuzz targets in FuzzGRPCTMServer. The reason 192 // a store is created is because the set up of the server 193 // comes with a big expense, and having data ready to pass 194 // to the targets improves efficiency. 195 func newFuzzStore(f *fuzz.ConsumeFuzzer) (*fuzzStore, error) { 196 fs := &fuzzStore{} 197 198 err := fs.createTablets(f) 199 if err != nil { 200 return nil, err 201 } 202 203 err = fs.createTabletAliases(f) 204 if err != nil { 205 return nil, err 206 } 207 208 err = fs.createStrings(f) 209 if err != nil { 210 return nil, err 211 } 212 213 err = fs.createBytes(f) 214 if err != nil { 215 return nil, err 216 } 217 218 err = fs.createInts(f) 219 if err != nil { 220 return nil, err 221 } 222 223 err = fs.createExecutionOrder(f) 224 if err != nil { 225 return nil, err 226 } 227 return fs, nil 228 } 229 230 func (fs *fuzzStore) getTablet() (*topodatapb.Tablet, error) { 231 if len(fs.tablets) == 0 { 232 return nil, fmt.Errorf("Not enough tablets") 233 } 234 tablet := fs.tablets[0] 235 fs.tablets = fs.tablets[1:] 236 return tablet, nil 237 } 238 239 func (fs *fuzzStore) getString() (string, error) { 240 if len(fs.miscStrings) == 0 { 241 return "", fmt.Errorf("Not enough tablets") 242 } 243 returnString := fs.miscStrings[0] 244 fs.miscStrings = fs.miscStrings[1:] 245 return returnString, nil 246 } 247 248 func (fs *fuzzStore) getInt() (int, error) { 249 if len(fs.miscInts) == 0 { 250 return 0, fmt.Errorf("Not enough tablets") 251 } 252 returnInt := fs.miscInts[0] 253 fs.miscInts = fs.miscInts[1:] 254 return returnInt, nil 255 } 256 257 func (fs *fuzzStore) getBytes() ([]byte, error) { 258 if len(fs.miscBytes) == 0 { 259 return nil, fmt.Errorf("Not enough tablets") 260 } 261 returnBytes := fs.miscBytes[0] 262 fs.miscBytes = fs.miscBytes[1:] 263 return returnBytes, nil 264 } 265 266 func (fs *fuzzStore) getTabletAlias() (*topodatapb.TabletAlias, error) { 267 if len(fs.tabletAliases) == 0 { 268 return nil, fmt.Errorf("Not enough tablets") 269 } 270 tabletAlias := fs.tabletAliases[0] 271 fs.tabletAliases = fs.tabletAliases[1:] 272 return tabletAlias, nil 273 } 274 275 // callExecuteFetchAsApp implements a wrapper 276 // for fuzzing ExecuteFetchAsApp 277 func (fs *fuzzStore) callExecuteFetchAsApp() error { 278 tablet, err := fs.getTablet() 279 if err != nil { 280 return err 281 } 282 byteQuery, err := fs.getBytes() 283 if err != nil { 284 return err 285 } 286 maxRows, err := fs.getInt() 287 if err != nil { 288 return err 289 } 290 _, _ = fs.client.ExecuteFetchAsApp(context.Background(), tablet, false, byteQuery, maxRows) 291 return nil 292 } 293 294 // callInitPrimary implements a wrapper 295 // for fuzzing InitPrimary 296 func (fs *fuzzStore) callInitPrimary() error { 297 tablet, err := fs.getTablet() 298 if err != nil { 299 return err 300 } 301 _, _ = fs.client.InitPrimary(context.Background(), tablet, false) 302 return nil 303 } 304 305 // callResetReplication implements a wrapper 306 // for fuzzing ResetReplication 307 func (fs *fuzzStore) callResetReplication() error { 308 tablet, err := fs.getTablet() 309 if err != nil { 310 return err 311 } 312 _ = fs.client.ResetReplication(context.Background(), tablet) 313 return nil 314 } 315 316 // callGetReplicas implements a wrapper 317 // for fuzzing GetReplicas 318 func (fs *fuzzStore) callGetReplicas() error { 319 tablet, err := fs.getTablet() 320 if err != nil { 321 return err 322 } 323 _, _ = fs.client.GetReplicas(context.Background(), tablet) 324 return nil 325 } 326 327 // callStartReplication implements a wrapper 328 // for fuzzing StartReplication 329 func (fs *fuzzStore) callStartReplication() error { 330 tablet, err := fs.getTablet() 331 if err != nil { 332 return err 333 } 334 _ = fs.client.StartReplication(context.Background(), tablet, false) 335 return nil 336 } 337 338 // callStopReplication implements a wrapper 339 // for fuzzing StopReplication 340 func (fs *fuzzStore) callStopReplication() error { 341 tablet, err := fs.getTablet() 342 if err != nil { 343 return err 344 } 345 _ = fs.client.StopReplication(context.Background(), tablet) 346 return nil 347 } 348 349 // callPrimaryPosition implements a wrapper 350 // for fuzzing PrimaryPosition 351 func (fs *fuzzStore) callPrimaryPosition() error { 352 tablet, err := fs.getTablet() 353 if err != nil { 354 return err 355 } 356 _, _ = fs.client.PrimaryPosition(context.Background(), tablet) 357 return nil 358 } 359 360 // callReplicationStatus implements a wrapper 361 // for fuzzing ReplicationStatus 362 func (fs *fuzzStore) callReplicationStatus() error { 363 tablet, err := fs.getTablet() 364 if err != nil { 365 return err 366 } 367 _, _ = fs.client.ReplicationStatus(context.Background(), tablet) 368 return nil 369 } 370 371 // callFullStatus implements a wrapper 372 // for fuzzing FullStatus 373 func (fs *fuzzStore) callFullStatus() error { 374 tablet, err := fs.getTablet() 375 if err != nil { 376 return err 377 } 378 _, _ = fs.client.FullStatus(context.Background(), tablet) 379 return nil 380 } 381 382 // callPrimaryStatus implements a wrapper 383 // for fuzzing PrimaryStatus 384 func (fs *fuzzStore) callPrimaryStatus() error { 385 tablet, err := fs.getTablet() 386 if err != nil { 387 return err 388 } 389 _, _ = fs.client.PrimaryStatus(context.Background(), tablet) 390 return nil 391 } 392 393 // callDemotePrimary implements a wrapper 394 // for fuzzing DemotePrimary 395 func (fs *fuzzStore) callDemotePrimary() error { 396 tablet, err := fs.getTablet() 397 if err != nil { 398 return err 399 } 400 _, _ = fs.client.DemotePrimary(context.Background(), tablet) 401 return nil 402 } 403 404 // callUndoDemotePrimary implements a wrapper 405 // for fuzzing UndoDemotePrimary 406 func (fs *fuzzStore) callUndoDemotePrimary() error { 407 tablet, err := fs.getTablet() 408 if err != nil { 409 return err 410 } 411 _ = fs.client.UndoDemotePrimary(context.Background(), tablet, false) 412 return nil 413 } 414 415 // callReplicaWasPromoted implements a wrapper 416 // for fuzzing ReplicaWasPromoted 417 func (fs *fuzzStore) callReplicaWasPromoted() error { 418 tablet, err := fs.getTablet() 419 if err != nil { 420 return err 421 } 422 _ = fs.client.ReplicaWasPromoted(context.Background(), tablet) 423 return nil 424 } 425 426 // callResetReplicationParameters implements a wrapper 427 // for fuzzing ResetReplicationParameters 428 func (fs *fuzzStore) callResetReplicationParameters() error { 429 tablet, err := fs.getTablet() 430 if err != nil { 431 return err 432 } 433 _ = fs.client.ResetReplicationParameters(context.Background(), tablet) 434 return nil 435 } 436 437 // callPromoteReplica implements a wrapper 438 // for fuzzing PromoteReplica 439 func (fs *fuzzStore) callPromoteReplica() error { 440 tablet, err := fs.getTablet() 441 if err != nil { 442 return err 443 } 444 _, _ = fs.client.PromoteReplica(context.Background(), tablet, false) 445 return nil 446 } 447 448 // callStopReplicationAndGetStatus implements a wrapper 449 // for fuzzing StopReplicationAndGetStatus 450 func (fs *fuzzStore) callStopReplicationAndGetStatus() error { 451 tablet, err := fs.getTablet() 452 if err != nil { 453 return err 454 } 455 _, _, _ = fs.client.StopReplicationAndGetStatus(context.Background(), tablet, 0) 456 return nil 457 } 458 459 // callReplicaWasRestarted implements a wrapper 460 // for fuzzing ReplicaWasRestarted 461 func (fs *fuzzStore) callReplicaWasRestarted() error { 462 tablet, err := fs.getTablet() 463 if err != nil { 464 return err 465 } 466 parent, err := fs.getTabletAlias() 467 if err != nil { 468 return err 469 } 470 _ = fs.client.ReplicaWasRestarted(context.Background(), tablet, parent) 471 return nil 472 } 473 474 // callWaitForPosition implements a wrapper 475 // for fuzzing WaitForPosition 476 func (fs *fuzzStore) callWaitForPosition() error { 477 tablet, err := fs.getTablet() 478 if err != nil { 479 return err 480 } 481 pos, err := fs.getString() 482 if err != nil { 483 return err 484 } 485 _ = fs.client.WaitForPosition(context.Background(), tablet, pos) 486 return nil 487 } 488 489 // callVReplicationExec implements a wrapper 490 // for fuzzing VReplicationExec 491 func (fs *fuzzStore) callVReplicationExec() error { 492 tablet, err := fs.getTablet() 493 if err != nil { 494 return err 495 } 496 query, err := fs.getString() 497 if err != nil { 498 return err 499 } 500 _, _ = fs.client.VReplicationExec(context.Background(), tablet, query) 501 return nil 502 } 503 504 // callVExec implements a wrapper 505 // for fuzzing VExec 506 func (fs *fuzzStore) callVExec() error { 507 tablet, err := fs.getTablet() 508 if err != nil { 509 return err 510 } 511 query, err := fs.getString() 512 if err != nil { 513 return err 514 } 515 workflow, err := fs.getString() 516 if err != nil { 517 return err 518 } 519 keyspace, err := fs.getString() 520 if err != nil { 521 return err 522 } 523 _, _ = fs.client.VExec(context.Background(), tablet, query, workflow, keyspace) 524 return nil 525 } 526 527 // callVReplicationWaitForPos implements a wrapper 528 // for fuzzing VReplicationWaitForPos 529 func (fs *fuzzStore) callVReplicationWaitForPos() error { 530 tablet, err := fs.getTablet() 531 if err != nil { 532 return err 533 } 534 pos, err := fs.getString() 535 if err != nil { 536 return err 537 } 538 timeCreatedNS, err := fs.getInt() 539 if err != nil { 540 return err 541 } 542 _ = fs.client.VReplicationWaitForPos(context.Background(), tablet, timeCreatedNS, pos) 543 return nil 544 } 545 546 // callSetReplicationSource implements a wrapper 547 // for fuzzing SetReplicationSource 548 func (fs *fuzzStore) callSetReplicationSource() error { 549 tablet, err := fs.getTablet() 550 if err != nil { 551 return err 552 } 553 pos, err := fs.getString() 554 if err != nil { 555 return err 556 } 557 timeCreatedNS, err := fs.getInt() 558 if err != nil { 559 return err 560 } 561 parent, err := fs.getTabletAlias() 562 if err != nil { 563 return err 564 } 565 _ = fs.client.SetReplicationSource(context.Background(), tablet, parent, int64(timeCreatedNS), pos, false, false) 566 return nil 567 } 568 569 // callInitReplica implements a wrapper 570 // for fuzzing InitReplica 571 func (fs *fuzzStore) callInitReplica() error { 572 tablet, err := fs.getTablet() 573 if err != nil { 574 return err 575 } 576 timeCreatedNS, err := fs.getInt() 577 if err != nil { 578 return err 579 } 580 parent, err := fs.getTabletAlias() 581 if err != nil { 582 return err 583 } 584 replicationPosition, err := fs.getString() 585 if err != nil { 586 return err 587 } 588 _ = fs.client.InitReplica(context.Background(), tablet, parent, replicationPosition, int64(timeCreatedNS), false) 589 return nil 590 } 591 592 // callPopulateReparentJournal implements a wrapper 593 // for fuzzing PopulateReparentJournal 594 func (fs *fuzzStore) callPopulateReparentJournal() error { 595 tablet, err := fs.getTablet() 596 if err != nil { 597 return err 598 } 599 timeCreatedNS, err := fs.getInt() 600 if err != nil { 601 return err 602 } 603 tabletAlias, err := fs.getTabletAlias() 604 if err != nil { 605 return err 606 } 607 actionName, err := fs.getString() 608 if err != nil { 609 return err 610 } 611 pos, err := fs.getString() 612 if err != nil { 613 return err 614 } 615 _ = fs.client.PopulateReparentJournal(context.Background(), tablet, int64(timeCreatedNS), actionName, tabletAlias, pos) 616 return nil 617 } 618 619 // executeInRandomOrder calls the fuzz targets in 620 // the order specified by f.executionOrder 621 func (fs *fuzzStore) executeInRandomOrder() { 622 maxTargets := 24 623 for _, execInt := range fs.executionOrder { 624 var err error 625 switch execInt % maxTargets { 626 case 0: 627 err = fs.callInitPrimary() 628 case 1: 629 err = fs.callResetReplication() 630 case 2: 631 err = fs.callGetReplicas() 632 case 3: 633 err = fs.callStartReplication() 634 case 4: 635 err = fs.callStopReplication() 636 case 5: 637 err = fs.callPrimaryPosition() 638 case 7: 639 err = fs.callReplicationStatus() 640 case 8: 641 err = fs.callPrimaryStatus() 642 case 9: 643 err = fs.callDemotePrimary() 644 case 11: 645 err = fs.callUndoDemotePrimary() 646 case 12: 647 err = fs.callReplicaWasPromoted() 648 case 13: 649 err = fs.callPromoteReplica() 650 case 14: 651 err = fs.callReplicaWasRestarted() 652 case 15: 653 err = fs.callWaitForPosition() 654 case 16: 655 err = fs.callVReplicationExec() 656 case 17: 657 err = fs.callVExec() 658 case 18: 659 err = fs.callStopReplicationAndGetStatus() 660 case 19: 661 err = fs.callExecuteFetchAsApp() 662 case 20: 663 err = fs.callVReplicationWaitForPos() 664 case 21: 665 err = fs.callSetReplicationSource() 666 case 22: 667 err = fs.callInitReplica() 668 case 23: 669 err = fs.callPopulateReparentJournal() 670 case 24: 671 err = fs.callResetReplicationParameters() 672 case 25: 673 err = fs.callFullStatus() 674 } 675 676 // err means that fuzzStore doesn't have any data 677 // to pass to the target so we return here 678 if err != nil { 679 return 680 } 681 } 682 } 683 684 // FuzzGRPCTMServer implements the fuzzer. 685 func FuzzGRPCTMServer(data []byte) int { 686 initter.Do(onceInit) 687 f := fuzz.NewConsumer(data) 688 fs, err := newFuzzStore(f) 689 if err != nil { 690 return 0 691 } 692 t := &testing.T{} 693 694 // Listen on a random port 695 listener, err := net.Listen("tcp", "127.0.0.1:0") 696 if err != nil { 697 return 0 698 } 699 defer listener.Close() 700 701 host := listener.Addr().(*net.TCPAddr).IP.String() 702 port := int32(listener.Addr().(*net.TCPAddr).Port) 703 _, _ = host, port 704 for _, tablet := range fs.tablets { 705 tablet.Hostname = host 706 tablet.PortMap = map[string]int32{ 707 "grpc": port, 708 } 709 } 710 s := grpc.NewServer() 711 fakeTM := tmrpctest.NewFakeRPCTM(t) 712 grpctmserver.RegisterForTest(s, fakeTM) 713 go s.Serve(listener) 714 defer s.Stop() 715 716 // Create a gRPC client to talk to the fake tablet. 717 client := grpctmclient.NewClient() 718 defer client.Close() 719 fs.client = client 720 721 // Call the targets in random order. 722 fs.executeInRandomOrder() 723 724 return 1 725 }