Browse Source

testing: move fuzzers over from OSS-Fuzz

Signed-off-by: AdamKorcz <adam@adalogics.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
AdamKorcz 2 năm trước cách đây
mục cha
commit
93fa093122

+ 15 - 0
daemon/logger/jsonfilelog/fuzz_test.go

@@ -0,0 +1,15 @@
+package jsonfilelog
+
+import (
+	"bytes"
+	"testing"
+)
+
+func FuzzLoggerDecode(f *testing.F) {
+	f.Fuzz(func(t *testing.T, data []byte) {
+		dec := decodeFunc(bytes.NewBuffer(data))
+		defer dec.Close()
+
+		_, _ = dec.Decode()
+	})
+}

+ 21 - 0
daemon/logger/jsonfilelog/jsonlog/fuzz_test.go

@@ -0,0 +1,21 @@
+package jsonlog
+
+import (
+	"bytes"
+	"testing"
+
+	fuzz "github.com/AdaLogics/go-fuzz-headers"
+)
+
+func FuzzJSONLogsMarshalJSONBuf(f *testing.F) {
+	f.Fuzz(func(t *testing.T, data []byte) {
+		ff := fuzz.NewConsumer(data)
+		l := &JSONLogs{}
+		err := ff.GenerateStruct(l)
+		if err != nil {
+			return
+		}
+		var buf bytes.Buffer
+		l.MarshalJSONBuf(&buf)
+	})
+}

+ 40 - 0
libnetwork/etchosts/fuzz_test.go

@@ -0,0 +1,40 @@
+package etchosts
+
+import (
+	"os"
+	"path/filepath"
+	"testing"
+
+	fuzz "github.com/AdaLogics/go-fuzz-headers"
+)
+
+func FuzzAdd(f *testing.F) {
+	f.Fuzz(func(t *testing.T, data []byte) {
+		ff := fuzz.NewConsumer(data)
+		fileBytes, err := ff.GetBytes()
+		if err != nil {
+			return
+		}
+		noOfRecords, err := ff.GetInt()
+		if err != nil {
+			return
+		}
+
+		recs := make([]Record, 0)
+		for i := 0; i < noOfRecords%40; i++ {
+			r := Record{}
+			err = ff.GenerateStruct(&r)
+			if err != nil {
+				return
+			}
+			recs = append(recs, r)
+		}
+		tmpDir := t.TempDir()
+		testFile := filepath.Join(tmpDir, "testFile")
+		err = os.WriteFile(testFile, fileBytes, 0o644)
+		if err != nil {
+			return
+		}
+		_ = Add(testFile, recs)
+	})
+}

+ 30 - 0
oci/fuzz_test.go

@@ -0,0 +1,30 @@
+package oci
+
+import (
+	"testing"
+
+	fuzz "github.com/AdaLogics/go-fuzz-headers"
+	specs "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+func FuzzAppendDevicePermissionsFromCgroupRules(f *testing.F) {
+	f.Fuzz(func(t *testing.T, data []byte) {
+		ff := fuzz.NewConsumer(data)
+		sp := make([]specs.LinuxDeviceCgroup, 0)
+		noOfRecords, err := ff.GetInt()
+		if err != nil {
+			return
+		}
+		for i := 0; i < noOfRecords%40; i++ {
+			s := specs.LinuxDeviceCgroup{}
+			err := ff.GenerateStruct(&s)
+			if err != nil {
+				return
+			}
+			sp = append(sp, s)
+		}
+		rules := make([]string, 0)
+		ff.CreateSlice(&rules)
+		_, _ = AppendDevicePermissionsFromCgroupRules(sp, rules)
+	})
+}

+ 39 - 0
pkg/archive/fuzz_test.go

@@ -0,0 +1,39 @@
+package archive
+
+import (
+	"bytes"
+	"testing"
+
+	fuzz "github.com/AdaLogics/go-fuzz-headers"
+)
+
+func FuzzDecompressStream(f *testing.F) {
+	f.Fuzz(func(t *testing.T, data []byte) {
+		r := bytes.NewReader(data)
+		_, _ = DecompressStream(r)
+	})
+}
+
+func FuzzUntar(f *testing.F) {
+	f.Fuzz(func(t *testing.T, data []byte) {
+		ff := fuzz.NewConsumer(data)
+		tarBytes, err := ff.TarBytes()
+		if err != nil {
+			return
+		}
+		options := &TarOptions{}
+		err = ff.GenerateStruct(options)
+		if err != nil {
+			return
+		}
+		tmpDir := t.TempDir()
+		Untar(bytes.NewReader(tarBytes), tmpDir, options)
+	})
+}
+
+func FuzzApplyLayer(f *testing.F) {
+	f.Fuzz(func(t *testing.T, data []byte) {
+		tmpDir := t.TempDir()
+		_, _ = ApplyLayer(tmpDir, bytes.NewReader(data))
+	})
+}

+ 39 - 0
pkg/tailfile/fuzz_test.go

@@ -0,0 +1,39 @@
+package tailfile
+
+import (
+	"os"
+	"path/filepath"
+	"testing"
+
+	fuzz "github.com/AdaLogics/go-fuzz-headers"
+)
+
+func FuzzTailfile(f *testing.F) {
+	f.Fuzz(func(t *testing.T, data []byte) {
+		if len(data) < 5 {
+			return
+		}
+		ff := fuzz.NewConsumer(data)
+		n, err := ff.GetUint64()
+		if err != nil {
+			return
+		}
+		fileBytes, err := ff.GetBytes()
+		if err != nil {
+			return
+		}
+		tempDir := t.TempDir()
+		fil, err := os.Create(filepath.Join(tempDir, "tailFile"))
+		if err != nil {
+			return
+		}
+		defer fil.Close()
+
+		_, err = fil.Write(fileBytes)
+		if err != nil {
+			return
+		}
+		fil.Seek(0, 0)
+		_, _ = TailFile(fil, int(n))
+	})
+}

+ 1 - 0
vendor.mod

