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