vitess.io/vitess@v0.16.2/go/vt/vtgate/vindexes/fuzz.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 vindexes 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "os" 23 "reflect" 24 "sync" 25 "testing" 26 27 fuzz "github.com/AdaLogics/go-fuzz-headers" 28 29 "vitess.io/vitess/go/sqltypes" 30 querypb "vitess.io/vitess/go/vt/proto/query" 31 ) 32 33 var initter sync.Once 34 35 func onceInit() { 36 testing.Init() 37 } 38 39 // All querypbTypes 40 var querypbTypes = []querypb.Type{querypb.Type_NULL_TYPE, 41 querypb.Type_INT8, 42 querypb.Type_UINT8, 43 querypb.Type_INT16, 44 querypb.Type_UINT16, 45 querypb.Type_INT24, 46 querypb.Type_UINT24, 47 querypb.Type_INT32, 48 querypb.Type_UINT32, 49 querypb.Type_INT64, 50 querypb.Type_UINT64, 51 querypb.Type_FLOAT32, 52 querypb.Type_FLOAT64, 53 querypb.Type_TIMESTAMP, 54 querypb.Type_DATE, 55 querypb.Type_TIME, 56 querypb.Type_DATETIME, 57 querypb.Type_YEAR, 58 querypb.Type_DECIMAL, 59 querypb.Type_TEXT, 60 querypb.Type_BLOB, 61 querypb.Type_VARCHAR, 62 querypb.Type_VARBINARY, 63 querypb.Type_CHAR, 64 querypb.Type_BINARY, 65 querypb.Type_BIT, 66 querypb.Type_ENUM, 67 querypb.Type_SET, 68 querypb.Type_GEOMETRY, 69 querypb.Type_JSON, 70 querypb.Type_EXPRESSION} 71 72 // All valid vindexes 73 var availableVindexes = []string{"binary", 74 "unicode_loose_md5", 75 "binary_md5", 76 "lookup_hash", 77 "lookup_hash_unique", 78 "lookup", 79 "lookup_unique", 80 "lookup_unicodeloosemd5_hash", 81 "lookup_unicodeloosemd5_hash_unique", 82 "hash", 83 "region_experimental", 84 "consistent_lookup", 85 "consistent_lookup_unique", 86 "cfc", 87 "numeric", 88 "numeric_static_map", 89 "xxhash", 90 "unicode_loose_xxhash", 91 "reverse_bits", 92 "region_json", 93 "null"} 94 95 // FuzzVindex implements the vindexes fuzzer 96 func FuzzVindex(data []byte) int { 97 initter.Do(onceInit) 98 f := fuzz.NewConsumer(data) 99 100 // Write region map file 101 createdFile, err := writeRegionMapFile(f) 102 if err != nil { 103 return 0 104 } 105 defer os.Remove(createdFile) 106 107 // Choose type of vindex 108 index, err := f.GetInt() 109 if err != nil { 110 return 0 111 } 112 targetVindex := availableVindexes[index%len(availableVindexes)] 113 114 // Create params map 115 params := make(map[string]string) 116 err = f.FuzzMap(¶ms) 117 if err != nil { 118 return 0 119 } 120 params["region_map"] = createdFile 121 122 // Create the vindex 123 l, err := CreateVindex(targetVindex, targetVindex, params) 124 if err != nil { 125 return 0 126 } 127 128 // Create values to be passed to our targets 129 allValues, err := createValues(f) 130 if err != nil { 131 return 0 132 } 133 134 vc := &loggingVCursor{} 135 136 // Time to call the actual targets. THere are two: 137 // 1) Map() 138 // 2) Create() 139 140 // Target 1: 141 _, _ = Map(ctx, l, vc, allValues) 142 143 // Target 2: 144 s1 := reflect.TypeOf(l).String() 145 switch s1 { 146 case "*vindexes.ConsistentLookup", "*vindexes.LookupHash", 147 "*vindexes.LookupHashUnique", "*vindexes.LookupNonUnique", 148 "*vindexes.LookupUnique", "*vindexes.LookupUnicodeLooseMD5Hash", 149 "*vindexes.LookupUnicodeLooseMD5HashUnique", "*vindexes.clCommon", 150 "*vindexes.lookupInternal": 151 ksids, err := createKsids(f) 152 if err != nil { 153 return 0 154 } 155 _ = l.(Lookup).Create(ctx, vc, allValues, ksids, false /* ignoreMode */) 156 } 157 return 1 158 } 159 160 // Creates a slice of byte slices to use as ksids 161 func createKsids(f *fuzz.ConsumeFuzzer) ([][]byte, error) { 162 // create ksids 163 noOfTotalKsids, err := f.GetInt() 164 if err != nil { 165 return nil, err 166 } 167 ksids := make([][]byte, noOfTotalKsids%100) 168 for i := 0; i < noOfTotalKsids%100; i++ { 169 newBytes, err := f.GetBytes() 170 if err != nil { 171 return nil, err 172 } 173 ksids = append(ksids, newBytes) 174 } 175 return ksids, nil 176 } 177 178 // Checks if a byte slice is valid json 179 func IsJSON(data []byte) bool { 180 var js json.RawMessage 181 return json.Unmarshal(data, &js) == nil 182 } 183 184 // Create and write the RegionMap file 185 func writeRegionMapFile(f *fuzz.ConsumeFuzzer) (string, error) { 186 fileBytes, err := f.GetBytes() 187 if err != nil || !IsJSON(fileBytes) { 188 return "", fmt.Errorf("Could not get valid bytes") 189 } 190 191 createdFile, err := os.CreateTemp("", "tmpfile-") 192 if err != nil { 193 return "", err 194 } 195 defer createdFile.Close() 196 197 _, err = createdFile.Write(fileBytes) 198 if err != nil { 199 return "", err 200 } 201 return createdFile.Name(), nil 202 203 } 204 205 // Creates a slice of slices of sqltypes.Value 206 func createValues(f *fuzz.ConsumeFuzzer) ([][]sqltypes.Value, error) { 207 noOfTotalValues, err := f.GetInt() 208 if err != nil { 209 return nil, err 210 } 211 allValues := make([][]sqltypes.Value, 0) 212 for i := 0; i < noOfTotalValues%100; i++ { 213 noOfValues, err := f.GetInt() 214 if err != nil { 215 return nil, err 216 } 217 values := make([]sqltypes.Value, 0) 218 for i2 := 0; i2 < noOfValues%100; i2++ { 219 v, err := createValue(f) 220 if err != nil { 221 return nil, err 222 } 223 values = append(values, v) 224 } 225 allValues = append(allValues, values) 226 } 227 return allValues, nil 228 } 229 230 // Creates a single sqltypes.Value 231 func createValue(f *fuzz.ConsumeFuzzer) (sqltypes.Value, error) { 232 typeIndex, err := f.GetInt() 233 if err != nil { 234 return sqltypes.Value{}, err 235 } 236 237 inputBytes, err := f.GetBytes() 238 if err != nil { 239 return sqltypes.Value{}, err 240 } 241 242 v := querypbTypes[typeIndex%len(querypbTypes)] 243 return sqltypes.NewValue(v, inputBytes) 244 }