Implemented Ruby fuzzing (#3339)

This PR implements fuzzing for Ruby library using https://github.com/trailofbits/ruzzy, a coverage-guided fuzzer for pure Ruby code and Ruby C extensions based on libFuzzer. Implemented binary, compact, and JSON protocol fuzzers.

A separate PR will follow to address OOM and a crash caused by unchecked memory allocation in structs (Edit: #3340).
diff --git a/.gitignore b/.gitignore
index 4647ea4..d48519f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -261,7 +261,7 @@
 /lib/rb/ext/mkmf.log
 /lib/rb/ext/thrift_native.bundle
 /lib/rb/ext/thrift_native.so
-/lib/rb/test/
+/lib/rb/test/debug_proto/
 /lib/rb/thrift-*.gem
 /lib/php/src/ext/thrift_protocol/Makefile.*
 /lib/php/src/ext/thrift_protocol/build/
diff --git a/FUZZING.md b/FUZZING.md
index 146b015..08afed8 100644
--- a/FUZZING.md
+++ b/FUZZING.md
@@ -21,6 +21,7 @@
 - Java/JVM (and other JVM languages)
 - JavaScript
 - Python
+- Ruby
 - Rust
 - Swift
 
@@ -45,11 +46,11 @@
 ## Building and Running the Fuzzers
 
 Each language has its own fuzzers under the `lib/<language>/test/fuzz` directory.
-The fuzzers are built when building the language-specific code (using the normal build system), as regular binaries (without fuzzing support enabled), to ensure that there are no build breakages.
+Build integration varies by language. C++, c_glib, Go, Rust, and Ruby wire fuzz code into their normal build systems so that code generation and build drift are caught early. Some languages also provide local runner targets or native fuzz binaries.
 
 To ensure fuzzing can find issues as soon as possible, we will enable fuzzing support in CI once the fuzzers are stable.
 
-Currently the only convenient, formally supported build with fuzzing support enabled is the via the oss-fuzz workflow. For languages where local fuzzing is easy to do, documentation is provided along with the fuzzers.
+Currently the only convenient, formally supported build with fuzzing support enabled is via the oss-fuzz workflow. For languages where local fuzzing is practical, documentation is provided alongside the fuzzers. For example, C++ builds libFuzzer binaries directly, while Ruby exposes `make` targets that wrap Ruzzy.
 
 ## OSS-Fuzz Integration
 
@@ -75,4 +76,4 @@
 
 If you do add or change a fuzzer, please remember to make corresponding changes to the oss-fuzz build script in case they are needed.
 
-Please see CONTRIBUTING.md for general contribution guidelines.
\ No newline at end of file
+Please see CONTRIBUTING.md for general contribution guidelines.
diff --git a/configure.ac b/configure.ac
index a93f701..9e32707 100644
--- a/configure.ac
+++ b/configure.ac
@@ -805,6 +805,7 @@
   lib/dart/Makefile
   lib/py/Makefile
   lib/rb/Makefile
+  lib/rb/test/fuzz/Makefile
   lib/rs/Makefile
   lib/rs/test/Makefile
   lib/rs/test/fuzz/Makefile
diff --git a/lib/rb/Makefile.am b/lib/rb/Makefile.am
index ab5d903..fd5218f 100644
--- a/lib/rb/Makefile.am
+++ b/lib/rb/Makefile.am
@@ -19,6 +19,12 @@
 
 DESTDIR ?= /
 
+SUBDIRS = .
+
+if WITH_TESTS
+SUBDIRS += test/fuzz
+endif
+
 if HAVE_BUNDLER
 
 all-local:
diff --git a/lib/rb/Rakefile b/lib/rb/Rakefile
index 7b51145..d1f0f7c 100644
--- a/lib/rb/Rakefile
+++ b/lib/rb/Rakefile
@@ -38,7 +38,7 @@
 end
 
 desc 'Compile the .thrift files for the specs'
-task :'gen-rb' => [:'gen-rb:spec', :'gen-rb:namespaced_spec', :'gen-rb:flat_spec', :'gen-rb:benchmark', :'gen-rb:debug_proto', :'gen-rb:constants_demo']
+task :'gen-rb' => [:'gen-rb:spec', :'gen-rb:namespaced_spec', :'gen-rb:flat_spec', :'gen-rb:benchmark', :'gen-rb:debug_proto', :'gen-rb:constants_demo', :'gen-rb:fuzz']
 namespace :'gen-rb' do
   task :'spec' do
     dir = File.dirname(__FILE__) + '/spec'
@@ -72,6 +72,12 @@
     dir = File.dirname(__FILE__) + '/spec'
     sh THRIFT, '--gen', 'rb:namespaced', '--recurse', '-o', dir, "../../test/ConstantsDemo.thrift"
   end
+
+  task :'fuzz' do
+    dir = File.dirname(__FILE__) + '/test/fuzz'
+    sh "mkdir", "-p", dir
+    sh THRIFT, '--gen', 'rb', '-o', dir, "../../test/FuzzTest.thrift"
+  end
 end
 
 desc "Build the native library"
diff --git a/lib/rb/test/fuzz/.gitignore b/lib/rb/test/fuzz/.gitignore
new file mode 100644
index 0000000..d152bf7
--- /dev/null
+++ b/lib/rb/test/fuzz/.gitignore
@@ -0,0 +1,3 @@
+crash-*
+gen-rb/
+leak-*
diff --git a/lib/rb/test/fuzz/Makefile.am b/lib/rb/test/fuzz/Makefile.am
new file mode 100644
index 0000000..5a9a6ca
--- /dev/null
+++ b/lib/rb/test/fuzz/Makefile.am
@@ -0,0 +1,173 @@
+#
+# 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.
+#
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+FUZZ_RB_ROOT = $(top_srcdir)/lib/rb
+FUZZ_GEN_DIR = $(FUZZ_RB_ROOT)/test/fuzz
+FUZZ_STUBS = \
+	$(FUZZ_GEN_DIR)/gen-rb/fuzz_test_constants.rb \
+	$(FUZZ_GEN_DIR)/gen-rb/fuzz_test_types.rb
+
+FUZZ_RUBY_FILES = \
+	$(srcdir)/fuzz_tracer.rb \
+	$(srcdir)/fuzz_common.rb \
+	$(srcdir)/fuzz_parse_binary_protocol.rb \
+	$(srcdir)/fuzz_parse_binary_protocol_harness.rb \
+	$(srcdir)/fuzz_parse_binary_protocol_accelerated.rb \
+	$(srcdir)/fuzz_parse_binary_protocol_accelerated_harness.rb \
+	$(srcdir)/fuzz_parse_compact_protocol.rb \
+	$(srcdir)/fuzz_parse_compact_protocol_harness.rb \
+	$(srcdir)/fuzz_parse_json_protocol.rb \
+	$(srcdir)/fuzz_parse_json_protocol_harness.rb \
+	$(srcdir)/fuzz_roundtrip_binary_protocol.rb \
+	$(srcdir)/fuzz_roundtrip_binary_protocol_harness.rb \
+	$(srcdir)/fuzz_roundtrip_binary_protocol_accelerated.rb \
+	$(srcdir)/fuzz_roundtrip_binary_protocol_accelerated_harness.rb \
+	$(srcdir)/fuzz_roundtrip_compact_protocol.rb \
+	$(srcdir)/fuzz_roundtrip_compact_protocol_harness.rb \
+	$(srcdir)/fuzz_roundtrip_json_protocol.rb \
+	$(srcdir)/fuzz_roundtrip_json_protocol_harness.rb
+
+FUZZ_ASAN_OPTIONS ?= allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0
+FUZZ_CC ?= clang
+FUZZ_CXX ?= clang++
+FUZZ_LDSHARED ?= $(FUZZ_CC) -shared
+FUZZ_LDSHAREDXX ?= $(FUZZ_CXX) -shared
+FUZZ_CFLAGS ?= -fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g
+FUZZ_CXXFLAGS ?= -fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g
+FUZZ_MAKE ?= make
+FUZZ_MAKEFLAGS ?= --environment-overrides V=1
+
+check: fuzz-prepare
+
+fuzz-prepare: $(FUZZ_STUBS)
+	@for script in $(FUZZ_RUBY_FILES); do \
+		$(RUBY) -c $$script; \
+	done
+	cd $(FUZZ_RB_ROOT) && \
+		RUBYLIB="lib:test/fuzz/gen-rb" $(RUBY) -e 'require "thrift"; require "fuzz_test_types"; abort("Fuzz::FuzzTest is missing") unless defined?(Fuzz::FuzzTest)'
+
+$(THRIFT):
+	$(MAKE) -C $(top_builddir)/compiler/cpp all
+
+$(FUZZ_STUBS): $(top_srcdir)/test/FuzzTest.thrift $(THRIFT)
+	$(MKDIR_P) $(FUZZ_GEN_DIR)
+	$(THRIFT) --gen rb -o $(FUZZ_GEN_DIR) $(top_srcdir)/test/FuzzTest.thrift
+
+fuzz-build-ext:
+	cd $(FUZZ_RB_ROOT)/ext && \
+		env MAKE="$(FUZZ_MAKE) $(FUZZ_MAKEFLAGS)" \
+			CC="$(FUZZ_CC)" \
+			CXX="$(FUZZ_CXX)" \
+			LDSHARED="$(FUZZ_LDSHARED)" \
+			LDSHAREDXX="$(FUZZ_LDSHAREDXX)" \
+			CFLAGS="$(FUZZ_CFLAGS)" \
+			CXXFLAGS="$(FUZZ_CXXFLAGS)" \
+			$(RUBY) extconf.rb && \
+		env MAKE="$(FUZZ_MAKE) $(FUZZ_MAKEFLAGS)" \
+			CC="$(FUZZ_CC)" \
+			CXX="$(FUZZ_CXX)" \
+			LDSHARED="$(FUZZ_LDSHARED)" \
+			LDSHAREDXX="$(FUZZ_LDSHAREDXX)" \
+			CFLAGS="$(FUZZ_CFLAGS)" \
+			CXXFLAGS="$(FUZZ_CXXFLAGS)" \
+			$(FUZZ_MAKE) $(FUZZ_MAKEFLAGS) clean all
+
+fuzz-run: fuzz-prepare
+	@test -n "$(TARGET)" || { echo 'Set TARGET=<fuzz script name>'; exit 1; }
+	cd $(FUZZ_RB_ROOT) && \
+		ASAN_OPTIONS="$(FUZZ_ASAN_OPTIONS)" \
+		LD_PRELOAD=$$($(RUBY) -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
+		$(RUBY) test/fuzz/$(TARGET) $(CORPUS) $(FUZZ_ARGS)
+
+fuzz-parse-binary:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-run TARGET=fuzz_parse_binary_protocol.rb CORPUS="$(CORPUS)" FUZZ_ARGS="$(FUZZ_ARGS)"
+
+fuzz-parse-binary-accelerated: fuzz-build-ext
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-run TARGET=fuzz_parse_binary_protocol_accelerated.rb CORPUS="$(CORPUS)" FUZZ_ARGS="$(FUZZ_ARGS)"
+
+fuzz-parse-compact:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-run TARGET=fuzz_parse_compact_protocol.rb CORPUS="$(CORPUS)" FUZZ_ARGS="$(FUZZ_ARGS)"
+
+fuzz-parse-json:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-run TARGET=fuzz_parse_json_protocol.rb CORPUS="$(CORPUS)" FUZZ_ARGS="$(FUZZ_ARGS)"
+
+fuzz-roundtrip-binary:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-run TARGET=fuzz_roundtrip_binary_protocol.rb CORPUS="$(CORPUS)" FUZZ_ARGS="$(FUZZ_ARGS)"
+
+fuzz-roundtrip-binary-accelerated: fuzz-build-ext
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-run TARGET=fuzz_roundtrip_binary_protocol_accelerated.rb CORPUS="$(CORPUS)" FUZZ_ARGS="$(FUZZ_ARGS)"
+
+fuzz-roundtrip-compact:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-run TARGET=fuzz_roundtrip_compact_protocol.rb CORPUS="$(CORPUS)" FUZZ_ARGS="$(FUZZ_ARGS)"
+
+fuzz-roundtrip-json:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-run TARGET=fuzz_roundtrip_json_protocol.rb CORPUS="$(CORPUS)" FUZZ_ARGS="$(FUZZ_ARGS)"
+
+fuzz-smoke-parse-binary:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-parse-binary CORPUS="$(CORPUS)" FUZZ_ARGS="-runs=100 $(FUZZ_ARGS)"
+
+fuzz-smoke-parse-binary-accelerated:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-parse-binary-accelerated CORPUS="$(CORPUS)" FUZZ_ARGS="-runs=100 $(FUZZ_ARGS)"
+
+fuzz-smoke-parse-compact:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-parse-compact CORPUS="$(CORPUS)" FUZZ_ARGS="-runs=100 $(FUZZ_ARGS)"
+
+fuzz-smoke-parse-json:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-parse-json CORPUS="$(CORPUS)" FUZZ_ARGS="-runs=100 $(FUZZ_ARGS)"
+
+fuzz-smoke-roundtrip-binary:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-roundtrip-binary CORPUS="$(CORPUS)" FUZZ_ARGS="-runs=100 $(FUZZ_ARGS)"
+
+fuzz-smoke-roundtrip-binary-accelerated:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-roundtrip-binary-accelerated CORPUS="$(CORPUS)" FUZZ_ARGS="-runs=100 $(FUZZ_ARGS)"
+
+fuzz-smoke-roundtrip-compact:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-roundtrip-compact CORPUS="$(CORPUS)" FUZZ_ARGS="-runs=100 $(FUZZ_ARGS)"
+
+fuzz-smoke-roundtrip-json:
+	$(MAKE) $(AM_MAKEFLAGS) fuzz-roundtrip-json CORPUS="$(CORPUS)" FUZZ_ARGS="-runs=100 $(FUZZ_ARGS)"
+
+clean-local:
+	-$(RM) -r $(FUZZ_GEN_DIR)/gen-rb
+
+distdir:
+	$(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+EXTRA_DIST = \
+	.gitignore \
+	README.md \
+	fuzz_common.rb \
+	fuzz_tracer.rb \
+	fuzz_parse_binary_protocol.rb \
+	fuzz_parse_binary_protocol_harness.rb \
+	fuzz_parse_binary_protocol_accelerated.rb \
+	fuzz_parse_binary_protocol_accelerated_harness.rb \
+	fuzz_parse_compact_protocol.rb \
+	fuzz_parse_compact_protocol_harness.rb \
+	fuzz_parse_json_protocol.rb \
+	fuzz_parse_json_protocol_harness.rb \
+	fuzz_roundtrip_binary_protocol.rb \
+	fuzz_roundtrip_binary_protocol_harness.rb \
+	fuzz_roundtrip_binary_protocol_accelerated.rb \
+	fuzz_roundtrip_binary_protocol_accelerated_harness.rb \
+	fuzz_roundtrip_compact_protocol.rb \
+	fuzz_roundtrip_compact_protocol_harness.rb \
+	fuzz_roundtrip_json_protocol.rb \
+	fuzz_roundtrip_json_protocol_harness.rb
diff --git a/lib/rb/test/fuzz/README.md b/lib/rb/test/fuzz/README.md
new file mode 100644
index 0000000..8d0fde6
--- /dev/null
+++ b/lib/rb/test/fuzz/README.md
@@ -0,0 +1,149 @@
+# Ruby Fuzzing README
+
+The Ruby Thrift implementation uses [Ruzzy](https://github.com/trailofbits/ruzzy) for fuzzing. Ruzzy is a coverage-guided fuzzer for pure Ruby code and Ruby C extensions.
+
+We currently have several fuzz targets that test different aspects of the Thrift implementation:
+
+- `fuzz_parse_binary_protocol.rb` -- fuzzes deserialization of the Binary protocol
+- `fuzz_parse_binary_protocol_accelerated.rb` -- fuzzes deserialization of the accelerated Binary protocol
+- `fuzz_parse_compact_protocol.rb` -- fuzzes deserialization of the Compact protocol
+- `fuzz_parse_json_protocol.rb` -- fuzzes JSON protocol messages
+- `fuzz_roundtrip_binary_protocol.rb` -- fuzzes Binary roundtrips (deserialize, serialize, deserialize, compare)
+- `fuzz_roundtrip_binary_protocol_accelerated.rb` -- fuzzes accelerated Binary roundtrips
+- `fuzz_roundtrip_compact_protocol.rb` -- fuzzes Compact roundtrips
+- `fuzz_roundtrip_json_protocol.rb` -- fuzzes JSON message roundtrips
+
+The runnable files in this directory are tracer entrypoints. Ruzzy requires that pure Ruby fuzzing starts from a tracer script which then loads a separate harness, so do not invoke the matching `_harness.rb` files directly.
+
+The fuzzers use Ruzzy's mutation engine to generate test cases. Each target uses common testing code from `fuzz_common.rb`.
+
+For more information about Ruzzy and its options, see the [Ruzzy documentation](https://github.com/trailofbits/ruzzy).
+
+You can also use the corpus generator from the Rust implementation to generate initial Binary and Compact corpora that can be reused by the Ruby fuzzers, since those wire formats are identical between implementations.
+
+## Setup
+
+Run these commands from the repository root:
+
+```bash
+apt install -y clang-19
+
+# from https://github.com/trailofbits/ruzzy?tab=readme-ov-file#installing
+MAKE="make --environment-overrides V=1" \
+     CC="/usr/bin/clang-19" \
+     CXX="/usr/bin/clang++-19" \
+     LDSHARED="/usr/bin/clang-19 -shared" \
+     LDSHAREDXX="/usr/bin/clang++-19 -shared" \
+     gem install ruzzy
+
+# Validate the fuzz directory.
+# This generates test/fuzz/gen-rb, syntax-checks the fuzz scripts,
+# and verifies that Fuzz::FuzzTest loads correctly.
+make -C lib/rb/test/fuzz check
+```
+
+`make -C lib/rb check` now recurses into `lib/rb/test/fuzz`, so the same validation also runs as part of the normal Ruby `make check` flow.
+
+## Running Fuzzers
+
+The Makefile in this directory hides the `LD_PRELOAD` and `ASAN_OPTIONS` setup needed by Ruzzy.
+
+Pure Ruby targets only need `fuzz-prepare`, which is the same work as `check`:
+
+```bash
+make -C lib/rb/test/fuzz fuzz-parse-binary
+make -C lib/rb/test/fuzz fuzz-roundtrip-compact
+make -C lib/rb/test/fuzz fuzz-parse-json
+```
+
+Accelerated targets rebuild `thrift_native` with sanitizer flags first:
+
+```bash
+make -C lib/rb/test/fuzz fuzz-parse-binary-accelerated
+make -C lib/rb/test/fuzz fuzz-roundtrip-binary-accelerated
+```
+
+Use `CORPUS=...` to point at an input corpus. The path is resolved from `lib/rb`, which matches the old manual commands:
+
+```bash
+make -C lib/rb/test/fuzz fuzz-parse-binary \
+    CORPUS=../rs/test/fuzz/corpus/binary
+
+make -C lib/rb/test/fuzz fuzz-parse-compact \
+    CORPUS=../rs/test/fuzz/corpus/compact
+```
+
+Use `FUZZ_ARGS=...` for extra libFuzzer-style arguments. For a short local run, the Makefile also provides bounded smoke targets:
+
+```bash
+make -C lib/rb/test/fuzz fuzz-smoke-parse-binary
+make -C lib/rb/test/fuzz fuzz-smoke-parse-binary-accelerated \
+    FUZZ_CC=/usr/bin/clang-19 \
+    FUZZ_CXX=/usr/bin/clang++-19
+```
+
+If the default `clang` and `clang++` names are not correct for your system, override these variables for accelerated targets:
+
+```bash
+make -C lib/rb/test/fuzz fuzz-build-ext \
+    FUZZ_CC=/usr/bin/clang-19 \
+    FUZZ_CXX=/usr/bin/clang++-19
+```
+
+The underlying manual command from `lib/rb` is still:
+
+```bash
+# Memory allocation failures are common and low impact (DoS), so skip them for now.
+# Like Python, the Ruby interpreter leaks data, so ignore these for now.
+# Ruby recommends disabling sigaltstack.
+export ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0"
+
+LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
+    ruby test/fuzz/fuzz_parse_binary_protocol.rb
+```
+
+## Rust Corpus Generator
+
+We can use the Rust corpus generator to prepare corpora for the Binary and Compact protocol targets. Run it from the repository root:
+
+```bash
+cargo run --manifest-path lib/rs/test/fuzz/Cargo.toml --bin corpus_generator -- \
+    --output-dir <output_dir> \
+    --protocol <binary|compact> \
+    --generate <num_files> \
+    --buffer-size <buffer_size> \
+    --random-size <random_size>
+```
+
+Reasonable values (determined empirically):
+
+```bash
+cargo run --manifest-path lib/rs/test/fuzz/Cargo.toml --bin corpus_generator -- \
+    --output-dir ./lib/rs/test/fuzz/corpus/binary \
+    --protocol binary \
+    --generate 1000 \
+    --buffer-size 65536 \
+    --random-size 16384
+
+cargo run --manifest-path lib/rs/test/fuzz/Cargo.toml --bin corpus_generator -- \
+    --output-dir ./lib/rs/test/fuzz/corpus/compact \
+    --protocol compact \
+    --generate 1000 \
+    --buffer-size 16384 \
+    --random-size 16384
+```
+
+Then run a matching Ruby target:
+
+```bash
+make -C lib/rb/test/fuzz fuzz-parse-binary \
+    CORPUS=../rs/test/fuzz/corpus/binary
+```
+
+The Rust corpus generator does not emit JSON protocol inputs, so use it only with the Binary and Compact Ruby targets.
+
+## Troubleshooting
+
+If libFuzzer prints `WARNING: no interesting inputs were found so far. Is the code instrumented for coverage?` and quickly shrinks the seed corpus to `corp: 1/1b`, coverage tracing is not active. That usually means the tracer script was bypassed or `ruzzy` was not installed with the expected sanitizer-enabled toolchain. Run the `.rb` files in this directory, not the `_harness.rb` files.
+
+If an accelerated target starts using the pure Ruby implementation, rebuild the extension with `make -C lib/rb/test/fuzz fuzz-build-ext ...` and then rerun the accelerated target.
diff --git a/lib/rb/test/fuzz/fuzz_common.rb b/lib/rb/test/fuzz/fuzz_common.rb
new file mode 100644
index 0000000..ed28e7c
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_common.rb
@@ -0,0 +1,95 @@
+#
+# 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.
+#
+
+$:.unshift File.expand_path('../../lib', __dir__)
+$:.unshift File.expand_path('../../ext', __dir__)
+require 'thrift'
+$:.unshift File.dirname(__FILE__) + "/gen-rb"
+require 'fuzz_test_constants'
+
+require 'coverage'
+Coverage.start(branches: true) unless Coverage.respond_to?(:running?) && Coverage.running?
+require 'ruzzy'
+# Ruzzy.enable_branch_coverage_hooks
+
+def ignorable_fuzz_exception?(error)
+  return true if error.is_a?(Thrift::ProtocolException) ||
+    error.is_a?(EOFError) ||
+    error.is_a?(Encoding::UndefinedConversionError)
+
+  [
+    /don't know what (?:c)?type/,
+    /Too many fields for union/,
+    /too big to convert to '(?:int|long)'/,
+    /bignum too big to convert into 'long'/,
+    /negative array size/,
+    /Union fields are not set/
+  ].any? { |pattern| error.message =~ pattern }
+end
+
+def read_fuzz_test(protocol, read_message_begin)
+  protocol.read_message_begin if read_message_begin
+  obj = Fuzz::FuzzTest.new
+  obj.read(protocol)
+  protocol.read_message_end if read_message_begin
+  obj
+end
+
+def write_fuzz_test(protocol, obj, write_message_begin)
+  if write_message_begin
+    protocol.write_message_begin('fuzz', Thrift::MessageTypes::CALL, 0)
+  end
+  obj.write(protocol)
+  protocol.write_message_end if write_message_begin
+end
+
+def create_parser_fuzzer(protocol_factory_class, read_message_begin: false)
+  lambda do |data|
+    transport = Thrift::MemoryBufferTransport.new(data)
+    protocol = protocol_factory_class.new.get_protocol(transport)
+    read_fuzz_test(protocol, read_message_begin)
+    0
+  rescue StandardError => e
+    # We're looking for memory corruption, not Ruby exceptions
+    raise unless ignorable_fuzz_exception?(e)
+  end
+end
+
+def create_roundtrip_fuzzer(protocol_factory_class, read_message_begin: false)
+  lambda do |data|
+    transport = Thrift::MemoryBufferTransport.new(data)
+    protocol = protocol_factory_class.new.get_protocol(transport)
+    obj = read_fuzz_test(protocol, read_message_begin)
+
+    serialized_data = +""
+    transport2 = Thrift::MemoryBufferTransport.new(serialized_data)
+    protocol2 = protocol_factory_class.new.get_protocol(transport2)
+    write_fuzz_test(protocol2, obj, read_message_begin)
+
+    transport3 = Thrift::MemoryBufferTransport.new(serialized_data)
+    protocol3 = protocol_factory_class.new.get_protocol(transport3)
+    deserialized_obj = read_fuzz_test(protocol3, read_message_begin)
+
+    raise "Roundtrip mismatch" unless obj == deserialized_obj
+    0
+  rescue StandardError => e
+    # We're looking for memory corruption, not Ruby exceptions
+    raise unless ignorable_fuzz_exception?(e)
+  end
+end
diff --git a/lib/rb/test/fuzz/fuzz_parse_binary_protocol.rb b/lib/rb/test/fuzz/fuzz_parse_binary_protocol.rb
new file mode 100644
index 0000000..2a17aca
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_parse_binary_protocol.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_tracer'
+
+trace_fuzz_target(__FILE__)
diff --git a/lib/rb/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb b/lib/rb/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb
new file mode 100644
index 0000000..2a17aca
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_tracer'
+
+trace_fuzz_target(__FILE__)
diff --git a/lib/rb/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb b/lib/rb/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb
new file mode 100644
index 0000000..28c0bff
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_common'
+
+Ruzzy.fuzz(create_parser_fuzzer(Thrift::BinaryProtocolAcceleratedFactory))
diff --git a/lib/rb/test/fuzz/fuzz_parse_binary_protocol_harness.rb b/lib/rb/test/fuzz/fuzz_parse_binary_protocol_harness.rb
new file mode 100644
index 0000000..c7fb658
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_parse_binary_protocol_harness.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_common'
+
+Ruzzy.fuzz(create_parser_fuzzer(Thrift::BinaryProtocolFactory))
diff --git a/lib/rb/test/fuzz/fuzz_parse_compact_protocol.rb b/lib/rb/test/fuzz/fuzz_parse_compact_protocol.rb
new file mode 100644
index 0000000..2a17aca
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_parse_compact_protocol.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_tracer'
+
+trace_fuzz_target(__FILE__)
diff --git a/lib/rb/test/fuzz/fuzz_parse_compact_protocol_harness.rb b/lib/rb/test/fuzz/fuzz_parse_compact_protocol_harness.rb
new file mode 100644
index 0000000..0ded62c
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_parse_compact_protocol_harness.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_common'
+
+Ruzzy.fuzz(create_parser_fuzzer(Thrift::CompactProtocolFactory))
diff --git a/lib/rb/test/fuzz/fuzz_parse_json_protocol.rb b/lib/rb/test/fuzz/fuzz_parse_json_protocol.rb
new file mode 100644
index 0000000..2a17aca
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_parse_json_protocol.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_tracer'
+
+trace_fuzz_target(__FILE__)
diff --git a/lib/rb/test/fuzz/fuzz_parse_json_protocol_harness.rb b/lib/rb/test/fuzz/fuzz_parse_json_protocol_harness.rb
new file mode 100644
index 0000000..3c25be4
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_parse_json_protocol_harness.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_common'
+
+Ruzzy.fuzz(create_parser_fuzzer(Thrift::JsonProtocolFactory, read_message_begin: true))
diff --git a/lib/rb/test/fuzz/fuzz_roundtrip_binary_protocol.rb b/lib/rb/test/fuzz/fuzz_roundtrip_binary_protocol.rb
new file mode 100644
index 0000000..2a17aca
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_roundtrip_binary_protocol.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_tracer'
+
+trace_fuzz_target(__FILE__)
diff --git a/lib/rb/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb b/lib/rb/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb
new file mode 100644
index 0000000..2a17aca
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_tracer'
+
+trace_fuzz_target(__FILE__)
diff --git a/lib/rb/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb b/lib/rb/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb
new file mode 100644
index 0000000..26886b9
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_common'
+
+Ruzzy.fuzz(create_roundtrip_fuzzer(Thrift::BinaryProtocolAcceleratedFactory))
diff --git a/lib/rb/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb b/lib/rb/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb
new file mode 100644
index 0000000..4bf0936
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_common'
+
+Ruzzy.fuzz(create_roundtrip_fuzzer(Thrift::BinaryProtocolFactory))
diff --git a/lib/rb/test/fuzz/fuzz_roundtrip_compact_protocol.rb b/lib/rb/test/fuzz/fuzz_roundtrip_compact_protocol.rb
new file mode 100644
index 0000000..2a17aca
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_roundtrip_compact_protocol.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_tracer'
+
+trace_fuzz_target(__FILE__)
diff --git a/lib/rb/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb b/lib/rb/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb
new file mode 100644
index 0000000..fa9c830
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_common'
+
+Ruzzy.fuzz(create_roundtrip_fuzzer(Thrift::CompactProtocolFactory))
diff --git a/lib/rb/test/fuzz/fuzz_roundtrip_json_protocol.rb b/lib/rb/test/fuzz/fuzz_roundtrip_json_protocol.rb
new file mode 100644
index 0000000..2a17aca
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_roundtrip_json_protocol.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_tracer'
+
+trace_fuzz_target(__FILE__)
diff --git a/lib/rb/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb b/lib/rb/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb
new file mode 100644
index 0000000..501c265
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+require_relative 'fuzz_common'
+
+Ruzzy.fuzz(create_roundtrip_fuzzer(Thrift::JsonProtocolFactory, read_message_begin: true))
diff --git a/lib/rb/test/fuzz/fuzz_tracer.rb b/lib/rb/test/fuzz/fuzz_tracer.rb
new file mode 100644
index 0000000..a903650
--- /dev/null
+++ b/lib/rb/test/fuzz/fuzz_tracer.rb
@@ -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.
+#
+
+require 'ruzzy'
+
+def trace_fuzz_target(script_path)
+  harness_path = File.expand_path(
+    "#{File.basename(script_path, '.rb')}_harness.rb",
+    File.dirname(script_path)
+  )
+  Ruzzy.trace(harness_path)
+end
diff --git a/test/FuzzTest.thrift b/test/FuzzTest.thrift
index 7a6da33..af26b65 100644
--- a/test/FuzzTest.thrift
+++ b/test/FuzzTest.thrift
@@ -20,6 +20,7 @@
 namespace cpp fuzz
 namespace java org.apache.thrift.fuzz
 namespace py fuzz
+namespace rb Fuzz
 namespace swift Fuzz
 
 // Test typedefs