THRIFT-5855: go fuzzers

Add fuzzers for go support, to improve the reliability/robustness of the implementation
diff --git a/FUZZING.md b/FUZZING.md
index 0a48246..f239bb7 100644
--- a/FUZZING.md
+++ b/FUZZING.md
@@ -15,7 +15,7 @@
 
 We currently maintain fuzzers for the following languages:
 
-- Go (needs improvement)
+- Go
 - c_glib (partially supported, needs round-trip support)
 - C++
 
diff --git a/lib/go/test/fuzz/Makefile.am b/lib/go/test/fuzz/Makefile.am
index a481e95..e9a960b 100644
--- a/lib/go/test/fuzz/Makefile.am
+++ b/lib/go/test/fuzz/Makefile.am
@@ -19,6 +19,7 @@
 
 gopathfuzz: $(THRIFT) fuzz.go
 	$(THRIFT) -r --gen go:thrift_import=github.com/apache/thrift/lib/go/thrift,package_prefix=github.com/apache/thrift/lib/go/test/fuzz/gen-go/$(COMPILER_EXTRAFLAG) ../../../../tutorial/tutorial.thrift
+	$(THRIFT) -r --gen go:thrift_import=github.com/apache/thrift/lib/go/thrift,package_prefix=github.com/apache/thrift/lib/go/test/fuzz/gen-go/$(COMPILER_EXTRAFLAG) ../../../../test/FuzzTest.thrift
 	touch gopathfuzz
 
 check: gopathfuzz
diff --git a/lib/go/test/fuzz/README.md b/lib/go/test/fuzz/README.md
new file mode 100644
index 0000000..54bc4ba
--- /dev/null
+++ b/lib/go/test/fuzz/README.md
@@ -0,0 +1,15 @@
+# Go fuzzing README
+
+To build the fuzz targets, simply run `make check` in this directory
+
+To reproduce a bug, update the code in the fuzz_test.go file, pass the right input buffer in there and update the code to call the relevant function.
+
+We currently have the following fuzz targets:
+
+* FuzzTutorial -- a fuzzer which spins up an mini server and fuzzes it with random data, following the tutorial example
+* FuzzParseCompact -- fuzzes the deserialization of the Compact protocol
+* FuzzParseBinary -- fuzzes the deserialization of the Binary protocol
+* FuzzParseJson -- fuzzes the deserialization of the JSON protocol
+* FuzzRoundtripCompact -- fuzzes the roundtrip of the Compact protocol
+* FuzzRoundtripBinary -- fuzzes the roundtrip of the Binary protocol
+* FuzzRoundtripJson -- fuzzes the roundtrip of the JSON protocol
diff --git a/lib/go/test/fuzz/fuzz.go b/lib/go/test/fuzz/fuzz.go
index 0698379..bad6b41 100644
--- a/lib/go/test/fuzz/fuzz.go
+++ b/lib/go/test/fuzz/fuzz.go
@@ -29,11 +29,15 @@
 
 	"github.com/apache/thrift/lib/go/test/fuzz/gen-go/shared"
 	"github.com/apache/thrift/lib/go/test/fuzz/gen-go/tutorial"
+	"github.com/apache/thrift/lib/go/test/fuzz/gen-go/fuzztest"
 	"github.com/apache/thrift/lib/go/thrift"
 )
 
 const nbFuzzedProtocols = 2
 
+// 10MB message size limit to prevent over-allocation during fuzzing
+const FUZZ_MAX_MESSAGE_SIZE = 10 * 1024 * 1024
+
 func fuzzChooseProtocol(d byte, t thrift.TTransport) thrift.TProtocol {
 	switch d % nbFuzzedProtocols {
 	default:
@@ -42,10 +46,12 @@
 		return thrift.NewTBinaryProtocolFactoryConf(nil).GetProtocol(t)
 	case 1:
 		return thrift.NewTCompactProtocolFactoryConf(nil).GetProtocol(t)
+	case 2:
+		return thrift.NewTJSONProtocolFactory().GetProtocol(t)
 	}
 }
 