@@ -10,6 +10,7 @@ require (
 	cloud.google.com/go/compute v1.7.0
 	cloud.google.com/go/logging v1.4.2
 	code.cloudfoundry.org/clock v1.0.0
+	github.com/AdaLogics/go-fuzz-headers v0.0.0-20221118232415-3345c89a7c72
 	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1
 	github.com/Graylog2/go-gelf v0.0.0-20191017102106-1550ee647df0
 	github.com/Microsoft/go-winio v0.5.2

+ 2 - 0
vendor.sum

@@ -65,6 +65,8 @@ code.cloudfoundry.org/clock v1.0.0 h1:kFXWQM4bxYvdBw2X8BbBeXwQNgfoWv1vqAk2ZZyBN2
 code.cloudfoundry.org/clock v1.0.0/go.mod h1:QD9Lzhd/ux6eNQVUDVRJX/RKTigpewimNYBi7ivZKY8=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg=
+github.com/AdaLogics/go-fuzz-headers v0.0.0-20221118232415-3345c89a7c72 h1:kq78byqmxX6R9uk4uN3HD2F5tkZJAZMauuLSkNPS8to=
+github.com/AdaLogics/go-fuzz-headers v0.0.0-20221118232415-3345c89a7c72/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0=
 github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
 github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=

+ 201 - 0
vendor/github.com/AdaLogics/go-fuzz-headers/LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.

+ 93 - 0
vendor/github.com/AdaLogics/go-fuzz-headers/README.md

@@ -0,0 +1,93 @@
+# go-fuzz-headers
+This repository contains various helper functions for go fuzzing. It is mostly used in combination with [go-fuzz](https://github.com/dvyukov/go-fuzz), but compatibility with fuzzing in the standard library will also be supported. Any coverage guided fuzzing engine that provides an array or slice of bytes can be used with go-fuzz-headers.
+
+
+## Usage
+Using go-fuzz-headers is easy. First create a new consumer with the bytes provided by the fuzzing engine:
+
+```go
+import (
+	fuzz "github.com/AdaLogics/go-fuzz-headers"
+)
+data := []byte{'R', 'a', 'n', 'd', 'o', 'm'}
+f := fuzz.NewConsumer(data)
+
+```
+
+This creates a `Consumer` that consumes the bytes of the input as it uses them to fuzz different types.
+
+After that, `f` can be used to easily create fuzzed instances of different types. Below are some examples:
+
+### Structs
+One of the most useful features of go-fuzz-headers is its ability to fill structs with the data provided by the fuzzing engine. This is done with a single line:
+```go
+type Person struct {
+    Name string
+    Age  int
+}
+p := Person{}
+// Fill p with values based on the data provided by the fuzzing engine:
+err := f.GenerateStruct(&p)
+```
+
+This includes nested structs too. In this example, the fuzz Consumer will also insert values in `p.BestFriend`:
+```go
+type PersonI struct {
+    Name       string
+    Age        int
+    BestFriend PersonII
+}
+type PersonII struct {
+    Name string
+    Age  int
+}
+p := PersonI{}
+err := f.GenerateStruct(&p)
+```
+
+If the consumer should insert values for unexported fields as well as exported, this can be enabled with:
+
+```go
+f.AllowUnexportedFields()
+```
+
+...and disabled with:
+
+```go
+f.DisallowUnexportedFields()
+```
+
+### Other types:
+
+Other useful APIs:
+
+```go
+createdString, err := f.GetString() // Gets a string
+createdInt, err := f.GetInt() // Gets an integer
+createdByte, err := f.GetByte() // Gets a byte
+createdBytes, err := f.GetBytes() // Gets a byte slice
+createdBool, err := f.GetBool() // Gets a boolean
+err := f.FuzzMap(target_map) // Fills a map
+createdTarBytes, err := f.TarBytes() // Gets bytes of a valid tar archive
+err := f.CreateFiles(inThisDir) // Fills inThisDir with files
+createdString, err := f.GetStringFrom("anyCharInThisString", ofThisLength) // Gets a string that consists of chars from "anyCharInThisString" and has the exact length "ofThisLength"
+```
+
+Most APIs are added as they are needed.
+
+## Projects that use go-fuzz-headers
+- [runC](https://github.com/opencontainers/runc)
+- [Istio](https://github.com/istio/istio)
+- [Vitess](https://github.com/vitessio/vitess)
+- [Containerd](https://github.com/containerd/containerd)
+
+Feel free to add your own project to the list, if you use go-fuzz-headers to fuzz it.
+
+
+ 
+
+## Status
+The project is under development and will be updated regularly.
+
+## References
+go-fuzz-headers' approach to fuzzing structs is strongly inspired by [gofuzz](https://github.com/google/gofuzz).

+ 830 - 0
vendor/github.com/AdaLogics/go-fuzz-headers/consumer.go

@@ -0,0 +1,830 @@
+package gofuzzheaders
+
+import (
+	"archive/tar"
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"io"
+	"math"
+	"os"
+	"path/filepath"
+	"reflect"
+	"strings"
+	"time"
+	"unsafe"
+
+	securejoin "github.com/cyphar/filepath-securejoin"
+)
+
+var MaxTotalLen uint32 = 2000000
+
+func SetMaxTotalLen(newLen uint32) {
+	MaxTotalLen = newLen
+}
+
+type ConsumeFuzzer struct {
+	data                 []byte
+	dataTotal            uint32
+	CommandPart          []byte
+	RestOfArray          []byte
+	NumberOfCalls        int
+	position             uint32
+	fuzzUnexportedFields bool
+	Funcs                map[reflect.Type]reflect.Value
+}
+
+func IsDivisibleBy(n int, divisibleby int) bool {
+	return (n % divisibleby) == 0
+}
+
+func NewConsumer(fuzzData []byte) *ConsumeFuzzer {
+	return &ConsumeFuzzer{
+		data:      fuzzData,
+		dataTotal: uint32(len(fuzzData)),
+		Funcs:     make(map[reflect.Type]reflect.Value),
+	}
+}
+
+func (f *ConsumeFuzzer) Split(minCalls, maxCalls int) error {
+	if f.dataTotal == 0 {
+		return errors.New("could not split")
+	}
+	numberOfCalls := int(f.data[0])
+	if numberOfCalls < minCalls || numberOfCalls > maxCalls {
+		return errors.New("bad number of calls")
+	}
+	if int(f.dataTotal) < numberOfCalls+numberOfCalls+1 {
+		return errors.New("length of data does not match required parameters")
+	}
+
+	// Define part 2 and 3 of the data array
+	commandPart := f.data[1 : numberOfCalls+1]
+	restOfArray := f.data[numberOfCalls+1:]
+
+	// Just a small check. It is necessary
+	if len(commandPart) != numberOfCalls {
+		return errors.New("length of commandPart does not match number of calls")
+	}
+
+	// Check if restOfArray is divisible by numberOfCalls
+	if !IsDivisibleBy(len(restOfArray), numberOfCalls) {
+		return errors.New("length of commandPart does not match number of calls")
+	}
+	f.CommandPart = commandPart
+	f.RestOfArray = restOfArray
+	f.NumberOfCalls = numberOfCalls
+	return nil
+}
+
+func (f *ConsumeFuzzer) AllowUnexportedFields() {
+	f.fuzzUnexportedFields = true
+}
+
+func (f *ConsumeFuzzer) DisallowUnexportedFields() {
+	f.fuzzUnexportedFields = false
+}
+
+func (f *ConsumeFuzzer) GenerateStruct(targetStruct interface{}) error {
+	e := reflect.ValueOf(targetStruct).Elem()
+	return f.fuzzStruct(e, false)
+}
+
+func (f *ConsumeFuzzer) setCustom(v reflect.Value) error {
+	// First: see if we have a fuzz function for it.
+	doCustom, ok := f.Funcs[v.Type()]
+	if !ok {
+		return fmt.Errorf("could not find a custom function")
+	}
+
+	switch v.Kind() {
+	case reflect.Ptr:
+		if v.IsNil() {
+			if !v.CanSet() {
+				return fmt.Errorf("could not use a custom function")
+			}
+			v.Set(reflect.New(v.Type().Elem()))
+		}
+	case reflect.Map:
+		if v.IsNil() {
+			if !v.CanSet() {
+				return fmt.Errorf("could not use a custom function")
+			}
+			v.Set(reflect.MakeMap(v.Type()))
+		}
+	default:
+		return fmt.Errorf("could not use a custom function")
+	}
+
+	verr := doCustom.Call([]reflect.Value{v, reflect.ValueOf(Continue{
+		F: f,
+	})})
+
+	// check if we return an error
+	if verr[0].IsNil() {
+		return nil
+	}
+	return fmt.Errorf("could not use a custom function")
+}
+
+func (f *ConsumeFuzzer) fuzzStruct(e reflect.Value, customFunctions bool) error {
+	// We check if we should check for custom functions
+	if customFunctions && e.IsValid() && e.CanAddr() {
+		err := f.setCustom(e.Addr())
+		if err == nil {
+			return nil
+		}
+	}
+
+	switch e.Kind() {
+	case reflect.Struct:
+		for i := 0; i < e.NumField(); i++ {
+			var v reflect.Value
+			if !e.Field(i).CanSet() {
+				if f.fuzzUnexportedFields {
+					v = reflect.NewAt(e.Field(i).Type(), unsafe.Pointer(e.Field(i).UnsafeAddr())).Elem()
+				}
+				if err := f.fuzzStruct(v, customFunctions); err != nil {
+					return err
+				}
+			} else {
+				v = e.Field(i)
+				if err := f.fuzzStruct(v, customFunctions); err != nil {
+					return err
+				}
+			}
+		}
+	case reflect.String:
+		str, err := f.GetString()
+		if err != nil {
+			return err
+		}
+		if e.CanSet() {
+			e.SetString(str)
+		}
+	case reflect.Slice:
+		var maxElements uint32
+		// Byte slices should not be restricted
+		if e.Type().String() == "[]uint8" {
+			maxElements = 10000000
+		} else {
+			maxElements = 50
+		}
+
+		randQty, err := f.GetUint32()
+		if err != nil {
+			return err
+		}
+		numOfElements := randQty % maxElements
+		if (f.dataTotal - f.position) < numOfElements {
+			numOfElements = f.dataTotal - f.position
+		}
+
+		uu := reflect.MakeSlice(e.Type(), int(numOfElements), int(numOfElements))
+
+		for i := 0; i < int(numOfElements); i++ {
+			// If we have more than 10, then we can proceed with that.
+			if err := f.fuzzStruct(uu.Index(i), customFunctions); err != nil {
+				if i >= 10 {
+					if e.CanSet() {
+						e.Set(uu)
+					}
+					return nil
+				} else {
+					return err
+				}
+			}
+		}
+		if e.CanSet() {
+			e.Set(uu)
+		}
+	case reflect.Uint16:
+		newInt, err := f.GetUint16()
+		if err != nil {
+			return err
+		}
+		if e.CanSet() {
+			e.SetUint(uint64(newInt))
+		}
+	case reflect.Uint32:
+		newInt, err := f.GetUint32()
+		if err != nil {
+			return err
+		}
+		if e.CanSet() {
+			e.SetUint(uint64(newInt))
+		}
+	case reflect.Uint64:
+		newInt, err := f.GetInt()
+		if err != nil {
+			return err
+		}
+		if e.CanSet() {
+			e.SetUint(uint64(newInt))
+		}
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		newInt, err := f.GetInt()
+		if err != nil {
+			return err
+		}
+		if e.CanSet() {
+			e.SetInt(int64(newInt))
+		}
+	case reflect.Float32:
+		newFloat, err := f.GetFloat32()
+		if err != nil {
+			return err
+		}
+		if e.CanSet() {
+			e.SetFloat(float64(newFloat))
+		}
+	case reflect.Float64:
+		newFloat, err := f.GetFloat64()
+		if err != nil {
+			return err
+		}
+		if e.CanSet() {
+			e.SetFloat(float64(newFloat))
+		}
+	case reflect.Map:
+		if e.CanSet() {
+			e.Set(reflect.MakeMap(e.Type()))
+			const maxElements = 50
+			randQty, err := f.GetInt()
+			if err != nil {
+				return err
+			}
+			numOfElements := randQty % maxElements
+			for i := 0; i < numOfElements; i++ {
+				key := reflect.New(e.Type().Key()).Elem()
+				if err := f.fuzzStruct(key, customFunctions); err != nil {
+					return err
+				}
+				val := reflect.New(e.Type().Elem()).Elem()
+				if err = f.fuzzStruct(val, customFunctions); err != nil {
+					return err
+				}
+				e.SetMapIndex(key, val)
+			}
+		}
+	case reflect.Ptr:
+		if e.CanSet() {
+			e.Set(reflect.New(e.Type().Elem()))
+			if err := f.fuzzStruct(e.Elem(), customFunctions); err != nil {
+				return err
+			}
+			return nil
+		}
+	case reflect.Uint8:
+		b, err := f.GetByte()
+		if err != nil {
+			return err
+		}
+		if e.CanSet() {
+			e.SetUint(uint64(b))
+		}
+	}
+	return nil
+}
+
+func (f *ConsumeFuzzer) GetStringArray() (reflect.Value, error) {
+	// The max size of the array:
+	const max uint32 = 20
+
+	arraySize := f.position
+	if arraySize > max {
+		arraySize = max
+	}
+	stringArray := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("string")), int(arraySize), int(arraySize))
+	if f.position+arraySize >= f.dataTotal {
+		return stringArray, errors.New("could not make string array")
+	}
+
+	for i := 0; i < int(arraySize); i++ {
+		stringSize := uint32(f.data[f.position])
+		if f.position+stringSize >= f.dataTotal {
+			return stringArray, nil
+		}
+		stringToAppend := string(f.data[f.position : f.position+stringSize])
+		strVal := reflect.ValueOf(stringToAppend)
+		stringArray = reflect.Append(stringArray, strVal)
+		f.position += stringSize
+	}
+	return stringArray, nil
+}
+
+func (f *ConsumeFuzzer) GetInt() (int, error) {
+	if f.position >= f.dataTotal {
+		return 0, errors.New("not enough bytes to create int")
+	}
+	returnInt := int(f.data[f.position])
+	f.position++
+	return returnInt, nil
+}
+
+func (f *ConsumeFuzzer) GetByte() (byte, error) {
+	if f.position >= f.dataTotal {
+		return 0x00, errors.New("not enough bytes to get byte")
+	}
+	returnByte := f.data[f.position]
+	f.position++
+	return returnByte, nil
+}
+
+func (f *ConsumeFuzzer) GetNBytes(numberOfBytes int) ([]byte, error) {
+	if f.position >= f.dataTotal {
+		return nil, errors.New("not enough bytes to get byte")
+	}
+	returnBytes := make([]byte, 0, numberOfBytes)
+	for i := 0; i < numberOfBytes; i++ {
+		newByte, err := f.GetByte()
+		if err != nil {
+			return nil, err
+		}
+		returnBytes = append(returnBytes, newByte)
+	}
+	return returnBytes, nil
+}
+
+func (f *ConsumeFuzzer) GetUint16() (uint16, error) {
+	u16, err := f.GetNBytes(2)
+	if err != nil {
+		return 0, err
+	}
+	littleEndian, err := f.GetBool()
+	if err != nil {
+		return 0, err
+	}
+	if littleEndian {
+		return binary.LittleEndian.Uint16(u16), nil
+	}
+	return binary.BigEndian.Uint16(u16), nil
+}
+
+func (f *ConsumeFuzzer) GetUint32() (uint32, error) {
+	u32, err := f.GetNBytes(4)
+	if err != nil {
+		return 0, err
+	}
+	littleEndian, err := f.GetBool()
+	if err != nil {
+		return 0, err
+	}
+	if littleEndian {
+		return binary.LittleEndian.Uint32(u32), nil
+	}
+	return binary.BigEndian.Uint32(u32), nil
+}
+
+func (f *ConsumeFuzzer) GetUint64() (uint64, error) {
+	u64, err := f.GetNBytes(8)
+	if err != nil {
+		return 0, err
+	}
+	littleEndian, err := f.GetBool()
+	if err != nil {
+		return 0, err
+	}
+	if littleEndian {
+		return binary.LittleEndian.Uint64(u64), nil
+	}
+	return binary.BigEndian.Uint64(u64), nil
+}
+
+func (f *ConsumeFuzzer) GetBytes() ([]byte, error) {
+	if f.position >= f.dataTotal {
+		return nil, errors.New("not enough bytes to create byte array")
+	}
+	length, err := f.GetUint32()
+	if err != nil {
+		return nil, errors.New("not enough bytes to create byte array")
+	}
+	if f.position+length > MaxTotalLen {
+		return nil, errors.New("created too large a string")
+	}
+	byteBegin := f.position - 1
+	if byteBegin >= f.dataTotal {
+		return nil, errors.New("not enough bytes to create byte array")
+	}
+	if length == 0 {
+		return nil, errors.New("zero-length is not supported")
+	}
+	if byteBegin+length >= f.dataTotal {
+		return nil, errors.New("not enough bytes to create byte array")
+	}
+	if byteBegin+length < byteBegin {
+		return nil, errors.New("numbers overflow")
+	}
+	f.position = byteBegin + length
+	return f.data[byteBegin:f.position], nil
+}
+
+func (f *ConsumeFuzzer) GetString() (string, error) {
+	if f.position >= f.dataTotal {
+		return "nil", errors.New("not enough bytes to create string")
+	}
+	length, err := f.GetUint32()
+	if err != nil {
+		return "nil", errors.New("not enough bytes to create string")
+	}
+	if f.position > MaxTotalLen {
+		return "nil", errors.New("created too large a string")
+	}
+	byteBegin := f.position - 1
+	if byteBegin >= f.dataTotal {
+		return "nil", errors.New("not enough bytes to create string")
+	}
+	if byteBegin+length > f.dataTotal {
+		return "nil", errors.New("not enough bytes to create string")
+	}
+	if byteBegin > byteBegin+length {
+		return "nil", errors.New("numbers overflow")
+	}
+	f.position = byteBegin + length
+	return string(f.data[byteBegin:f.position]), nil
+}
+
+func (f *ConsumeFuzzer) GetBool() (bool, error) {
+	if f.position >= f.dataTotal {
+		return false, errors.New("not enough bytes to create bool")
+	}
+	if IsDivisibleBy(int(f.data[f.position]), 2) {
+		f.position++
+		return true, nil
+	} else {
+		f.position++
+		return false, nil
+	}
+}
+
+func (f *ConsumeFuzzer) FuzzMap(m interface{}) error {
+	return f.GenerateStruct(m)
+}
+
+func returnTarBytes(buf []byte) ([]byte, error) {
+	// Count files
+	var fileCounter int
+	tr := tar.NewReader(bytes.NewReader(buf))
+	for {
+		_, err := tr.Next()
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			return nil, err
+		}
+		fileCounter++
+	}
+	if fileCounter > 4 {
+		return buf, nil
+	}
+	return nil, fmt.Errorf("not enough files were created\n")
+}
+
+func setTarHeaderFormat(hdr *tar.Header, f *ConsumeFuzzer) error {
+	ind, err := f.GetInt()
+	if err != nil {
+		return err
+	}
+	switch ind % 4 {
+	case 0:
+		hdr.Format = tar.FormatUnknown
+	case 1:
+		hdr.Format = tar.FormatUSTAR
+	case 2:
+		hdr.Format = tar.FormatPAX
+	case 3:
+		hdr.Format = tar.FormatGNU
+	}
+	return nil
+}
+
+func setTarHeaderTypeflag(hdr *tar.Header, f *ConsumeFuzzer) error {
+	ind, err := f.GetInt()
+	if err != nil {
+		return err
+	}
+	switch ind % 13 {
+	case 0:
+		hdr.Typeflag = tar.TypeReg
+	case 1:
+		hdr.Typeflag = tar.TypeLink
+		linkname, err := f.GetString()
+		if err != nil {
+			return err
+		}
+		hdr.Linkname = linkname
+	case 2:
+		hdr.Typeflag = tar.TypeSymlink
+		linkname, err := f.GetString()
+		if err != nil {
+			return err
+		}
+		hdr.Linkname = linkname
+	case 3:
+		hdr.Typeflag = tar.TypeChar
+	case 4:
+		hdr.Typeflag = tar.TypeBlock
+	case 5:
+		hdr.Typeflag = tar.TypeDir
+	case 6:
+		hdr.Typeflag = tar.TypeFifo
+	case 7:
+		hdr.Typeflag = tar.TypeCont
+	case 8:
+		hdr.Typeflag = tar.TypeXHeader
+	case 9:
+		hdr.Typeflag = tar.TypeXGlobalHeader
+	case 10:
+		hdr.Typeflag = tar.TypeGNUSparse
+	case 11:
+		hdr.Typeflag = tar.TypeGNULongName
+	case 12:
+		hdr.Typeflag = tar.TypeGNULongLink
+	}
+	return nil
+}
+
+func (f *ConsumeFuzzer) createTarFileBody() ([]byte, error) {
+	length, err := f.GetUint32()
+	if err != nil {
+		return nil, errors.New("not enough bytes to create byte array")
+	}
+
+	// A bit of optimization to attempt to create a file body
+	// when we don't have as many bytes left as "length"
+	remainingBytes := f.dataTotal - f.position
+	if remainingBytes == 0 {
+		return nil, errors.New("created too large a string")
+	}
+	if remainingBytes < 50 {
+		length = length % remainingBytes
+	} else if f.dataTotal < 500 {
+		length = length % f.dataTotal
+	}
+	if f.position+length > MaxTotalLen {
+		return nil, errors.New("created too large a string")
+	}
+	byteBegin := f.position - 1
+	if byteBegin >= f.dataTotal {
+		return nil, errors.New("not enough bytes to create byte array")
+	}
+	if length == 0 {
+		return nil, errors.New("zero-length is not supported")
+	}
+	if byteBegin+length >= f.dataTotal {
+		return nil, errors.New("not enough bytes to create byte array")
+	}
+	if byteBegin+length < byteBegin {
+		return nil, errors.New("numbers overflow")
+	}
+	f.position = byteBegin + length
+	return f.data[byteBegin:f.position], nil
+}
+
+// getTarFileName is similar to GetString(), but creates string based
+// on the length of f.data to reduce the likelihood of overflowing
+// f.data.
+func (f *ConsumeFuzzer) getTarFilename() (string, error) {
+	length, err := f.GetUint32()
+	if err != nil {
+		return "nil", errors.New("not enough bytes to create string")
+	}
+
+	// A bit of optimization to attempt to create a file name
+	// when we don't have as many bytes left as "length"
+	remainingBytes := f.dataTotal - f.position
+	if remainingBytes == 0 {
+		return "nil", errors.New("created too large a string")
+	}
+	if remainingBytes < 50 {
+		length = length % remainingBytes
+	} else if f.dataTotal < 500 {
+		length = length % f.dataTotal
+	}
+	if f.position > MaxTotalLen {
+		return "nil", errors.New("created too large a string")
+	}
+	byteBegin := f.position - 1
+	if byteBegin >= f.dataTotal {
+		return "nil", errors.New("not enough bytes to create string")
+	}
+	if byteBegin+length > f.dataTotal {
+		return "nil", errors.New("not enough bytes to create string")
+	}
+	if byteBegin > byteBegin+length {
+		return "nil", errors.New("numbers overflow")
+	}
+	f.position = byteBegin + length
+	return string(f.data[byteBegin:f.position]), nil
+}
+
+// TarBytes returns valid bytes for a tar archive
+func (f *ConsumeFuzzer) TarBytes() ([]byte, error) {
+	numberOfFiles, err := f.GetInt()
+	if err != nil {
+		return nil, err
+	}
+
+	var buf bytes.Buffer
+	tw := tar.NewWriter(&buf)
+	defer tw.Close()
+
+	const maxNoOfFiles = 1000
+	for i := 0; i < numberOfFiles%maxNoOfFiles; i++ {
+		filename, err := f.getTarFilename()
+		if err != nil {
+			return returnTarBytes(buf.Bytes())
+		}
+		filebody, err := f.createTarFileBody()
+		if err != nil {
+			return returnTarBytes(buf.Bytes())
+		}
+		sec, err := f.GetInt()
+		if err != nil {
+			return returnTarBytes(buf.Bytes())
+		}
+		nsec, err := f.GetInt()
+		if err != nil {
+			return returnTarBytes(buf.Bytes())
+		}
+
+		hdr := &tar.Header{
+			Name:    filename,
+			Size:    int64(len(filebody)),
+			Mode:    0o600,
+			ModTime: time.Unix(int64(sec), int64(nsec)),
+		}
+		if err := setTarHeaderTypeflag(hdr, f); err != nil {
+			return returnTarBytes(buf.Bytes())
+		}
+		if err := setTarHeaderFormat(hdr, f); err != nil {
+			return returnTarBytes(buf.Bytes())
+		}
+		if err := tw.WriteHeader(hdr); err != nil {
+			return returnTarBytes(buf.Bytes())
+		}
+		if _, err := tw.Write(filebody); err != nil {
+			return returnTarBytes(buf.Bytes())
+		}
+	}
+	return returnTarBytes(buf.Bytes())
+}
+
+// CreateFiles creates pseudo-random files in rootDir.
+// It creates subdirs and places the files there.
+// It is the callers responsibility to ensure that
+// rootDir exists.
+func (f *ConsumeFuzzer) CreateFiles(rootDir string) error {
+	numberOfFiles, err := f.GetInt()
+	if err != nil {
+		return err
+	}
+	maxNumberOfFiles := numberOfFiles % 4000 // This is completely arbitrary
+	if maxNumberOfFiles == 0 {
+		return errors.New("maxNumberOfFiles is nil")
+	}
+
+	var noOfCreatedFiles int
+	for i := 0; i < maxNumberOfFiles; i++ {
+		// The file to create:
+		fileName, err := f.GetString()
+		if err != nil {
+			if noOfCreatedFiles > 0 {
+				// If files have been created, we don't return an error.
+				break
+			} else {
+				return errors.New("could not get fileName")
+			}
+		}
+		fullFilePath, err := securejoin.SecureJoin(rootDir, fileName)
+		if err != nil {
+			return err
+		}
+
+		// Find the subdirectory of the file
+		if subDir := filepath.Dir(fileName); subDir != "" && subDir != "." {
+			// create the dir first; avoid going outside the root dir
+			if strings.Contains(subDir, "../") || (len(subDir) > 0 && subDir[0] == 47) || strings.Contains(subDir, "\\") {
+				continue
+			}
+			dirPath, err := securejoin.SecureJoin(rootDir, subDir)
+			if err != nil {
+				continue
+			}
+			if _, err := os.Stat(dirPath); os.IsNotExist(err) {
+				err2 := os.MkdirAll(dirPath, 0o777)
+				if err2 != nil {
+					continue
+				}
+			}
+			fullFilePath, err = securejoin.SecureJoin(dirPath, fileName)
+			if err != nil {
+				continue
+			}
+		} else {
+			// Create symlink
+			createSymlink, err := f.GetBool()
+			if err != nil {
+				if noOfCreatedFiles > 0 {
+					break
+				} else {
+					return errors.New("could not create the symlink")
+				}
+			}
+			if createSymlink {
+				symlinkTarget, err := f.GetString()
+				if err != nil {
+					return err
+				}
+				err = os.Symlink(symlinkTarget, fullFilePath)
+				if err != nil {
+					return err
+				}
+				// stop loop here, since a symlink needs no further action
+				noOfCreatedFiles++
+				continue
+			}
+			// We create a normal file
+			fileContents, err := f.GetBytes()
+			if err != nil {
+				if noOfCreatedFiles > 0 {
+					break
+				} else {
+					return errors.New("could not create the file")
+				}
+			}
+			err = os.WriteFile(fullFilePath, fileContents, 0o666)
+			if err != nil {
+				continue
+			}
+			noOfCreatedFiles++
+		}
+	}
+	return nil
+}
+
+// GetStringFrom returns a string that can only consist of characters
+// included in possibleChars. It returns an error if the created string
+// does not have the specified length.
+func (f *ConsumeFuzzer) GetStringFrom(possibleChars string, length int) (string, error) {
+	if (f.dataTotal - f.position) < uint32(length) {
+		return "", errors.New("not enough bytes to create a string")
+	}
+	output := make([]byte, 0, length)
+	for i := 0; i < length; i++ {
+		charIndex, err := f.GetInt()
+		if err != nil {
+			return string(output), err
+		}
+		output = append(output, possibleChars[charIndex%len(possibleChars)])
+	}
+	return string(output), nil
+}
+
+func (f *ConsumeFuzzer) GetRune() ([]rune, error) {
+	stringToConvert, err := f.GetString()
+	if err != nil {
+		return []rune("nil"), err
+	}
+	return []rune(stringToConvert), nil
+}
+
+func (f *ConsumeFuzzer) GetFloat32() (float32, error) {
+	u32, err := f.GetNBytes(4)
+	if err != nil {
+		return 0, err
+	}
+	littleEndian, err := f.GetBool()
+	if err != nil {
+		return 0, err
+	}
+	if littleEndian {
+		u32LE := binary.LittleEndian.Uint32(u32)
+		return math.Float32frombits(u32LE), nil
+	}
+	u32BE := binary.BigEndian.Uint32(u32)
+	return math.Float32frombits(u32BE), nil
+}
+
+func (f *ConsumeFuzzer) GetFloat64() (float64, error) {
+	u64, err := f.GetNBytes(8)
+	if err != nil {
+		return 0, err
+	}
+	littleEndian, err := f.GetBool()
+	if err != nil {
+		return 0, err
+	}
+	if littleEndian {
+		u64LE := binary.LittleEndian.Uint64(u64)
+		return math.Float64frombits(u64LE), nil
+	}
+	u64BE := binary.BigEndian.Uint64(u64)
+	return math.Float64frombits(u64BE), nil
+}
+
+func (f *ConsumeFuzzer) CreateSlice(targetSlice interface{}) error {
+	return f.GenerateStruct(targetSlice)
+}

+ 48 - 0
vendor/github.com/AdaLogics/go-fuzz-headers/funcs.go

@@ -0,0 +1,48 @@
+package gofuzzheaders
+
+import (
+	"fmt"
+	"reflect"
+)
+
+type Continue struct {
+	F *ConsumeFuzzer
+}
+
+func (f *ConsumeFuzzer) AddFuncs(fuzzFuncs []interface{}) {
+	for i := range fuzzFuncs {
+		v := reflect.ValueOf(fuzzFuncs[i])
+		if v.Kind() != reflect.Func {
+			panic("Need only funcs!")
+		}
+		t := v.Type()
+		if t.NumIn() != 2 || t.NumOut() != 1 {
+			fmt.Println(t.NumIn(), t.NumOut())
+
+			panic("Need 2 in and 1 out params. In must be the type. Out must be an error")
+		}
+		argT := t.In(0)
+		switch argT.Kind() {
+		case reflect.Ptr, reflect.Map:
+		default:
+			panic("fuzzFunc must take pointer or map type")
+		}
+		if t.In(1) != reflect.TypeOf(Continue{}) {
+			panic("fuzzFunc's second parameter must be type Continue")
+		}
+		f.Funcs[argT] = v
+	}
+}
+
+func (f *ConsumeFuzzer) GenerateWithCustom(targetStruct interface{}) error {
+	e := reflect.ValueOf(targetStruct).Elem()
+	return f.fuzzStruct(e, true)
+}
+
+func (c Continue) GenerateStruct(targetStruct interface{}) error {
+	return c.F.GenerateStruct(targetStruct)
+}
+
+func (c Continue) GenerateStructWithCustom(targetStruct interface{}) error {
+	return c.F.GenerateWithCustom(targetStruct)
+}

+ 542 - 0
vendor/github.com/AdaLogics/go-fuzz-headers/sql.go

@@ -0,0 +1,542 @@
+package gofuzzheaders
+
+import (
+	"fmt"
+	"strings"
+)
+
+// returns a keyword by index
+func getKeyword(f *ConsumeFuzzer) (string, error) {
+	index, err := f.GetInt()
+	if err != nil {
+		return keywords[0], err
+	}
+	for i, k := range keywords {
+		if i == index {
+			return k, nil
+		}
+	}
+	return keywords[0], fmt.Errorf("could not get a kw")
+}
+
+// Simple utility function to check if a string
+// slice contains a string.
+func containsString(s []string, e string) bool {
+	for _, a := range s {
+		if a == e {
+			return true
+		}
+	}
+	return false
+}
+
+// These keywords are used specifically for fuzzing Vitess
+var keywords = []string{
+	"accessible", "action", "add", "after", "against", "algorithm",
+	"all", "alter", "always", "analyze", "and", "as", "asc", "asensitive",
+	"auto_increment", "avg_row_length", "before", "begin", "between",
+	"bigint", "binary", "_binary", "_utf8mb4", "_utf8", "_latin1", "bit",
+	"blob", "bool", "boolean", "both", "by", "call", "cancel", "cascade",
+	"cascaded", "case", "cast", "channel", "change", "char", "character",
+	"charset", "check", "checksum", "coalesce", "code", "collate", "collation",
+	"column", "columns", "comment", "committed", "commit", "compact", "complete",
+	"compressed", "compression", "condition", "connection", "constraint", "continue",
+	"convert", "copy", "cume_dist", "substr", "substring", "create", "cross",
+	"csv", "current_date", "current_time", "current_timestamp", "current_user",
+	"cursor", "data", "database", "databases", "day", "day_hour", "day_microsecond",
+	"day_minute", "day_second", "date", "datetime", "dec", "decimal", "declare",
+	"default", "definer", "delay_key_write", "delayed", "delete", "dense_rank",
+	"desc", "describe", "deterministic", "directory", "disable", "discard",
+	"disk", "distinct", "distinctrow", "div", "double", "do", "drop", "dumpfile",
+	"duplicate", "dynamic", "each", "else", "elseif", "empty", "enable",
+	"enclosed", "encryption", "end", "enforced", "engine", "engines", "enum",
+	"error", "escape", "escaped", "event", "exchange", "exclusive", "exists",
+	"exit", "explain", "expansion", "export", "extended", "extract", "false",
+	"fetch", "fields", "first", "first_value", "fixed", "float", "float4",
+	"float8", "flush", "for", "force", "foreign", "format", "from", "full",
+	"fulltext", "function", "general", "generated", "geometry", "geometrycollection",
+	"get", "global", "gtid_executed", "grant", "group", "grouping", "groups",
+	"group_concat", "having", "header", "high_priority", "hosts", "hour", "hour_microsecond",
+	"hour_minute", "hour_second", "if", "ignore", "import", "in", "index", "indexes",
+	"infile", "inout", "inner", "inplace", "insensitive", "insert", "insert_method",
+	"int", "int1", "int2", "int3", "int4", "int8", "integer", "interval",
+	"into", "io_after_gtids", "is", "isolation", "iterate", "invoker", "join",
+	"json", "json_table", "key", "keys", "keyspaces", "key_block_size", "kill", "lag",
+	"language", "last", "last_value", "last_insert_id", "lateral", "lead", "leading",
+	"leave", "left", "less", "level", "like", "limit", "linear", "lines",
+	"linestring", "load", "local", "localtime", "localtimestamp", "lock", "logs",
+	"long", "longblob", "longtext", "loop", "low_priority", "manifest",
+	"master_bind", "match", "max_rows", "maxvalue", "mediumblob", "mediumint",
+	"mediumtext", "memory", "merge", "microsecond", "middleint", "min_rows", "minute",
+	"minute_microsecond", "minute_second", "mod", "mode", "modify", "modifies",
+	"multilinestring", "multipoint", "multipolygon", "month", "name",
+	"names", "natural", "nchar", "next", "no", "none", "not", "no_write_to_binlog",
+	"nth_value", "ntile", "null", "numeric", "of", "off", "offset", "on",
+	"only", "open", "optimize", "optimizer_costs", "option", "optionally",
+	"or", "order", "out", "outer", "outfile", "over", "overwrite", "pack_keys",
+	"parser", "partition", "partitioning", "password", "percent_rank", "plugins",
+	"point", "polygon", "precision", "primary", "privileges", "processlist",
+	"procedure", "query", "quarter", "range", "rank", "read", "reads", "read_write",
+	"real", "rebuild", "recursive", "redundant", "references", "regexp", "relay",
+	"release", "remove", "rename", "reorganize", "repair", "repeat", "repeatable",
+	"replace", "require", "resignal", "restrict", "return", "retry", "revert",
+	"revoke", "right", "rlike", "rollback", "row", "row_format", "row_number",
+	"rows", "s3", "savepoint", "schema", "schemas", "second", "second_microsecond",
+	"security", "select", "sensitive", "separator", "sequence", "serializable",
+	"session", "set", "share", "shared", "show", "signal", "signed", "slow",
+	"smallint", "spatial", "specific", "sql", "sqlexception", "sqlstate",
+	"sqlwarning", "sql_big_result", "sql_cache", "sql_calc_found_rows",
+	"sql_no_cache", "sql_small_result", "ssl", "start", "starting",
+	"stats_auto_recalc", "stats_persistent", "stats_sample_pages", "status",
+	"storage", "stored", "straight_join", "stream", "system", "vstream",
+	"table", "tables", "tablespace", "temporary", "temptable", "terminated",
+	"text", "than", "then", "time", "timestamp", "timestampadd", "timestampdiff",
+	"tinyblob", "tinyint", "tinytext", "to", "trailing", "transaction", "tree",
+	"traditional", "trigger", "triggers", "true", "truncate", "uncommitted",
+	"undefined", "undo", "union", "unique", "unlock", "unsigned", "update",
+	"upgrade", "usage", "use", "user", "user_resources", "using", "utc_date",
+	"utc_time", "utc_timestamp", "validation", "values", "variables", "varbinary",
+	"varchar", "varcharacter", "varying", "vgtid_executed", "virtual", "vindex",
+	"vindexes", "view", "vitess", "vitess_keyspaces", "vitess_metadata",
+	"vitess_migration", "vitess_migrations", "vitess_replication_status",
+	"vitess_shards", "vitess_tablets", "vschema", "warnings", "when",
+	"where", "while", "window", "with", "without", "work", "write", "xor",
+	"year", "year_month", "zerofill",
+}
+
+// Keywords that could get an additional keyword
+var needCustomString = []string{
+	"DISTINCTROW", "FROM", // Select keywords:
+	"GROUP BY", "HAVING", "WINDOW",
+	"FOR",
+	"ORDER BY", "LIMIT",
+	"INTO", "PARTITION", "AS", // Insert Keywords:
+	"ON DUPLICATE KEY UPDATE",
+	"WHERE", "LIMIT", // Delete keywords
+	"INFILE", "INTO TABLE", "CHARACTER SET", // Load keywords
+	"TERMINATED BY", "ENCLOSED BY",
+	"ESCAPED BY", "STARTING BY",
+	"TERMINATED BY", "STARTING BY",
+	"IGNORE",
+	"VALUE", "VALUES", // Replace tokens
+	"SET",                                   // Update tokens
+	"ENGINE =",                              // Drop tokens
+	"DEFINER =", "ON SCHEDULE", "RENAME TO", // Alter tokens
+	"COMMENT", "DO", "INITIAL_SIZE = ", "OPTIONS",
+}
+
+var alterTableTokens = [][]string{
+	{"CUSTOM_FUZZ_STRING"},
+	{"CUSTOM_ALTTER_TABLE_OPTIONS"},
+	{"PARTITION_OPTIONS_FOR_ALTER_TABLE"},
+}
+
+var alterTokens = [][]string{
+	{
+		"DATABASE", "SCHEMA", "DEFINER = ", "EVENT", "FUNCTION", "INSTANCE",
+		"LOGFILE GROUP", "PROCEDURE", "SERVER",
+	},
+	{"CUSTOM_FUZZ_STRING"},
+	{
+		"ON SCHEDULE", "ON COMPLETION PRESERVE", "ON COMPLETION NOT PRESERVE",
+		"ADD UNDOFILE", "OPTIONS",
+	},
+	{"RENAME TO", "INITIAL_SIZE = "},
+	{"ENABLE", "DISABLE", "DISABLE ON SLAVE", "ENGINE"},
+	{"COMMENT"},
+	{"DO"},
+}
+
+var setTokens = [][]string{
+	{"CHARACTER SET", "CHARSET", "CUSTOM_FUZZ_STRING", "NAMES"},
+	{"CUSTOM_FUZZ_STRING", "DEFAULT", "="},
+	{"CUSTOM_FUZZ_STRING"},
+}
+
+var dropTokens = [][]string{
+	{"TEMPORARY", "UNDO"},
+	{
+		"DATABASE", "SCHEMA", "EVENT", "INDEX", "LOGFILE GROUP",
+		"PROCEDURE", "FUNCTION", "SERVER", "SPATIAL REFERENCE SYSTEM",
+		"TABLE", "TABLESPACE", "TRIGGER", "VIEW",
+	},
+	{"IF EXISTS"},
+	{"CUSTOM_FUZZ_STRING"},
+	{"ON", "ENGINE = ", "RESTRICT", "CASCADE"},
+}
+
+var renameTokens = [][]string{
+	{"TABLE"},
+	{"CUSTOM_FUZZ_STRING"},
+	{"TO"},
+	{"CUSTOM_FUZZ_STRING"},
+}
+
+var truncateTokens = [][]string{
+	{"TABLE"},
+	{"CUSTOM_FUZZ_STRING"},
+}
+
+var createTokens = [][]string{
+	{"OR REPLACE", "TEMPORARY", "UNDO"}, // For create spatial reference system
+	{
+		"UNIQUE", "FULLTEXT", "SPATIAL", "ALGORITHM = UNDEFINED", "ALGORITHM = MERGE",
+		"ALGORITHM = TEMPTABLE",
+	},
+	{
+		"DATABASE", "SCHEMA", "EVENT", "FUNCTION", "INDEX", "LOGFILE GROUP",
+		"PROCEDURE", "SERVER", "SPATIAL REFERENCE SYSTEM", "TABLE", "TABLESPACE",
+		"TRIGGER", "VIEW",
+	},
+	{"IF NOT EXISTS"},
+	{"CUSTOM_FUZZ_STRING"},
+}
+
+/*
+// For future use.
+var updateTokens = [][]string{
+	{"LOW_PRIORITY"},
+	{"IGNORE"},
+	{"SET"},
+	{"WHERE"},
+	{"ORDER BY"},
+	{"LIMIT"},
+}
+*/
+
+var replaceTokens = [][]string{
+	{"LOW_PRIORITY", "DELAYED"},
+	{"INTO"},
+	{"PARTITION"},
+	{"CUSTOM_FUZZ_STRING"},
+	{"VALUES", "VALUE"},
+}
+
+var loadTokens = [][]string{
+	{"DATA"},
+	{"LOW_PRIORITY", "CONCURRENT", "LOCAL"},
+	{"INFILE"},
+	{"REPLACE", "IGNORE"},
+	{"INTO TABLE"},
+	{"PARTITION"},
+	{"CHARACTER SET"},
+	{"FIELDS", "COLUMNS"},
+	{"TERMINATED BY"},
+	{"OPTIONALLY"},
+	{"ENCLOSED BY"},
+	{"ESCAPED BY"},
+	{"LINES"},
+	{"STARTING BY"},
+	{"TERMINATED BY"},
+	{"IGNORE"},
+	{"LINES", "ROWS"},
+	{"CUSTOM_FUZZ_STRING"},
+}
+
+// These Are everything that comes after "INSERT"
+var insertTokens = [][]string{
+	{"LOW_PRIORITY", "DELAYED", "HIGH_PRIORITY", "IGNORE"},
+	{"INTO"},
+	{"PARTITION"},
+	{"CUSTOM_FUZZ_STRING"},
+	{"AS"},
+	{"ON DUPLICATE KEY UPDATE"},
+}
+
+// These are everything that comes after "SELECT"
+var selectTokens = [][]string{
+	{"*", "CUSTOM_FUZZ_STRING", "DISTINCTROW"},
+	{"HIGH_PRIORITY"},
+	{"STRAIGHT_JOIN"},
+	{"SQL_SMALL_RESULT", "SQL_BIG_RESULT", "SQL_BUFFER_RESULT"},
+	{"SQL_NO_CACHE", "SQL_CALC_FOUND_ROWS"},
+	{"CUSTOM_FUZZ_STRING"},
+	{"FROM"},
+	{"WHERE"},
+	{"GROUP BY"},
+	{"HAVING"},
+	{"WINDOW"},
+	{"ORDER BY"},
+	{"LIMIT"},
+	{"CUSTOM_FUZZ_STRING"},
+	{"FOR"},
+}
+
+// These are everything that comes after "DELETE"
+var deleteTokens = [][]string{
+	{"LOW_PRIORITY", "QUICK", "IGNORE", "FROM", "AS"},
+	{"PARTITION"},
+	{"WHERE"},
+	{"ORDER BY"},
+	{"LIMIT"},
+}
+
+var alter_table_options = []string{
+	"ADD", "COLUMN", "FIRST", "AFTER", "INDEX", "KEY", "FULLTEXT", "SPATIAL",
+	"CONSTRAINT", "UNIQUE", "FOREIGN KEY", "CHECK", "ENFORCED", "DROP", "ALTER",
+	"NOT", "INPLACE", "COPY", "SET", "VISIBLE", "INVISIBLE", "DEFAULT", "CHANGE",
+	"CHARACTER SET", "COLLATE", "DISABLE", "ENABLE", "KEYS", "TABLESPACE", "LOCK",
+	"FORCE", "MODIFY", "SHARED", "EXCLUSIVE", "NONE", "ORDER BY", "RENAME COLUMN",
+	"AS", "=", "ASC", "DESC", "WITH", "WITHOUT", "VALIDATION", "ADD PARTITION",
+	"DROP PARTITION", "DISCARD PARTITION", "IMPORT PARTITION", "TRUNCATE PARTITION",
+	"COALESCE PARTITION", "REORGANIZE PARTITION", "EXCHANGE PARTITION",
+	"ANALYZE PARTITION", "CHECK PARTITION", "OPTIMIZE PARTITION", "REBUILD PARTITION",
+	"REPAIR PARTITION", "REMOVE PARTITIONING", "USING", "BTREE", "HASH", "COMMENT",
+	"KEY_BLOCK_SIZE", "WITH PARSER", "AUTOEXTEND_SIZE", "AUTO_INCREMENT", "AVG_ROW_LENGTH",
+	"CHECKSUM", "INSERT_METHOD", "ROW_FORMAT", "DYNAMIC", "FIXED", "COMPRESSED", "REDUNDANT",
+	"COMPACT", "SECONDARY_ENGINE_ATTRIBUTE", "STATS_AUTO_RECALC", "STATS_PERSISTENT",
+	"STATS_SAMPLE_PAGES", "ZLIB", "LZ4", "ENGINE_ATTRIBUTE", "KEY_BLOCK_SIZE", "MAX_ROWS",
+	"MIN_ROWS", "PACK_KEYS", "PASSWORD", "COMPRESSION", "CONNECTION", "DIRECTORY",
+	"DELAY_KEY_WRITE", "ENCRYPTION", "STORAGE", "DISK", "MEMORY", "UNION",
+}
+
+// Creates an 'alter table' statement. 'alter table' is an exception
+// in that it has its own function. The majority of statements
+// are created by 'createStmt()'.
+func createAlterTableStmt(f *ConsumeFuzzer) (string, error) {
+	maxArgs, err := f.GetInt()
+	if err != nil {
+		return "", err
+	}
+	maxArgs = maxArgs % 30
+	if maxArgs == 0 {
+		return "", fmt.Errorf("could not create alter table stmt")
+	}
+
+	var stmt strings.Builder
+	stmt.WriteString("ALTER TABLE ")
+	for i := 0; i < maxArgs; i++ {
+		// Calculate if we get existing token or custom string
+		tokenType, err := f.GetInt()
+		if err != nil {
+			return "", err
+		}
+		if tokenType%4 == 1 {
+			customString, err := f.GetString()
+			if err != nil {
+				return "", err
+			}
+			stmt.WriteString(" " + customString)
+		} else {
+			tokenIndex, err := f.GetInt()
+			if err != nil {
+				return "", err
+			}
+			stmt.WriteString(" " + alter_table_options[tokenIndex%len(alter_table_options)])
+		}
+	}
+	return stmt.String(), nil
+}
+
+func chooseToken(tokens []string, f *ConsumeFuzzer) (string, error) {
+	index, err := f.GetInt()
+	if err != nil {
+		return "", err
+	}
+	var token strings.Builder
+	token.WriteString(tokens[index%len(tokens)])
+	if token.String() == "CUSTOM_FUZZ_STRING" {
+		customFuzzString, err := f.GetString()
+		if err != nil {
+			return "", err
+		}
+		return customFuzzString, nil
+	}
+
+	// Check if token requires an argument
+	if containsString(needCustomString, token.String()) {
+		customFuzzString, err := f.GetString()
+		if err != nil {
+			return "", err
+		}
+		token.WriteString(" " + customFuzzString)
+	}
+	return token.String(), nil
+}
+
+var stmtTypes = map[string][][]string{
+	"DELETE":      deleteTokens,
+	"INSERT":      insertTokens,
+	"SELECT":      selectTokens,
+	"LOAD":        loadTokens,
+	"REPLACE":     replaceTokens,
+	"CREATE":      createTokens,
+	"DROP":        dropTokens,
+	"RENAME":      renameTokens,
+	"TRUNCATE":    truncateTokens,
+	"SET":         setTokens,
+	"ALTER":       alterTokens,
+	"ALTER TABLE": alterTableTokens, // ALTER TABLE has its own set of tokens
+}
+
+var stmtTypeEnum = map[int]string{
+	0:  "DELETE",
+	1:  "INSERT",
+	2:  "SELECT",
+	3:  "LOAD",
+	4:  "REPLACE",
+	5:  "CREATE",
+	6:  "DROP",
+	7:  "RENAME",
+	8:  "TRUNCATE",
+	9:  "SET",
+	10: "ALTER",
+	11: "ALTER TABLE",
+}
+
+func createStmt(f *ConsumeFuzzer) (string, error) {
+	stmtIndex, err := f.GetInt()
+	if err != nil {
+		return "", err
+	}
+	stmtIndex = stmtIndex % len(stmtTypes)
+
+	queryType := stmtTypeEnum[stmtIndex]
+	tokens := stmtTypes[queryType]
+
+	// We have custom creator for ALTER TABLE
+	if queryType == "ALTER TABLE" {
+		query, err := createAlterTableStmt(f)
+		if err != nil {
+			return "", err
+		}
+		return query, nil
+	}
+
+	// Here we are creating a query that is not
+	// an 'alter table' query. For available
+	// queries, see "stmtTypes"
+
+	// First specify the first query keyword:
+	var query strings.Builder
+	query.WriteString(queryType)
+
+	// Next create the args for the
+	queryArgs, err := createStmtArgs(tokens, f)
+	if err != nil {
+		return "", err
+	}
+	query.WriteString(" " + queryArgs)
+	return query.String(), nil
+}
+
+// Creates the arguments of a statements. In a select statement
+// that would be everything after "select".
+func createStmtArgs(tokenslice [][]string, f *ConsumeFuzzer) (string, error) {
+	var query, token strings.Builder
+
+	// We go through the tokens in the tokenslice,
+	// create the respective token and add it to
+	// "query"
+	for _, tokens := range tokenslice {
+		// For extra randomization, the fuzzer can
+		// choose to not include this token.
+		includeThisToken, err := f.GetBool()
+		if err != nil {
+			return "", err
+		}
+		if !includeThisToken {
+			continue
+		}
+
+		// There may be several tokens to choose from:
+		if len(tokens) > 1 {
+			chosenToken, err := chooseToken(tokens, f)
+			if err != nil {
+				return "", err
+			}
+			query.WriteString(" " + chosenToken)
+		} else {
+			token.WriteString(tokens[0])
+
+			// In case the token is "CUSTOM_FUZZ_STRING"
+			// we will then create a non-structured string
+			if token.String() == "CUSTOM_FUZZ_STRING" {
+				customFuzzString, err := f.GetString()
+				if err != nil {
+					return "", err
+				}
+				query.WriteString(" " + customFuzzString)
+				continue
+			}
+
+			// Check if token requires an argument.
+			// Tokens that take an argument can be found
+			// in 'needCustomString'. If so, we add a
+			// non-structured string to the token.
+			if containsString(needCustomString, token.String()) {
+				customFuzzString, err := f.GetString()
+				if err != nil {
+					return "", err
+				}
+				token.WriteString(fmt.Sprintf(" %s", customFuzzString))
+			}
+			query.WriteString(fmt.Sprintf(" %s", token.String()))
+		}
+	}
+	return query.String(), nil
+}
+
+// Creates a semi-structured query. It creates a string
+// that is a combination of the keywords and random strings.
+func createQuery(f *ConsumeFuzzer) (string, error) {
+	queryLen, err := f.GetInt()
+	if err != nil {
+		return "", err
+	}
+	maxLen := queryLen % 60
+	if maxLen == 0 {
+		return "", fmt.Errorf("could not create a query")
+	}
+	var query strings.Builder
+	for i := 0; i < maxLen; i++ {
+		// Get a new token:
+		useKeyword, err := f.GetBool()
+		if err != nil {
+			return "", err
+		}
+		if useKeyword {
+			keyword, err := getKeyword(f)
+			if err != nil {
+				return "", err
+			}
+			query.WriteString(" " + keyword)
+		} else {
+			customString, err := f.GetString()
+			if err != nil {
+				return "", err
+			}
+			query.WriteString(" " + customString)
+		}
+	}
+	if query.String() == "" {
+		return "", fmt.Errorf("could not create a query")
+	}
+	return query.String(), nil
+}
+
+// GetSQLString is the API that users interact with.
+//
+// Usage:
+//
+//	f := NewConsumer(data)
+//	sqlString, err := f.GetSQLString()
+func (f *ConsumeFuzzer) GetSQLString() (string, error) {
+	var query string
+	veryStructured, err := f.GetBool()
+	if err != nil {
+		return "", err
+	}
+	if veryStructured {
+		query, err = createStmt(f)
+		if err != nil {
+			return "", err
+		}
+	} else {
+		query, err = createQuery(f)
+		if err != nil {
+			return "", err
+		}
+	}
+	return query, nil
+}

+ 3 - 0
vendor/modules.txt

@@ -13,6 +13,9 @@ cloud.google.com/go/logging/internal
 # code.cloudfoundry.org/clock v1.0.0
 ## explicit
 code.cloudfoundry.org/clock
+# github.com/AdaLogics/go-fuzz-headers v0.0.0-20221118232415-3345c89a7c72
+## explicit; go 1.18
+github.com/AdaLogics/go-fuzz-headers
 # github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1
 ## explicit; go 1.16
 github.com/Azure/go-ansiterm

+ 15 - 0
volume/mounts/fuzz_test.go

@@ -0,0 +1,15 @@
+package mounts
+
+import (
+	"testing"
+)
+
+func FuzzParseLinux(f *testing.F) {
+	f.Fuzz(func(t *testing.T, data []byte) {
+		parser := NewLinuxParser()
+		if p, ok := parser.(*linuxParser); ok {
+			p.fi = mockFiProvider{}
+		}
+		_, _ = parser.ParseMountRaw(string(data), "local")
+	})
+}