THRIFT-5855: cpp fuzzers

Add fuzzers for cpp support, to improve the reliability/robustness of the implementation
diff --git a/FUZZING.md b/FUZZING.md
index b3e0c15..0a48246 100644
--- a/FUZZING.md
+++ b/FUZZING.md
@@ -17,10 +17,10 @@
 
 - Go (needs improvement)
 - c_glib (partially supported, needs round-trip support)
+- C++
 
 We are working on adding fuzzers for the following languages:
 
-- C++
 - Rust  
 - Swift
 - Python
diff --git a/configure.ac b/configure.ac
index 8147a0b..4f55aad 100644
--- a/configure.ac
+++ b/configure.ac
@@ -773,6 +773,7 @@
   lib/cl/Makefile
   lib/cpp/Makefile
   lib/cpp/test/Makefile
+  lib/cpp/test/fuzz/Makefile
   lib/cpp/thrift-nb.pc
   lib/cpp/thrift-z.pc
   lib/cpp/thrift-qt5.pc
diff --git a/lib/cpp/test/Makefile.am b/lib/cpp/test/Makefile.am
index b4f6055..7c4153b 100644
--- a/lib/cpp/test/Makefile.am
+++ b/lib/cpp/test/Makefile.am
@@ -18,6 +18,9 @@
 #
 AUTOMAKE_OPTIONS = subdir-objects serial-tests nostdinc
 
+SUBDIRS = .
+SUBDIRS += fuzz
+
 BUILT_SOURCES = gen-cpp/AnnotationTest_types.h \
                 gen-cpp/DebugProtoTest_types.h \
                 gen-cpp/EnumTest_types.h \
