From 2f6ae06dc0cc4cb17819e4d78c466a787a92009d Mon Sep 17 00:00:00 2001 From: AdamKorcz Date: Fri, 29 Oct 2021 11:04:53 +0100 Subject: [PATCH] Fuzzing: Add more and modify exiting Signed-off-by: AdamKorcz --- go/test/fuzzing/ast_fuzzer.go | 59 ++ go/test/fuzzing/oss_fuzz_build.sh | 43 +- go/test/fuzzing/vt_schema_fuzzer.go | 88 +++ go/test/fuzzing/vttablet_fuzzer.go | 728 ++++++++++++++++++++++ go/vt/vtgate/engine/fuzz.go | 107 ++++ go/vt/vtgate/engine/fuzz_flaky_test.go | 330 ---------- go/vt/vtgate/planbuilder/abstract/fuzz.go | 5 +- go/vt/vtgate/planbuilder/fuzz.go | 105 ++++ go/vt/vtgate/vindexes/fuzz.go | 244 ++++++++ 9 files changed, 1369 insertions(+), 340 deletions(-) create mode 100644 go/test/fuzzing/ast_fuzzer.go create mode 100644 go/test/fuzzing/vt_schema_fuzzer.go create mode 100644 go/test/fuzzing/vttablet_fuzzer.go create mode 100644 go/vt/vtgate/engine/fuzz.go delete mode 100644 go/vt/vtgate/engine/fuzz_flaky_test.go create mode 100644 go/vt/vtgate/planbuilder/fuzz.go create mode 100644 go/vt/vtgate/vindexes/fuzz.go diff --git a/go/test/fuzzing/ast_fuzzer.go b/go/test/fuzzing/ast_fuzzer.go new file mode 100644 index 00000000000..bcb106bb398 --- /dev/null +++ b/go/test/fuzzing/ast_fuzzer.go @@ -0,0 +1,59 @@ +//go:build gofuzz +// +build gofuzz + +/* +Copyright 2021 The Vitess Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fuzzing + +import ( + "vitess.io/vitess/go/vt/sqlparser" +) + +// FuzzEqualsSQLNode implements the fuzzer +func FuzzEqualsSQLNode(data []byte) int { + if len(data) < 10 { + return 0 + } + if (len(data) % 2) != 0 { + return 0 + } + firstHalf := string(data[:len(data)/2]) + secondHalf := string(data[(len(data)/2)+1:]) + inA, err := sqlparser.Parse(firstHalf) + if err != nil { + return 0 + } + inB, err := sqlparser.Parse(secondHalf) + if err != nil { + return 0 + } + + // There are 3 targets in this fuzzer: + // 1) sqlparser.EqualsSQLNode + // 2) sqlparser.CloneSQLNode + // 3) sqlparser.VisitSQLNode + + // Target 1: + _ = sqlparser.EqualsSQLNode(inA, inB) + + // Target 2: + newSQLNode := sqlparser.CloneSQLNode(inA) + if !sqlparser.EqualsSQLNode(inA, newSQLNode) { + panic("These two nodes should be identical") + } + + // Target 3: + _ = sqlparser.VisitSQLNode(inA, func(node sqlparser.SQLNode) (bool, error) { return false, nil }) + return 1 +} diff --git a/go/test/fuzzing/oss_fuzz_build.sh b/go/test/fuzzing/oss_fuzz_build.sh index c8c7b74ea08..9a3ad57f2af 100644 --- a/go/test/fuzzing/oss_fuzz_build.sh +++ b/go/test/fuzzing/oss_fuzz_build.sh @@ -14,23 +14,54 @@ # See the License for the specific language governing permissions and # limitations under the License. +set -o nounset +set -o pipefail +set -o errexit +set -x + +go get github.com/AdaLogics/go-fuzz-headers +go mod vendor + +#consistent_lookup_test.go is needed for loggingVCursor +mv ./go/vt/vtgate/vindexes/consistent_lookup_test.go \ + ./go/vt/vtgate/vindexes/consistent_lookup_test_fuzz.go +compile_go_fuzzer vitess.io/vitess/go/vt/vtgate/vindexes FuzzVindex fuzz_vindex + +# fake_vcursor_test.go is needed for loggingVCursor +mv ./go/vt/vtgate/engine/fake_vcursor_test.go \ + ./go/vt/vtgate/engine/fake_vcursor.go +compile_go_fuzzer vitess.io/vitess/go/vt/vtgate/engine FuzzEngine engine_fuzzer + +# plan_test.go is needed for vschemaWrapper +mv ./go/vt/vtgate/planbuilder/plan_test.go \ + ./go/vt/vtgate/planbuilder/plan_test_fuzz.go +compile_go_fuzzer vitess.io/vitess/go/vt/vtgate/planbuilder FuzzTestBuilder fuzz_test_builder gofuzz + + compile_go_fuzzer vitess.io/vitess/go/test/fuzzing Fuzz vtctl_fuzzer compile_go_fuzzer vitess.io/vitess/go/test/fuzzing FuzzIsDML is_dml_fuzzer compile_go_fuzzer vitess.io/vitess/go/test/fuzzing FuzzNormalizer normalizer_fuzzer compile_go_fuzzer vitess.io/vitess/go/test/fuzzing FuzzParser parser_fuzzer +compile_go_fuzzer vitess.io/vitess/go/test/fuzzing FuzzGRPCTMServer fuzz_grpc_tm_server +compile_go_fuzzer vitess.io/vitess/go/test/fuzzing FuzzOnlineDDLFromCommentedStatement fuzz_online_ddl_from_commented_statement +compile_go_fuzzer vitess.io/vitess/go/test/fuzzing FuzzNewOnlineDDLs fuzz_new_online_ddls +compile_go_fuzzer vitess.io/vitess/go/test/fuzzing FuzzEqualsSQLNode fuzz_equals_sql_node compile_go_fuzzer vitess.io/vitess/go/mysql FuzzWritePacket write_packet_fuzzer compile_go_fuzzer vitess.io/vitess/go/mysql FuzzHandleNextCommand handle_next_command_fuzzer compile_go_fuzzer vitess.io/vitess/go/mysql FuzzReadQueryResults read_query_results_fuzzer compile_go_fuzzer vitess.io/vitess/go/mysql FuzzTLSServer fuzz_tls + +# Several test utils are needed from suite_test.go: +mv ./go/vt/vtgate/grpcvtgateconn/suite_test.go \ + ./go/vt/vtgate/grpcvtgateconn/suite_test_fuzz.go +mv ./go/vt/vtgate/grpcvtgateconn/fuzz_flaky_test.go \ + ./go/vt/vtgate/grpcvtgateconn/fuzz.go compile_go_fuzzer vitess.io/vitess/go/vt/vtgate/grpcvtgateconn Fuzz grpc_vtgate_fuzzer -compile_go_fuzzer vitess.io/vitess/go/vt/vtgate/planbuilder/abstract FuzzAnalyse planbuilder_fuzzer gofuzz -mv ./go/vt/vtgate/engine/fake_vcursor_test.go \ - ./go/vt/vtgate/engine/fake_vcursor.go -mv ./go/vt/vtgate/engine/fuzz_flaky_test.go ./go/vt/vtgate/engine/engine_fuzz.go -compile_go_fuzzer vitess.io/vitess/go/vt/vtgate/engine FuzzEngine engine_fuzzer +compile_go_fuzzer vitess.io/vitess/go/vt/vtgate/planbuilder/abstract FuzzAnalyse fuzz_analyse gofuzz + + # Build dictionaries cp $SRC/vitess/go/test/fuzzing/vtctl_fuzzer.dict $OUT/ - diff --git a/go/test/fuzzing/vt_schema_fuzzer.go b/go/test/fuzzing/vt_schema_fuzzer.go new file mode 100644 index 00000000000..2092eac866a --- /dev/null +++ b/go/test/fuzzing/vt_schema_fuzzer.go @@ -0,0 +1,88 @@ +//go:build gofuzz +// +build gofuzz + +/* +Copyright 2021 The Vitess Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fuzzing + +import ( + fuzz "github.com/AdaLogics/go-fuzz-headers" + + "vitess.io/vitess/go/vt/schema" + "vitess.io/vitess/go/vt/sqlparser" +) + +// FuzzOnlineDDLFromCommentedStatement implements a fuzzer +// that targets schema.OnlineDDLFromCommentedStatement +func FuzzOnlineDDLFromCommentedStatement(data []byte) int { + stmt, err := sqlparser.Parse(string(data)) + if err != nil { + return 0 + } + onlineDDL, err := schema.OnlineDDLFromCommentedStatement(stmt) + if err != nil { + return 0 + } + _, _ = onlineDDL.GetAction() + _, _, _ = onlineDDL.GetActionStr() + _ = onlineDDL.GetGCUUID() + return 1 +} + +// FuzzNewOnlineDDLs implements a fuzzer that +// targets schema.NewOnlineDDLs +func FuzzNewOnlineDDLs(data []byte) int { + f := fuzz.NewConsumer(data) + + keyspace, err := f.GetString() + if err != nil { + return 0 + } + + ddlstmtString, err := f.GetString() + if err != nil { + return 0 + } + ddlStmt, _, err := schema.ParseOnlineDDLStatement(ddlstmtString) + if err != nil { + return 0 + } + + sql, err := f.GetString() + if err != nil { + return 0 + } + + ddlStrategySetting := &schema.DDLStrategySetting{} + err = f.GenerateStruct(ddlStrategySetting) + if err != nil { + return 0 + } + + requestContext, err := f.GetString() + if err != nil { + return 0 + } + + onlineDDLs, err := schema.NewOnlineDDLs(keyspace, sql, ddlStmt, ddlStrategySetting, requestContext) + if err != nil { + return 0 + } + for _, onlineDDL := range onlineDDLs { + _, _ = onlineDDL.GetAction() + _, _, _ = onlineDDL.GetActionStr() + _ = onlineDDL.GetGCUUID() + } + return 1 +} diff --git a/go/test/fuzzing/vttablet_fuzzer.go b/go/test/fuzzing/vttablet_fuzzer.go new file mode 100644 index 00000000000..3a5fac07643 --- /dev/null +++ b/go/test/fuzzing/vttablet_fuzzer.go @@ -0,0 +1,728 @@ +//go:build gofuzz +// +build gofuzz + +/* +Copyright 2021 The Vitess Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fuzzing + +import ( + "context" + "fmt" + "net" + "sync" + "testing" + + "google.golang.org/grpc" + + "vitess.io/vitess/go/vt/vttablet/grpctmclient" + "vitess.io/vitess/go/vt/vttablet/grpctmserver" + "vitess.io/vitess/go/vt/vttablet/tmrpctest" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" +) + +var initter sync.Once + +func onceInit() { + testing.Init() +} + +// createTablets creates a slice of Tablets that can +// be used by the fuzz targets. +func (fs *fuzzStore) createTablets(f *fuzz.ConsumeFuzzer) error { + tabletCount, err := f.GetInt() + if err != nil { + return err + } + tabletCount = tabletCount % 40 + if tabletCount == 0 { + return fmt.Errorf("We don't need a nil-len list") + } + tablets := make([]*topodatapb.Tablet, 0) + for i := 0; i < tabletCount; i++ { + tablet := &topodatapb.Tablet{} + err = f.GenerateStruct(tablet) + if err != nil { + return err + } + tablets = append(tablets, tablet) + } + fs.tablets = tablets + return nil +} + +// createTabletAliases creates a slice of TabletAliases that can +// be used by the fuzz targets. +func (fs *fuzzStore) createTabletAliases(f *fuzz.ConsumeFuzzer) error { + tabletAliasCount, err := f.GetInt() + if err != nil { + return err + } + tabletAliasCount = tabletAliasCount % 40 + if tabletAliasCount == 0 { + return fmt.Errorf("We don't need a nil-len list") + } + tabletAliases := make([]*topodatapb.TabletAlias, 0) + for i := 0; i < tabletAliasCount; i++ { + tabletAlias := &topodatapb.TabletAlias{} + err = f.GenerateStruct(tabletAlias) + if err != nil { + return err + } + tabletAliases = append(tabletAliases, tabletAlias) + } + fs.tabletAliases = tabletAliases + return nil +} + +// createStrings creates a slice of strings that can be used +// by the fuzz tagets. +func (fs *fuzzStore) createStrings(f *fuzz.ConsumeFuzzer) error { + stringCount, err := f.GetInt() + if err != nil { + return err + } + stringCount = stringCount % 100 + if stringCount == 0 { + return fmt.Errorf("We don't need a nil-len list") + } + stringSlice := make([]string, 0) + for i := 0; i < stringCount; i++ { + newString, err := f.GetString() + if err != nil { + return err + } + stringSlice = append(stringSlice, newString) + } + fs.miscStrings = stringSlice + return nil +} + +// createBytes creates a slice of byte slices that can be used +// by the fuzz targets. +func (fs *fuzzStore) createBytes(f *fuzz.ConsumeFuzzer) error { + bytesCount, err := f.GetInt() + if err != nil { + return err + } + bytesCount = bytesCount % 40 + if bytesCount == 0 { + return fmt.Errorf("We don't need a nil-len list") + } + byteSlice := make([][]byte, 0) + for i := 0; i < bytesCount; i++ { + newBytes, err := f.GetBytes() + if err != nil { + return err + } + byteSlice = append(byteSlice, newBytes) + } + fs.miscBytes = byteSlice + return nil +} + +// createInts creates a list of ints that can be used +// by the fuzz targets. +func (fs *fuzzStore) createInts(f *fuzz.ConsumeFuzzer) error { + intCount, err := f.GetInt() + if err != nil { + return err + } + intCount = intCount % 40 + if intCount == 0 { + return fmt.Errorf("We don't need a nil-len list") + } + intSlice := make([]int, 0) + for i := 0; i < intCount; i++ { + newInt, err := f.GetInt() + if err != nil { + return err + } + intSlice = append(intSlice, newInt) + } + fs.miscInts = intSlice + return nil +} + +// createExecutionOrder creates an array of ints that will later +// be used to determine the order in which we call our fuzz targets. +func (fs *fuzzStore) createExecutionOrder(f *fuzz.ConsumeFuzzer) error { + intCount, err := f.GetInt() + if err != nil { + return err + } + intCount = intCount % 60 + if intCount == 0 { + return fmt.Errorf("We don't need a nil-len list") + } + executionOrder := make([]int, 0) + for i := 0; i < intCount; i++ { + newInt, err := f.GetInt() + if err != nil { + return err + } + executionOrder = append(executionOrder, newInt) + } + fs.executionOrder = executionOrder + return nil +} + +type fuzzStore struct { + tablets []*topodatapb.Tablet + tabletAliases []*topodatapb.TabletAlias + miscStrings []string + miscBytes [][]byte + miscInts []int + client *grpctmclient.Client + executionOrder []int +} + +// newFuzzStore creates a store of the data that is needed +// for the fuzz targets in FuzzGRPCTMServer. The reason +// a store is created is because the set up of the server +// comes with a big expense, and having data ready to pass +// to the targets improves efficiency. +func newFuzzStore(f *fuzz.ConsumeFuzzer) (*fuzzStore, error) { + fs := &fuzzStore{} + + err := fs.createTablets(f) + if err != nil { + return nil, err + } + + err = fs.createTabletAliases(f) + if err != nil { + return nil, err + } + + err = fs.createStrings(f) + if err != nil { + return nil, err + } + + err = fs.createBytes(f) + if err != nil { + return nil, err + } + + err = fs.createInts(f) + if err != nil { + return nil, err + } + + err = fs.createExecutionOrder(f) + if err != nil { + return nil, err + } + return fs, nil +} + +func (fs *fuzzStore) getTablet() (*topodatapb.Tablet, error) { + if len(fs.tablets) == 0 { + return nil, fmt.Errorf("Not enough tablets") + } + tablet := fs.tablets[0] + fs.tablets = fs.tablets[1:] + return tablet, nil +} + +func (fs *fuzzStore) getString() (string, error) { + if len(fs.miscStrings) == 0 { + return "", fmt.Errorf("Not enough tablets") + } + returnString := fs.miscStrings[0] + fs.miscStrings = fs.miscStrings[1:] + return returnString, nil +} + +func (fs *fuzzStore) getInt() (int, error) { + if len(fs.miscInts) == 0 { + return 0, fmt.Errorf("Not enough tablets") + } + returnInt := fs.miscInts[0] + fs.miscInts = fs.miscInts[1:] + return returnInt, nil +} + +func (fs *fuzzStore) getBytes() ([]byte, error) { + if len(fs.miscBytes) == 0 { + return nil, fmt.Errorf("Not enough tablets") + } + returnBytes := fs.miscBytes[0] + fs.miscBytes = fs.miscBytes[1:] + return returnBytes, nil +} + +func (fs *fuzzStore) getTabletAlias() (*topodatapb.TabletAlias, error) { + if len(fs.tabletAliases) == 0 { + return nil, fmt.Errorf("Not enough tablets") + } + tabletAlias := fs.tabletAliases[0] + fs.tabletAliases = fs.tabletAliases[1:] + return tabletAlias, nil +} + +// callExecuteFetchAsApp implements a wrapper +// for fuzzing ExecuteFetchAsApp +func (fs *fuzzStore) callExecuteFetchAsApp() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + byteQuery, err := fs.getBytes() + if err != nil { + return err + } + maxRows, err := fs.getInt() + if err != nil { + return err + } + _, _ = fs.client.ExecuteFetchAsApp(context.Background(), tablet, false, byteQuery, maxRows) + return nil +} + +// callInitMaster implements a wrapper +// for fuzzing InitMaster +func (fs *fuzzStore) callInitMaster() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _, _ = fs.client.InitMaster(context.Background(), tablet) + return nil +} + +// callResetReplication implements a wrapper +// for fuzzing ResetReplication +func (fs *fuzzStore) callResetReplication() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _ = fs.client.ResetReplication(context.Background(), tablet) + return nil +} + +// callGetReplicas implements a wrapper +// for fuzzing GetReplicas +func (fs *fuzzStore) callGetReplicas() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _, _ = fs.client.GetReplicas(context.Background(), tablet) + return nil +} + +// callStartReplication implements a wrapper +// for fuzzing StartReplication +func (fs *fuzzStore) callStartReplication() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _ = fs.client.StartReplication(context.Background(), tablet) + return nil +} + +// callStopReplication implements a wrapper +// for fuzzing StopReplication +func (fs *fuzzStore) callStopReplication() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _ = fs.client.StopReplication(context.Background(), tablet) + return nil +} + +// callMasterPosition implements a wrapper +// for fuzzing MasterPosition +func (fs *fuzzStore) callMasterPosition() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _, _ = fs.client.MasterPosition(context.Background(), tablet) + return nil +} + +// callDemoteMaster implements a wrapper +// for fuzzing DemoteMaster +func (fs *fuzzStore) callDemoteMaster() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _, _ = fs.client.DemoteMaster(context.Background(), tablet) + return nil +} + +// callReplicationStatus implements a wrapper +// for fuzzing ReplicationStatus +func (fs *fuzzStore) callReplicationStatus() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _, _ = fs.client.ReplicationStatus(context.Background(), tablet) + return nil +} + +// callMasterStatus implements a wrapper +// for fuzzing MasterStatus +func (fs *fuzzStore) callMasterStatus() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _, _ = fs.client.MasterStatus(context.Background(), tablet) + return nil +} + +// callDemotePrimary implements a wrapper +// for fuzzing DemotePrimary +func (fs *fuzzStore) callDemotePrimary() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _, _ = fs.client.DemotePrimary(context.Background(), tablet) + return nil +} + +// callUndoDemoteMaster implements a wrapper +// for fuzzing UndoDemoteMaster +func (fs *fuzzStore) callUndoDemoteMaster() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _ = fs.client.UndoDemoteMaster(context.Background(), tablet) + return nil +} + +// callUndoDemotePrimary implements a wrapper +// for fuzzing UndoDemotePrimary +func (fs *fuzzStore) callUndoDemotePrimary() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _ = fs.client.UndoDemotePrimary(context.Background(), tablet) + return nil +} + +// callReplicaWasPromoted implements a wrapper +// for fuzzing ReplicaWasPromoted +func (fs *fuzzStore) callReplicaWasPromoted() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _ = fs.client.ReplicaWasPromoted(context.Background(), tablet) + return nil +} + +// callPromoteReplica implements a wrapper +// for fuzzing PromoteReplica +func (fs *fuzzStore) callPromoteReplica() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _, _ = fs.client.PromoteReplica(context.Background(), tablet) + return nil +} + +// callStopReplicationAndGetStatus implements a wrapper +// for fuzzing StopReplicationAndGetStatus +func (fs *fuzzStore) callStopReplicationAndGetStatus() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + _, _, _ = fs.client.StopReplicationAndGetStatus(context.Background(), tablet, 0) + return nil +} + +// callReplicaWasRestarted implements a wrapper +// for fuzzing ReplicaWasRestarted +func (fs *fuzzStore) callReplicaWasRestarted() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + parent, err := fs.getTabletAlias() + if err != nil { + return err + } + _ = fs.client.ReplicaWasRestarted(context.Background(), tablet, parent) + return nil +} + +// callWaitForPosition implements a wrapper +// for fuzzing WaitForPosition +func (fs *fuzzStore) callWaitForPosition() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + pos, err := fs.getString() + if err != nil { + return err + } + _ = fs.client.WaitForPosition(context.Background(), tablet, pos) + return nil +} + +// callVReplicationExec implements a wrapper +// for fuzzing VReplicationExec +func (fs *fuzzStore) callVReplicationExec() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + query, err := fs.getString() + if err != nil { + return err + } + _, _ = fs.client.VReplicationExec(context.Background(), tablet, query) + return nil +} + +// callVExec implements a wrapper +// for fuzzing VExec +func (fs *fuzzStore) callVExec() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + query, err := fs.getString() + if err != nil { + return err + } + workflow, err := fs.getString() + if err != nil { + return err + } + keyspace, err := fs.getString() + if err != nil { + return err + } + _, _ = fs.client.VExec(context.Background(), tablet, query, workflow, keyspace) + return nil +} + +// callVReplicationWaitForPos implements a wrapper +// for fuzzing VReplicationWaitForPos +func (fs *fuzzStore) callVReplicationWaitForPos() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + pos, err := fs.getString() + if err != nil { + return err + } + timeCreatedNS, err := fs.getInt() + if err != nil { + return err + } + _ = fs.client.VReplicationWaitForPos(context.Background(), tablet, timeCreatedNS, pos) + return nil +} + +// callSetMaster implements a wrapper +// for fuzzing SetMaster +func (fs *fuzzStore) callSetMaster() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + pos, err := fs.getString() + if err != nil { + return err + } + timeCreatedNS, err := fs.getInt() + if err != nil { + return err + } + parent, err := fs.getTabletAlias() + if err != nil { + return err + } + _ = fs.client.SetMaster(context.Background(), tablet, parent, int64(timeCreatedNS), pos, false) + return nil +} + +// callInitReplica implements a wrapper +// for fuzzing InitReplica +func (fs *fuzzStore) callInitReplica() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + timeCreatedNS, err := fs.getInt() + if err != nil { + return err + } + parent, err := fs.getTabletAlias() + if err != nil { + return err + } + replicationPosition, err := fs.getString() + if err != nil { + return err + } + _ = fs.client.InitReplica(context.Background(), tablet, parent, replicationPosition, int64(timeCreatedNS)) + return nil +} + +// callPopulateReparentJournal implements a wrapper +// for fuzzing PopulateReparentJournal +func (fs *fuzzStore) callPopulateReparentJournal() error { + tablet, err := fs.getTablet() + if err != nil { + return err + } + timeCreatedNS, err := fs.getInt() + if err != nil { + return err + } + tabletAlias, err := fs.getTabletAlias() + if err != nil { + return err + } + actionName, err := fs.getString() + if err != nil { + return err + } + pos, err := fs.getString() + if err != nil { + return err + } + _ = fs.client.PopulateReparentJournal(context.Background(), tablet, int64(timeCreatedNS), actionName, tabletAlias, pos) + return nil +} + +// executeInRandomOrder calls the fuzz targets in +// the order specified by f.executionOrder +func (fs *fuzzStore) executeInRandomOrder() { + maxTargets := 24 + for _, execInt := range fs.executionOrder { + var err error + switch execInt % maxTargets { + case 0: + err = fs.callInitMaster() + case 1: + err = fs.callResetReplication() + case 2: + err = fs.callGetReplicas() + case 3: + err = fs.callStartReplication() + case 4: + err = fs.callStopReplication() + case 5: + err = fs.callMasterPosition() + case 6: + err = fs.callDemoteMaster() + case 7: + err = fs.callReplicationStatus() + case 8: + err = fs.callMasterStatus() + case 9: + err = fs.callDemotePrimary() + case 10: + err = fs.callUndoDemoteMaster() + case 11: + err = fs.callUndoDemotePrimary() + case 12: + err = fs.callReplicaWasPromoted() + case 13: + err = fs.callPromoteReplica() + case 14: + err = fs.callReplicaWasRestarted() + case 15: + err = fs.callWaitForPosition() + case 16: + err = fs.callVReplicationExec() + case 17: + err = fs.callVExec() + case 18: + err = fs.callStopReplicationAndGetStatus() + case 19: + err = fs.callExecuteFetchAsApp() + case 20: + err = fs.callVReplicationWaitForPos() + case 21: + err = fs.callSetMaster() + case 22: + err = fs.callInitReplica() + case 23: + err = fs.callPopulateReparentJournal() + } + + // err means that fuzzStore doesn't have any data + // to pass to the target so we return here + if err != nil { + return + } + } +} + +// FuzzGRPCTMServer implements the fuzzer. +func FuzzGRPCTMServer(data []byte) int { + initter.Do(onceInit) + f := fuzz.NewConsumer(data) + fs, err := newFuzzStore(f) + if err != nil { + return 0 + } + t := &testing.T{} + + // Listen on a random port + listener, err := net.Listen("tcp", ":0") + if err != nil { + return 0 + } + defer listener.Close() + + host := listener.Addr().(*net.TCPAddr).IP.String() + port := int32(listener.Addr().(*net.TCPAddr).Port) + _, _ = host, port + for _, tablet := range fs.tablets { + tablet.Hostname = host + tablet.PortMap = map[string]int32{ + "grpc": port, + } + } + s := grpc.NewServer() + fakeTM := tmrpctest.NewFakeRPCTM(t) + grpctmserver.RegisterForTest(s, fakeTM) + go s.Serve(listener) + defer s.Stop() + + // Create a gRPC client to talk to the fake tablet. + client := grpctmclient.NewClient() + defer client.Close() + fs.client = client + + // Call the targets in random order. + fs.executeInRandomOrder() + + return 1 +} diff --git a/go/vt/vtgate/engine/fuzz.go b/go/vt/vtgate/engine/fuzz.go new file mode 100644 index 00000000000..a71b355ccd0 --- /dev/null +++ b/go/vt/vtgate/engine/fuzz.go @@ -0,0 +1,107 @@ +//go:build gofuzz +// +build gofuzz + +/* +Copyright 2021 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* + DEPENDENCIES: + This fuzzer relies heavily on + $VTROOT/go/vt/vtgate/engine/fake_vcursor_test.go, + and in order to run it, it is required to rename: + $VTROOT/go/vt/vtgate/engine/fake_vcursor_test.go + to + $VTROOT/go/vt/vtgate/engine/fake_vcursor.go + + This is handled by the OSS-fuzz build script and + is only important to make note of if the fuzzer + is run locally. + + STATUS: + The fuzzer does not currently implement executions + for all possible API's in the engine package, and + it can be considered experimental, as I (@AdamKorcz) + am interested in its performance when being run + continuously by OSS-fuzz. Needless to say, more + APIs can be added with ease. +*/ + +package engine + +import ( + fuzz "github.com/AdaLogics/go-fuzz-headers" + + querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" +) + +// FuzzEngine implements the fuzzer +func FuzzEngine(data []byte) int { + c := fuzz.NewConsumer(data) + + index, err := c.GetInt() + if err != nil { + return 0 + } + switch i := index % 3; i { + case 0: + execUpdate(c) + case 1: + execInsert(c) + case 2: + execRoute(c) + default: + return 0 + } + return 1 +} + +// execUpdate implements a wrapper to fuzz Update.Tryexecute() +func execUpdate(f *fuzz.ConsumeFuzzer) { + upd := &Update{} + err := f.GenerateStruct(upd) + if err != nil { + return + } + vc := &loggingVCursor{} + _, _ = upd.TryExecute(vc, map[string]*querypb.BindVariable{}, false) +} + +// execUpdate implements a wrapper to fuzz Insert.Tryexecute() +func execInsert(f *fuzz.ConsumeFuzzer) { + ins := &Insert{} + err := f.GenerateStruct(ins) + if err != nil { + return + } + vc := &loggingVCursor{} + _, _ = ins.TryExecute(vc, map[string]*querypb.BindVariable{}, false) +} + +// execUpdate implements a wrapper to fuzz Route.Tryexecute() +func execRoute(f *fuzz.ConsumeFuzzer) { + sel := &Route{} + err := f.GenerateStruct(sel) + if err != nil { + return + } + vc := newFuzzDMLTestVCursor("0") + _, _ = sel.TryExecute(vc, map[string]*querypb.BindVariable{}, false) +} + +func newFuzzDMLTestVCursor(shards ...string) *loggingVCursor { + return &loggingVCursor{shards: shards, resolvedTargetTabletType: topodatapb.TabletType_PRIMARY} +} diff --git a/go/vt/vtgate/engine/fuzz_flaky_test.go b/go/vt/vtgate/engine/fuzz_flaky_test.go deleted file mode 100644 index cade8cac608..00000000000 --- a/go/vt/vtgate/engine/fuzz_flaky_test.go +++ /dev/null @@ -1,330 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2021 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/* - DEPENDENCIES: - This fuzzer relies heavily on - $VTROOT/go/vt/vtgate/engine/fake_vcursor_test.go, - and in order to run it, it is required to rename: - $VTROOT/go/vt/vtgate/engine/fake_vcursor_test.go - to - $VTROOT/go/vt/vtgate/engine/fake_vcursor.go - - This is handled by the OSS-fuzz build script and - is only important to make note of if the fuzzer - is run locally. - - STATUS: - The fuzzer does not currently implement executions - for all possible API's in the engine package, and - it can be considered experimental, as I (@AdamKorcz) - am interested in its performance when being run - continuously by OSS-fuzz. Needless to say, more - APIs can be added with ease. -*/ - -package engine - -import ( - "errors" - - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/vindexes" - - "vitess.io/vitess/go/sqltypes" - querypb "vitess.io/vitess/go/vt/proto/query" - vschemapb "vitess.io/vitess/go/vt/proto/vschema" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - "vitess.io/vitess/go/vt/vtgate/evalengine" -) - -func createVSchema() (vschema *vindexes.VSchema, err error) { - invschema := &vschemapb.SrvVSchema{ - Keyspaces: map[string]*vschemapb.Keyspace{ - "sharded": { - Sharded: true, - Vindexes: map[string]*vschemapb.Vindex{ - "primary": { - Type: "lookup_unique", - Params: map[string]string{ - "table": "prim", - "from": "from1", - "to": "toc", - }, - }, - }, - Tables: map[string]*vschemapb.Table{ - "t1": { - ColumnVindexes: []*vschemapb.ColumnVindex{{ - Name: "primary", - Columns: []string{"id"}, - }}, - }, - }, - }, - }, - } - vs := vindexes.BuildVSchema(invschema) - if err != nil { - return nil, err - } - return vs, nil -} - -// FuzzEngine implements the fuzzer -func FuzzEngine(data []byte) int { - c := fuzz.NewConsumer(data) - vc := newFuzzDMLTestVCursor("0") - vs, err := createVSchema() - if err != nil { - return -1 - } - for i := 0; i < 20; i++ { - newInt, err := c.GetInt() - if err != nil { - return -1 - } - execCommand(newInt%7, c, vc, vs) - } - return 1 -} - -func execUnshardedUpdate(query string, vc *loggingVCursor) { - upd := &Update{ - DML: DML{ - Opcode: Unsharded, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: false, - }, - Query: query, - }, - } - _, _ = upd.Execute(vc, map[string]*querypb.BindVariable{}, false) -} - -func execUnshardedInsert(query string, vc *loggingVCursor) { - ins := &Insert{ - Opcode: InsertUnsharded, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: false, - }, - Query: query, - } - _, _ = ins.Execute(vc, map[string]*querypb.BindVariable{}, false) -} - -func execShardedInsert(query string, vc *loggingVCursor, vs *vindexes.VSchema) { - ks := vs.Keyspaces["sharded"] - ins := NewInsert( - InsertSharded, - ks.Keyspace, - []sqltypes.PlanValue{{ - Values: []sqltypes.PlanValue{{ - Values: []sqltypes.PlanValue{{ - Value: sqltypes.NewVarChar(query), - }}, - }}, - }}, - ks.Tables["t1"], - "prefix", - []string{" mid1", " mid2", " mid3"}, - " suffix", - ) - _, _ = ins.Execute(vc, map[string]*querypb.BindVariable{}, false) -} - -func execUnshardedRoute(query, field string, vc *loggingVCursor) { - sel := NewRoute( - SelectUnsharded, - &vindexes.Keyspace{ - Name: "ks", - Sharded: false, - }, - query, - field, - ) - _, _ = sel.Execute(vc, map[string]*querypb.BindVariable{}, false) -} - -func execShardedRoute(query, field string, vc *loggingVCursor) { - sel := NewRoute( - SelectScatter, - &vindexes.Keyspace{ - Name: "ks", - Sharded: true, - }, - query, - field, - ) - _, _ = sel.Execute(vc, map[string]*querypb.BindVariable{}, false) -} - -func execEqualUniqueRoute(query, field, hash, value string, vc *loggingVCursor) { - vindex, _ := vindexes.NewHash(hash, nil) - sel := NewRoute( - SelectEqualUnique, - &vindexes.Keyspace{ - Name: "ks", - Sharded: true, - }, - query, - field, - ) - sel.Vindex = vindex.(vindexes.SingleColumn) - sel.Values = []sqltypes.PlanValue{{Value: sqltypes.NewVarChar(value)}} - _, _ = sel.Execute(vc, map[string]*querypb.BindVariable{}, false) -} - -var defaultFuzzSelectResult = sqltypes.MakeTestResult( - sqltypes.MakeTestFields( - "id", - "int64", - ), - "1", -) - -func execRouteSelectDBA(query, field, tablename, schema, shards string) { - stringToExpr := func(in string) []evalengine.Expr { - var schema []evalengine.Expr - if in != "" { - schema = []evalengine.Expr{evalengine.NewLiteralString([]byte(in))} - } - return schema - } - - sel := &Route{ - Opcode: SelectDBA, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: false, - }, - Query: query, - FieldQuery: field, - SysTableTableSchema: stringToExpr(schema), - SysTableTableName: map[string]evalengine.Expr{"table_name": evalengine.NewLiteralString([]byte(tablename))}, - } - vc := &loggingVCursor{ - shards: []string{shards}, - results: []*sqltypes.Result{defaultFuzzSelectResult}, - } - - vc.tableRoutes = tableRoutes{ - tbl: &vindexes.Table{ - Name: sqlparser.NewTableIdent("routedTable"), - Keyspace: &vindexes.Keyspace{Name: "routedKeyspace"}, - }} - _, _ = sel.Execute(vc, map[string]*querypb.BindVariable{}, false) -} - -func queryAndField(c *fuzz.ConsumeFuzzer) (string, string, error) { - query, err := c.GetString() - if err != nil { - return "nil", "nil", err - } - field, err := c.GetString() - if err != nil { - return "nil", "nil", err - } - return query, field, nil -} - -func execCommand(index int, c *fuzz.ConsumeFuzzer, vc *loggingVCursor, vs *vindexes.VSchema) error { - switch i := index; i { - case 0: - query, err := c.GetString() - if err != nil { - return err - } - execUnshardedUpdate(query, vc) - return nil - case 1: - query, err := c.GetString() - if err != nil { - return err - } - execUnshardedInsert(query, vc) - return nil - case 2: - query, err := c.GetString() - if err != nil { - return err - } - execShardedInsert(query, vc, vs) - return nil - case 3: - query, field, err := queryAndField(c) - if err != nil { - return err - } - execUnshardedRoute(query, field, vc) - return nil - case 4: - query, field, err := queryAndField(c) - if err != nil { - return err - } - execShardedRoute(query, field, vc) - return nil - case 5: - query, field, err := queryAndField(c) - if err != nil { - return err - } - hash, err := c.GetString() - if err != nil { - return err - } - value, err := c.GetString() - if err != nil { - return err - } - execEqualUniqueRoute(query, field, hash, value, vc) - return nil - case 6: - query, field, err := queryAndField(c) - if err != nil { - return err - } - tablename, err := c.GetString() - if err != nil { - return err - } - schema, err := c.GetString() - if err != nil { - return err - } - shards, err := c.GetString() - if err != nil { - return err - } - execRouteSelectDBA(query, field, tablename, schema, shards) - return nil - default: - return errors.New("Could not exec") - } -} - -func newFuzzDMLTestVCursor(shards ...string) *loggingVCursor { - return &loggingVCursor{shards: shards, resolvedTargetTabletType: topodatapb.TabletType_PRIMARY} -} diff --git a/go/vt/vtgate/planbuilder/abstract/fuzz.go b/go/vt/vtgate/planbuilder/abstract/fuzz.go index 9315a0cfe9b..536c304e852 100644 --- a/go/vt/vtgate/planbuilder/abstract/fuzz.go +++ b/go/vt/vtgate/planbuilder/abstract/fuzz.go @@ -3,13 +3,10 @@ /* Copyright 2021 The Vitess Authors. - Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -52,7 +49,7 @@ func FuzzAnalyse(data []byte) int { } switch stmt := tree.(type) { case *sqlparser.Select: - semTable, err := semantics.Analyze(stmt, "", &fakeFuzzSI{}, semantics.NoRewrite) + semTable, err := semantics.Analyze(stmt, "", &fakeFuzzSI{}) if err != nil { return 0 } diff --git a/go/vt/vtgate/planbuilder/fuzz.go b/go/vt/vtgate/planbuilder/fuzz.go new file mode 100644 index 00000000000..284516de5e4 --- /dev/null +++ b/go/vt/vtgate/planbuilder/fuzz.go @@ -0,0 +1,105 @@ +//go:build gofuzz +// +build gofuzz + +/* +Copyright 2021 The Vitess Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package planbuilder + +import ( + "sync" + "testing" + + "vitess.io/vitess/go/json2" + "vitess.io/vitess/go/sqltypes" + vschemapb "vitess.io/vitess/go/vt/proto/vschema" + "vitess.io/vitess/go/vt/vtgate/vindexes" + + fuzz "github.com/AdaLogics/go-fuzz-headers" +) + +var initter sync.Once + +func onceInit() { + testing.Init() +} + +// loadSchemaForFuzzing is a helper to load *vindexes.VSchema +// for fuzzing. +func loadSchemaForFuzzing(f *fuzz.ConsumeFuzzer) (*vindexes.VSchema, error) { + //formal, err := vindexes.LoadFormal(filename) + formal, err := loadFormalForFuzzing(f) + if err != nil { + return nil, err + } + vschema := vindexes.BuildVSchema(formal) + if err != nil { + return nil, err + } + for _, ks := range vschema.Keyspaces { + if ks.Error != nil { + return nil, err + } + + for _, table := range ks.Tables { + for i, col := range table.Columns { + if sqltypes.IsText(col.Type) { + table.Columns[i].CollationName = "latin1_swedish_ci" + } + } + } + } + return vschema, nil +} + +// loadFormalForFuzzing is a helper to create *vschemapb.SrvVSchema +func loadFormalForFuzzing(f *fuzz.ConsumeFuzzer) (*vschemapb.SrvVSchema, error) { + formal := &vschemapb.SrvVSchema{} + data, err := f.GetBytes() + if err != nil { + return nil, err + } + err = json2.Unmarshal(data, formal) + if err != nil { + return nil, err + } + return formal, nil +} + +// FuzzTestBuilder implements the fuzzer +func FuzzTestBuilder(data []byte) int { + initter.Do(onceInit) + f := fuzz.NewConsumer(data) + query, err := f.GetString() + if err != nil { + return 0 + } + keyspace, err := f.GetString() + if err != nil { + return 0 + } + s, err := loadSchemaForFuzzing(f) + if err != nil { + return 0 + } + vschemaWrapper := &vschemaWrapper{ + v: s, + sysVarEnabled: true, + } + + _, err = TestBuilder(query, vschemaWrapper, keyspace) + if err != nil { + return 0 + } + return 1 +} diff --git a/go/vt/vtgate/vindexes/fuzz.go b/go/vt/vtgate/vindexes/fuzz.go new file mode 100644 index 00000000000..b8f7f29358a --- /dev/null +++ b/go/vt/vtgate/vindexes/fuzz.go @@ -0,0 +1,244 @@ +//go:build gofuzz +// +build gofuzz + +/* +Copyright 2021 The Vitess Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vindexes + +import ( + "encoding/json" + "fmt" + "os" + "reflect" + "sync" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + + "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" +) + +var initter sync.Once + +func onceInit() { + testing.Init() +} + +// All querypbTypes +var querypbTypes = []querypb.Type{querypb.Type_NULL_TYPE, + querypb.Type_INT8, + querypb.Type_UINT8, + querypb.Type_INT16, + querypb.Type_UINT16, + querypb.Type_INT24, + querypb.Type_UINT24, + querypb.Type_INT32, + querypb.Type_UINT32, + querypb.Type_INT64, + querypb.Type_UINT64, + querypb.Type_FLOAT32, + querypb.Type_FLOAT64, + querypb.Type_TIMESTAMP, + querypb.Type_DATE, + querypb.Type_TIME, + querypb.Type_DATETIME, + querypb.Type_YEAR, + querypb.Type_DECIMAL, + querypb.Type_TEXT, + querypb.Type_BLOB, + querypb.Type_VARCHAR, + querypb.Type_VARBINARY, + querypb.Type_CHAR, + querypb.Type_BINARY, + querypb.Type_BIT, + querypb.Type_ENUM, + querypb.Type_SET, + querypb.Type_GEOMETRY, + querypb.Type_JSON, + querypb.Type_EXPRESSION} + +// All valid vindexes +var availableVindexes = []string{"binary", + "unicode_loose_md5", + "binary_md5", + "lookup_hash", + "lookup_hash_unique", + "lookup", + "lookup_unique", + "lookup_unicodeloosemd5_hash", + "lookup_unicodeloosemd5_hash_unique", + "hash", + "region_experimental", + "consistent_lookup", + "consistent_lookup_unique", + "cfc", + "numeric", + "numeric_static_map", + "xxhash", + "unicode_loose_xxhash", + "reverse_bits", + "region_json", + "null"} + +// FuzzVindex implements the vindexes fuzzer +func FuzzVindex(data []byte) int { + initter.Do(onceInit) + f := fuzz.NewConsumer(data) + + // Write region map file + createdFile, err := writeRegionMapFile(f) + if err != nil { + return 0 + } + defer os.Remove(createdFile) + + // Choose type of vindex + index, err := f.GetInt() + if err != nil { + return 0 + } + targetVindex := availableVindexes[index%len(availableVindexes)] + + // Create params map + params := make(map[string]string) + err = f.FuzzMap(¶ms) + if err != nil { + return 0 + } + params["region_map"] = createdFile + + // Create the vindex + l, err := CreateVindex(targetVindex, targetVindex, params) + if err != nil { + return 0 + } + + // Create values to be passed to our targets + allValues, err := createValues(f) + if err != nil { + return 0 + } + + vc := &loggingVCursor{} + + // Time to call the actual targets. THere are two: + // 1) Map() + // 2) Create() + + // Target 1: + _, _ = Map(l, vc, allValues) + + // Target 2: + s1 := reflect.TypeOf(l).String() + switch s1 { + case "*vindexes.ConsistentLookup", "*vindexes.LookupHash", + "*vindexes.LookupHashUnique", "*vindexes.LookupNonUnique", + "*vindexes.LookupUnique", "*vindexes.LookupUnicodeLooseMD5Hash", + "*vindexes.LookupUnicodeLooseMD5HashUnique", "*vindexes.clCommon", + "*vindexes.lookupInternal": + ksids, err := createKsids(f) + if err != nil { + return 0 + } + _ = l.(Lookup).Create(vc, allValues, ksids, false) + } + return 1 +} + +// Creates a slice of byte slices to use as ksids +func createKsids(f *fuzz.ConsumeFuzzer) ([][]byte, error) { + // create ksids + noOfTotalKsids, err := f.GetInt() + if err != nil { + return nil, err + } + ksids := make([][]byte, noOfTotalKsids%100) + for i := 0; i < noOfTotalKsids%100; i++ { + newBytes, err := f.GetBytes() + if err != nil { + return nil, err + } + ksids = append(ksids, newBytes) + } + return ksids, nil +} + +// Checks if a byte slice is valid json +func IsJSON(data []byte) bool { + var js json.RawMessage + return json.Unmarshal(data, &js) == nil +} + +// Create and write the RegionMap file +func writeRegionMapFile(f *fuzz.ConsumeFuzzer) (string, error) { + fileBytes, err := f.GetBytes() + if err != nil || !IsJSON(fileBytes) { + return "", fmt.Errorf("Could not get valid bytes") + } + + createdFile, err := os.CreateTemp("", "tmpfile-") + if err != nil { + return "", err + } + defer createdFile.Close() + + _, err = createdFile.Write(fileBytes) + if err != nil { + return "", err + } + return createdFile.Name(), nil + +} + +// Creates a slice of slices of sqltypes.Value +func createValues(f *fuzz.ConsumeFuzzer) ([][]sqltypes.Value, error) { + noOfTotalValues, err := f.GetInt() + if err != nil { + return nil, err + } + allValues := make([][]sqltypes.Value, 0) + for i := 0; i < noOfTotalValues%100; i++ { + noOfValues, err := f.GetInt() + if err != nil { + return nil, err + } + values := make([]sqltypes.Value, 0) + for i2 := 0; i2 < noOfValues%100; i2++ { + v, err := createValue(f) + if err != nil { + return nil, err + } + values = append(values, v) + } + allValues = append(allValues, values) + } + return allValues, nil +} + +// Creates a single sqltypes.Value +func createValue(f *fuzz.ConsumeFuzzer) (sqltypes.Value, error) { + typeIndex, err := f.GetInt() + if err != nil { + return sqltypes.Value{}, err + } + + inputBytes, err := f.GetBytes() + if err != nil { + return sqltypes.Value{}, err + } + + v := querypbTypes[typeIndex%len(querypbTypes)] + return sqltypes.NewValue(v, inputBytes) +}