-func Fuzz(data []byte) int {
+func FuzzTutorial(data []byte) int {
 	if len(data) < 2 {
 		return 0
 	}
@@ -142,3 +148,242 @@
 	fmt.Print("zip()\n")
 	return nil
 }
+
+func FuzzParseBinary(data []byte) int {
+	// Skip if input is too small
+	if len(data) < 1 {
+		return 0
+	}
+
+	// Create transport and protocol
+	transport := thrift.NewTMemoryBufferLen(len(data))
+	defer func() {
+		transport.Close()
+		// Reset the buffer to release memory
+		transport.Buffer.Reset()
+	}()
+	transport.Write(data)
+	config := &thrift.TConfiguration{
+		MaxMessageSize: FUZZ_MAX_MESSAGE_SIZE,
+	}
+	protocol := thrift.NewTBinaryProtocolFactoryConf(config).GetProtocol(transport)
+
+	// Try to read the FuzzTest structure
+	fuzzTest := fuzztest.NewFuzzTest()
+	err := fuzzTest.Read(context.Background(), protocol)
+	if err != nil {
+		// Invalid input, but not a crash
+		return 0
+	}
+
+	// Successfully parsed
+	return 1
+}
+
+func FuzzParseCompact(data []byte) int {
+	// Skip if input is too small
+	if len(data) < 1 {
+		return 0
+	}
+
+	// Create transport and protocol
+	transport := thrift.NewTMemoryBufferLen(len(data))
+	defer func() {
+		transport.Close()
+		// Reset the buffer to release memory
+		transport.Buffer.Reset()
+	}()
+	transport.Write(data)
+	config := &thrift.TConfiguration{
+		MaxMessageSize: FUZZ_MAX_MESSAGE_SIZE,
+	}
+	protocol := thrift.NewTCompactProtocolFactoryConf(config).GetProtocol(transport)
+
+	// Try to read the FuzzTest structure
+	fuzzTest := fuzztest.NewFuzzTest()
+	err := fuzzTest.Read(context.Background(), protocol)
+	if err != nil {
+		// Invalid input, but not a crash
+		return 0
+	}
+
+	// Successfully parsed
+	return 1
+}
+
+func FuzzParseJson(data []byte) int {
+	// Skip if input is too small
+	if len(data) < 1 {
+		return 0
+	}
+
+	// Create transport and protocol
+	transport := thrift.NewTMemoryBufferLen(len(data))
+	defer func() {
+		transport.Close()
+		// Reset the buffer to release memory
+		transport.Buffer.Reset()
+	}()
+	transport.Write(data)
+	protocol := thrift.NewTJSONProtocolFactory().GetProtocol(transport)
+
+	// Try to read the FuzzTest structure
+	fuzzTest := fuzztest.NewFuzzTest()
+	err := fuzzTest.Read(context.Background(), protocol)
+	if err != nil {
+		// Invalid input, but not a crash
+		return 0
+	}
+
+	// Successfully parsed
+	return 1
+}
+
+func FuzzRoundtripBinary(data []byte) int {
+	// Skip if input is too small
+	if len(data) < 1 {
+		return 0
+	}
+
+	config := &thrift.TConfiguration{
+		MaxMessageSize: FUZZ_MAX_MESSAGE_SIZE,
+	}
+
+	// First parse
+	transport := thrift.NewTMemoryBufferLen(len(data))
+	transport.Write(data)
+	protocol := thrift.NewTBinaryProtocolFactoryConf(config).GetProtocol(transport)
+
+	// Try to read the FuzzTest structure
+	test1 := fuzztest.NewFuzzTest()
+	err := test1.Read(context.Background(), protocol)
+	if err != nil {
+		// Invalid input, but not a crash
+		return 0
+	}
+
+	// Serialize back
+	outTransport := thrift.NewTMemoryBuffer()
+	outProtocol := thrift.NewTBinaryProtocolFactoryConf(config).GetProtocol(outTransport)
+	err = test1.Write(context.Background(), outProtocol)
+	if err != nil {
+		return 0
+	}
+
+	// Get serialized data and deserialize again
+	serialized := outTransport.Bytes()
+	reTransport := thrift.NewTMemoryBufferLen(len(serialized))
+	reTransport.Write(serialized)
+	reProtocol := thrift.NewTBinaryProtocolFactoryConf(config).GetProtocol(reTransport)
+
+	test2 := fuzztest.NewFuzzTest()
+	err = test2.Read(context.Background(), reProtocol)
+	if err != nil {
+		return 0
+	}
+
+	// Verify equality
+	if !test1.Equals(test2) {
+		panic("Roundtrip failed: objects not equal after deserialization")
+	}
+
+	return 1
+}
+
+func FuzzRoundtripCompact(data []byte) int {
+	// Skip if input is too small
+	if len(data) < 1 {
+		return 0
+	}
+
+	config := &thrift.TConfiguration{
+		MaxMessageSize: FUZZ_MAX_MESSAGE_SIZE,
+	}
+
+	// First parse
+	transport := thrift.NewTMemoryBufferLen(len(data))
+	transport.Write(data)
+	protocol := thrift.NewTCompactProtocolFactoryConf(config).GetProtocol(transport)
+
+	// Try to read the FuzzTest structure
+	test1 := fuzztest.NewFuzzTest()
+	err := test1.Read(context.Background(), protocol)
+	if err != nil {
+		// Invalid input, but not a crash
+		return 0
+	}
+
+	// Serialize back
+	outTransport := thrift.NewTMemoryBuffer()
+	outProtocol := thrift.NewTCompactProtocolFactoryConf(config).GetProtocol(outTransport)
+	err = test1.Write(context.Background(), outProtocol)
+	if err != nil {
+		return 0
+	}
+
+	// Get serialized data and deserialize again
+	serialized := outTransport.Bytes()
+	reTransport := thrift.NewTMemoryBufferLen(len(serialized))
+	reTransport.Write(serialized)
+	reProtocol := thrift.NewTCompactProtocolFactoryConf(config).GetProtocol(reTransport)
+
+	test2 := fuzztest.NewFuzzTest()
+	err = test2.Read(context.Background(), reProtocol)
+	if err != nil {
+		return 0
+	}
+
+	// Verify equality
+	if !test1.Equals(test2) {
+		panic("Roundtrip failed: objects not equal after deserialization")
+	}
+
+	return 1
+}
+
+func FuzzRoundtripJson(data []byte) int {
+	// Skip if input is too small
+	if len(data) < 1 {
+		return 0
+	}
+
+	// First parse
+	transport := thrift.NewTMemoryBufferLen(len(data))
+	transport.Write(data)
+	protocol := thrift.NewTJSONProtocolFactory().GetProtocol(transport)
+
+	// Try to read the FuzzTest structure
+	test1 := fuzztest.NewFuzzTest()
+	err := test1.Read(context.Background(), protocol)
+	if err != nil {
+		// Invalid input, but not a crash
+		return 0
+	}
+
+	// Serialize back
+	outTransport := thrift.NewTMemoryBuffer()
+	outProtocol := thrift.NewTJSONProtocolFactory().GetProtocol(outTransport)
+	err = test1.Write(context.Background(), outProtocol)
+	if err != nil {
+		return 0
+	}
+
+	// Get serialized data and deserialize again
+	serialized := outTransport.Bytes()
+	reTransport := thrift.NewTMemoryBufferLen(len(serialized))
+	reTransport.Write(serialized)
+	reProtocol := thrift.NewTJSONProtocolFactory().GetProtocol(reTransport)
+
+	test2 := fuzztest.NewFuzzTest()
+	err = test2.Read(context.Background(), reProtocol)
+	if err != nil {
+		return 0
+	}
+
+	// Verify equality
+	if !test1.Equals(test2) {
+		panic("Roundtrip failed: objects not equal after deserialization")
+	}
+
+	return 1
+}
diff --git a/lib/go/test/fuzz/fuzz_test.go b/lib/go/test/fuzz/fuzz_test.go
index 2983e0f..73eb072 100644
--- a/lib/go/test/fuzz/fuzz_test.go
+++ b/lib/go/test/fuzz/fuzz_test.go
@@ -26,5 +26,5 @@
 )
 
 func TestFuzz(t *testing.T) {
-	Fuzz([]byte{1, 2, 3})
+	FuzzTutorial([]byte{1, 2, 3})
 }