diff --git a/lib/cpp/test/fuzz/CMakeLists.txt b/lib/cpp/test/fuzz/CMakeLists.txt
new file mode 100644
index 0000000..0251270
--- /dev/null
+++ b/lib/cpp/test/fuzz/CMakeLists.txt
@@ -0,0 +1,138 @@
+# Fuzz testing configuration
+
+# Generate FuzzTest code
+add_custom_command(OUTPUT gen-cpp/FuzzTest_types.cpp
+                         gen-cpp/FuzzTest_types.h
+    COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/FuzzTest.thrift
+)
+
+# Create a library for the generated code
+add_library(fuzztest_gen
+  gen-cpp/FuzzTest_types.cpp
+=)
+target_link_libraries(fuzztest_gen thrift)
+
+# Common fuzzing header
+set(FUZZ_COMMON_HEADERS
+  FuzzCommon.tcc
+)
+
+# Add fuzzing flags when using Clang
+if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+  set(FUZZING_FLAGS "-fsanitize=fuzzer,address -g")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FUZZING_FLAGS}")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FUZZING_FLAGS}")
+endif()
+
+# Compact protocol fuzzer
+add_executable(FuzzParseCompact
+  FuzzParseCompact.cpp
+  ${FUZZ_COMMON_HEADERS}
+)
+
+target_link_libraries(FuzzParseCompact
+  thrift
+  fuzztest_gen
+)
+
+# Compact protocol roundtrip fuzzer
+add_executable(FuzzRoundtripCompact
+  FuzzRoundtripCompact.cpp
+)
+
+target_link_libraries(FuzzRoundtripCompact
+  thrift
+  fuzztest_gen
+)
+
+# Binary protocol fuzzer
+add_executable(FuzzParseBinary
+  FuzzParseBinary.cpp
+  ${FUZZ_COMMON_HEADERS}
+)
+
+target_link_libraries(FuzzParseBinary
+  thrift
+  fuzztest_gen
+)
+
+# Binary protocol roundtrip fuzzer
+add_executable(FuzzRoundtripBinary
+  FuzzRoundtripBinary.cpp
+)
+
+target_link_libraries(FuzzRoundtripBinary
+  thrift
+  fuzztest_gen
+)
+
+# JSON protocol fuzzer
+add_executable(FuzzParseJson
+  FuzzParseJson.cpp
+  ${FUZZ_COMMON_HEADERS}
+)
+
+target_link_libraries(FuzzParseJson
+  thrift
+  fuzztest_gen
+)
+
+# JSON protocol roundtrip fuzzer
+add_executable(FuzzRoundtripJson
+  FuzzRoundtripJson.cpp
+  ${FUZZ_COMMON_HEADERS}
+)
+
+target_link_libraries(FuzzRoundtripJson
+  thrift
+  fuzztest_gen
+)
+
+# Create directories for fuzzing corpus
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/corpus/compact_protocol)
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/corpus/binary_protocol)
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/corpus/compact_protocol_roundtrip)
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/corpus/binary_protocol_roundtrip)
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/corpus/json_protocol)
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/corpus/json_protocol_roundtrip)
+
+# Add test targets that run the fuzzers briefly
+if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+  add_test(NAME FuzzParseCompact_run
+           COMMAND FuzzParseCompact -runs=100 ${CMAKE_CURRENT_BINARY_DIR}/corpus/compact_protocol
+  )
+  add_test(NAME FuzzRoundtripCompact_run
+           COMMAND FuzzRoundtripCompact -runs=100 ${CMAKE_CURRENT_BINARY_DIR}/corpus/compact_protocol_roundtrip
+  )
+  add_test(NAME FuzzParseBinary_run
+           COMMAND FuzzParseBinary -runs=100 ${CMAKE_CURRENT_BINARY_DIR}/corpus/binary_protocol
+  )
+  add_test(NAME FuzzRoundtripBinary_run
+           COMMAND FuzzRoundtripBinary -runs=100 ${CMAKE_CURRENT_BINARY_DIR}/corpus/binary_protocol_roundtrip
+  )
+  add_test(NAME FuzzParseJson_run
+           COMMAND FuzzParseJson -runs=100 ${CMAKE_CURRENT_BINARY_DIR}/corpus/json_protocol
+  )
+  add_test(NAME FuzzRoundtripJson_run
+           COMMAND FuzzRoundtripJson -runs=100 ${CMAKE_CURRENT_BINARY_DIR}/corpus/json_protocol_roundtrip
+  )
+else()
+  add_test(NAME FuzzParseCompact_run
+           COMMAND FuzzParseCompact ${CMAKE_CURRENT_BINARY_DIR}/corpus/compact_protocol
+  )
+  add_test(NAME FuzzRoundtripCompact_run
+           COMMAND FuzzRoundtripCompact ${CMAKE_CURRENT_BINARY_DIR}/corpus/compact_protocol_roundtrip
+  )
+  add_test(NAME FuzzParseBinary_run
+           COMMAND FuzzParseBinary ${CMAKE_CURRENT_BINARY_DIR}/corpus/binary_protocol
+  )
+  add_test(NAME FuzzRoundtripBinary_run
+           COMMAND FuzzRoundtripBinary ${CMAKE_CURRENT_BINARY_DIR}/corpus/binary_protocol_roundtrip
+  )
+  add_test(NAME FuzzParseJson_run
+           COMMAND FuzzParseJson ${CMAKE_CURRENT_BINARY_DIR}/corpus/json_protocol
+  )
+  add_test(NAME FuzzRoundtripJson_run
+           COMMAND FuzzRoundtripJson ${CMAKE_CURRENT_BINARY_DIR}/corpus/json_protocol_roundtrip
+  )
+endif() 
\ No newline at end of file
diff --git a/lib/cpp/test/fuzz/FuzzCommon.tcc b/lib/cpp/test/fuzz/FuzzCommon.tcc
new file mode 100644
index 0000000..da936e4
--- /dev/null
+++ b/lib/cpp/test/fuzz/FuzzCommon.tcc
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef THRIFT_TEST_FUZZ_COMMON_TCC_
+#define THRIFT_TEST_FUZZ_COMMON_TCC_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <memory>
+#include <cmath>
+#include <iostream>
+
+#include <thrift/protocol/TDebugProtocol.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/TToString.h>
+#include <thrift/TConfiguration.h>
+
+#include "gen-cpp/FuzzTest_types.h"
+
+namespace apache { namespace thrift { namespace fuzzer {
+
+using namespace apache::thrift::transport;
+using namespace apache::thrift::protocol;
+using namespace fuzz;
+
+// 10MB message size limit to prevent over-allocation during fuzzing
+const int FUZZ_MAX_MESSAGE_SIZE = 10 * 1024 * 1024;
+
+inline bool is_nan_false_positive(FuzzTest& test1, FuzzTest& test2) {
+  BasicTypes& b1 = test1.basic;
+  BasicTypes& b2 = test2.basic;
+  if (std::isnan(b1.double_field) && std::isnan(b2.double_field)) {
+    b1.double_field = 0.0;
+    b2.double_field = 0.0;
+  }
+
+  // Check for NaN in containers if they contain doubles
+  // This is a simplified version - may need adjustment based on actual schema
+  
+  return test1 == test2;
+}
+
+// Simple parse-only fuzzer
+template<typename ProtocolType>
+int fuzz_parse(const uint8_t* data, size_t size) {
+  try {
+    std::shared_ptr<TConfiguration> config(new TConfiguration(FUZZ_MAX_MESSAGE_SIZE));
+    std::shared_ptr<TMemoryBuffer> trans(new TMemoryBuffer(const_cast<uint8_t*>(data), size, TMemoryBuffer::OBSERVE, config));
+    std::shared_ptr<TProtocol> proto(new ProtocolType(trans));
+
+    FuzzTest test;
+    test.read(proto.get());
+  } catch (const TException&) {
+    // Ignore any Thrift exceptions - they're expected when fuzzing
+  }
+  return 0;
+}
+
+// Roundtrip fuzzer that verifies serialization/deserialization
+template<typename ProtocolType>
+int fuzz_roundtrip(const uint8_t* data, size_t size) {
+  try {
+    std::shared_ptr<TConfiguration> config(new TConfiguration(FUZZ_MAX_MESSAGE_SIZE));
+    
+    // First parse
+    std::shared_ptr<TMemoryBuffer> trans(new TMemoryBuffer(const_cast<uint8_t*>(data), size, TMemoryBuffer::OBSERVE, config));
+    std::shared_ptr<TProtocol> proto(new ProtocolType(trans));
+
+    FuzzTest test1;
+    test1.read(proto.get());
+
+    // Serialize back
+    std::shared_ptr<TMemoryBuffer> outTrans(new TMemoryBuffer(config));
+    std::shared_ptr<TProtocol> outProto(new ProtocolType(outTrans));
+    test1.write(outProto.get());
+
+    // Get serialized data
+    std::string serialized = outTrans->getBufferAsString();
+
+    // Deserialize again
+    std::shared_ptr<TMemoryBuffer> reTrans(new TMemoryBuffer(config));
+    reTrans->write((const uint8_t*)serialized.data(), static_cast<uint32_t>(serialized.size()));
+    std::shared_ptr<TProtocol> reProto(new ProtocolType(reTrans));
+
+    FuzzTest test2;
+    test2.read(reProto.get());
+
+    // Verify equality
+    if (!(test1 == test2) && !is_nan_false_positive(test1, test2)) {
+      const std::string str1(apache::thrift::ThriftDebugString(test1));
+      const std::string str2(apache::thrift::ThriftDebugString(test2));
+
+      std::cout << "Expected:\n" << str1 << "\nGotten:\n" << str2 << std::endl;
+
+      throw std::runtime_error("Roundtrip failed");
+    }
+  } catch (const TException&) {
+    // Ignore any Thrift exceptions - they're expected when fuzzing
+  }
+  return 0;
+}
+
+}}} // apache::thrift::fuzzer
+
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+__attribute__((weak)) int main(int argc, char** argv) {
+  return 0;
+}
+#endif 
+
+#endif // THRIFT_TEST_FUZZ_COMMON_TCC_ 
\ No newline at end of file
diff --git a/lib/cpp/test/fuzz/FuzzParseBinary.cpp b/lib/cpp/test/fuzz/FuzzParseBinary.cpp
new file mode 100644
index 0000000..b135d9a
--- /dev/null
+++ b/lib/cpp/test/fuzz/FuzzParseBinary.cpp
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <thrift/protocol/TBinaryProtocol.h>
+#include "FuzzCommon.tcc"
+
+using namespace apache::thrift::protocol;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return apache::thrift::fuzzer::fuzz_parse<TBinaryProtocol>(data, size);
+}
\ No newline at end of file
diff --git a/lib/cpp/test/fuzz/FuzzParseCompact.cpp b/lib/cpp/test/fuzz/FuzzParseCompact.cpp
new file mode 100644
index 0000000..01e47a0
--- /dev/null
+++ b/lib/cpp/test/fuzz/FuzzParseCompact.cpp
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+
+#include <thrift/protocol/TCompactProtocol.h>
+#include "FuzzCommon.tcc"
+
+using namespace apache::thrift::protocol;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return apache::thrift::fuzzer::fuzz_parse<TCompactProtocol>(data, size);
+}
\ No newline at end of file
diff --git a/lib/cpp/test/fuzz/FuzzParseJson.cpp b/lib/cpp/test/fuzz/FuzzParseJson.cpp
new file mode 100644
index 0000000..a9bf408
--- /dev/null
+++ b/lib/cpp/test/fuzz/FuzzParseJson.cpp
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+
+#include <thrift/protocol/TJSONProtocol.h>
+#include "FuzzCommon.tcc"
+
+using namespace apache::thrift::protocol;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return apache::thrift::fuzzer::fuzz_parse<TJSONProtocol>(data, size);
+}
\ No newline at end of file
diff --git a/lib/cpp/test/fuzz/FuzzRoundtripBinary.cpp b/lib/cpp/test/fuzz/FuzzRoundtripBinary.cpp
new file mode 100644
index 0000000..999bb09
--- /dev/null
+++ b/lib/cpp/test/fuzz/FuzzRoundtripBinary.cpp
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <thrift/protocol/TBinaryProtocol.h>
+#include "FuzzCommon.tcc"
+
+using namespace apache::thrift::protocol;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return apache::thrift::fuzzer::fuzz_roundtrip<TBinaryProtocol>(data, size);
+}
\ No newline at end of file
diff --git a/lib/cpp/test/fuzz/FuzzRoundtripCompact.cpp b/lib/cpp/test/fuzz/FuzzRoundtripCompact.cpp
new file mode 100644
index 0000000..0e07fa9
--- /dev/null
+++ b/lib/cpp/test/fuzz/FuzzRoundtripCompact.cpp
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <thrift/protocol/TCompactProtocol.h>
+#include "FuzzCommon.tcc"
+
+using namespace apache::thrift::protocol;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return apache::thrift::fuzzer::fuzz_roundtrip<TCompactProtocol>(data, size);
+}
\ No newline at end of file
diff --git a/lib/cpp/test/fuzz/FuzzRoundtripJson.cpp b/lib/cpp/test/fuzz/FuzzRoundtripJson.cpp
new file mode 100644
index 0000000..2a11547
--- /dev/null
+++ b/lib/cpp/test/fuzz/FuzzRoundtripJson.cpp
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <thrift/protocol/TJSONProtocol.h>
+#include "FuzzCommon.tcc"
+
+using namespace apache::thrift::protocol;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return apache::thrift::fuzzer::fuzz_roundtrip<TJSONProtocol>(data, size);
+}
\ No newline at end of file
diff --git a/lib/cpp/test/fuzz/Makefile.am b/lib/cpp/test/fuzz/Makefile.am
new file mode 100644
index 0000000..b664573
--- /dev/null
+++ b/lib/cpp/test/fuzz/Makefile.am
@@ -0,0 +1,83 @@
+# 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
+
+AUTOMAKE_OPTIONS = subdir-objects serial-tests nostdinc
+
+# Generate FuzzTest code
+BUILT_SOURCES = gen-cpp/FuzzTest_types.h
+
+noinst_LTLIBRARIES = libfuzztest_gen.la
+
+nodist_libfuzztest_gen_la_SOURCES = \
+    gen-cpp/FuzzTest_types.cpp \
+    gen-cpp/FuzzTest_types.h 
+
+libfuzztest_gen_la_LIBADD = $(top_builddir)/lib/cpp/libthrift.la
+
+# Common fuzzing headers
+noinst_HEADERS = FuzzCommon.tcc
+
+# Fuzzing executables
+AM_CPPFLAGS = -I$(top_srcdir)/lib/cpp/src -I$(top_srcdir)/lib/cpp/src/thrift -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I.
+
+check_PROGRAMS = FuzzParseCompact FuzzParseBinary FuzzRoundtripCompact FuzzRoundtripBinary FuzzParseJson FuzzRoundtripJson
+
+FuzzParseCompact_SOURCES = FuzzParseCompact.cpp FuzzCommon.tcc
+FuzzParseCompact_LDADD = libfuzztest_gen.la $(top_builddir)/lib/cpp/libthrift.la
+FuzzParseCompact_CXXFLAGS = $(AM_CXXFLAGS) $(AM_CPPFLAGS) -g
+if USING_CLANG
+FuzzParseCompact_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
+endif
+
+FuzzRoundtripCompact_SOURCES = FuzzRoundtripCompact.cpp FuzzCommon.tcc
+FuzzRoundtripCompact_LDADD = libfuzztest_gen.la $(top_builddir)/lib/cpp/libthrift.la
+FuzzRoundtripCompact_CXXFLAGS = $(AM_CXXFLAGS) $(AM_CPPFLAGS) -g
+if USING_CLANG
+FuzzRoundtripCompact_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
+endif
+
+FuzzParseBinary_SOURCES = FuzzParseBinary.cpp FuzzCommon.tcc
+FuzzParseBinary_LDADD = libfuzztest_gen.la $(top_builddir)/lib/cpp/libthrift.la
+FuzzParseBinary_CXXFLAGS = $(AM_CXXFLAGS) $(AM_CPPFLAGS) -g
+if USING_CLANG
+FuzzParseBinary_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
+endif
+
+FuzzRoundtripBinary_SOURCES = FuzzRoundtripBinary.cpp FuzzCommon.tcc
+FuzzRoundtripBinary_LDADD = libfuzztest_gen.la $(top_builddir)/lib/cpp/libthrift.la
+FuzzRoundtripBinary_CXXFLAGS = $(AM_CXXFLAGS) $(AM_CPPFLAGS) -g
+if USING_CLANG
+FuzzRoundtripBinary_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
+endif
+
+FuzzParseJson_SOURCES = FuzzParseJson.cpp FuzzCommon.tcc
+FuzzParseJson_LDADD = libfuzztest_gen.la $(top_builddir)/lib/cpp/libthrift.la
+FuzzParseJson_CXXFLAGS = $(AM_CXXFLAGS) $(AM_CPPFLAGS) -g
+if USING_CLANG
+FuzzParseJson_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
+endif
+
+FuzzRoundtripJson_SOURCES = FuzzRoundtripJson.cpp FuzzCommon.tcc
+FuzzRoundtripJson_LDADD = libfuzztest_gen.la $(top_builddir)/lib/cpp/libthrift.la
+FuzzRoundtripJson_CXXFLAGS = $(AM_CXXFLAGS) $(AM_CPPFLAGS) -g
+if USING_CLANG
+FuzzRoundtripJson_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
+endif
+
+# Generate thrift files
+gen-cpp/FuzzTest_types.cpp gen-cpp/FuzzTest_types.h: $(top_srcdir)/test/FuzzTest.thrift
+	$(THRIFT) --gen cpp $<
+
+# Clean target
+clean-local:
+	$(RM) -rf gen-cpp
+	$(RM) -f $(check_PROGRAMS)
+	$(RM) -f *.o *.lo *.la
+	$(RM) -rf .libs
+
+CLEANFILES = gen-cpp/*
+
+EXTRA_DIST = CMakeLists.txt FuzzParseCompact.cpp FuzzParseBinary.cpp FuzzRoundtripCompact.cpp FuzzRoundtripBinary.cpp FuzzParseJson.cpp FuzzRoundtripJson.cpp 
\ No newline at end of file
diff --git a/lib/cpp/test/fuzz/README.md b/lib/cpp/test/fuzz/README.md
new file mode 100644
index 0000000..8cde029
--- /dev/null
+++ b/lib/cpp/test/fuzz/README.md
@@ -0,0 +1,23 @@
+# C++ Fuzzing README
+
+To build the fuzz targets, run `make check` in this directory. The build system uses LLVM's libFuzzer for fuzzing the C++ Thrift implementation.
+
+These are standard libFuzzer targets, so you can run them using the standard libFuzzer interface. After building, you can run a fuzzer using:
+```bash
+./<fuzzer_name>
+```
+
+We currently have six fuzz targets:
+
+* FuzzParseBinary -- fuzzes the deserialization of the Binary protocol
+* FuzzParseCompact -- fuzzes the deserialization of the Compact protocol
+* FuzzParseJson -- fuzzes the deserialization of the JSON protocol
+* FuzzRoundtripBinary -- fuzzes the roundtrip of the Binary protocol (i.e. serializes then deserializes and compares the result)
+* FuzzRoundtripCompact -- fuzzes the roundtrip of the Compact protocol
+* FuzzRoundtripJson -- fuzzes the roundtrip of the JSON protocol
+
+The fuzzers use libFuzzer's built-in mutation engine to generate test cases. Each fuzzer implements the standard `LLVMFuzzerTestOneInput` interface and uses common testing code from `FuzzCommon.tcc`.
+
+For more information about libFuzzer and its options, see the [libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html). 
+
+You can also use the corpus generator from the Rust implementation to generate initial corpus files that can be used with these C++ fuzzers, since the wire formats are identical between implementations.
\ No newline at end of file