blob: 8d0fde6ae7737728d7d8a2d568752cafead793e2 [file] [log] [blame] [view]
Dmytro Shteflyuk60417982026-03-14 02:12:29 -04001# Ruby Fuzzing README
2
3The 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.
4
5We currently have several fuzz targets that test different aspects of the Thrift implementation:
6
7- `fuzz_parse_binary_protocol.rb` -- fuzzes deserialization of the Binary protocol
8- `fuzz_parse_binary_protocol_accelerated.rb` -- fuzzes deserialization of the accelerated Binary protocol
9- `fuzz_parse_compact_protocol.rb` -- fuzzes deserialization of the Compact protocol
10- `fuzz_parse_json_protocol.rb` -- fuzzes JSON protocol messages
11- `fuzz_roundtrip_binary_protocol.rb` -- fuzzes Binary roundtrips (deserialize, serialize, deserialize, compare)
12- `fuzz_roundtrip_binary_protocol_accelerated.rb` -- fuzzes accelerated Binary roundtrips
13- `fuzz_roundtrip_compact_protocol.rb` -- fuzzes Compact roundtrips
14- `fuzz_roundtrip_json_protocol.rb` -- fuzzes JSON message roundtrips
15
16The 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.
17
18The fuzzers use Ruzzy's mutation engine to generate test cases. Each target uses common testing code from `fuzz_common.rb`.
19
20For more information about Ruzzy and its options, see the [Ruzzy documentation](https://github.com/trailofbits/ruzzy).
21
22You 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.
23
24## Setup
25
26Run these commands from the repository root:
27
28```bash
29apt install -y clang-19
30
31# from https://github.com/trailofbits/ruzzy?tab=readme-ov-file#installing
32MAKE="make --environment-overrides V=1" \
33 CC="/usr/bin/clang-19" \
34 CXX="/usr/bin/clang++-19" \
35 LDSHARED="/usr/bin/clang-19 -shared" \
36 LDSHAREDXX="/usr/bin/clang++-19 -shared" \
37 gem install ruzzy
38
39# Validate the fuzz directory.
40# This generates test/fuzz/gen-rb, syntax-checks the fuzz scripts,
41# and verifies that Fuzz::FuzzTest loads correctly.
42make -C lib/rb/test/fuzz check
43```
44
45`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.
46
47## Running Fuzzers
48
49The Makefile in this directory hides the `LD_PRELOAD` and `ASAN_OPTIONS` setup needed by Ruzzy.
50
51Pure Ruby targets only need `fuzz-prepare`, which is the same work as `check`:
52
53```bash
54make -C lib/rb/test/fuzz fuzz-parse-binary
55make -C lib/rb/test/fuzz fuzz-roundtrip-compact
56make -C lib/rb/test/fuzz fuzz-parse-json
57```
58
59Accelerated targets rebuild `thrift_native` with sanitizer flags first:
60
61```bash
62make -C lib/rb/test/fuzz fuzz-parse-binary-accelerated
63make -C lib/rb/test/fuzz fuzz-roundtrip-binary-accelerated
64```
65
66Use `CORPUS=...` to point at an input corpus. The path is resolved from `lib/rb`, which matches the old manual commands:
67
68```bash
69make -C lib/rb/test/fuzz fuzz-parse-binary \
70 CORPUS=../rs/test/fuzz/corpus/binary
71
72make -C lib/rb/test/fuzz fuzz-parse-compact \
73 CORPUS=../rs/test/fuzz/corpus/compact
74```
75
76Use `FUZZ_ARGS=...` for extra libFuzzer-style arguments. For a short local run, the Makefile also provides bounded smoke targets:
77
78```bash
79make -C lib/rb/test/fuzz fuzz-smoke-parse-binary
80make -C lib/rb/test/fuzz fuzz-smoke-parse-binary-accelerated \
81 FUZZ_CC=/usr/bin/clang-19 \
82 FUZZ_CXX=/usr/bin/clang++-19
83```
84
85If the default `clang` and `clang++` names are not correct for your system, override these variables for accelerated targets:
86
87```bash
88make -C lib/rb/test/fuzz fuzz-build-ext \
89 FUZZ_CC=/usr/bin/clang-19 \
90 FUZZ_CXX=/usr/bin/clang++-19
91```
92
93The underlying manual command from `lib/rb` is still:
94
95```bash
96# Memory allocation failures are common and low impact (DoS), so skip them for now.
97# Like Python, the Ruby interpreter leaks data, so ignore these for now.
98# Ruby recommends disabling sigaltstack.
99export ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0"
100
101LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
102 ruby test/fuzz/fuzz_parse_binary_protocol.rb
103```
104
105## Rust Corpus Generator
106
107We can use the Rust corpus generator to prepare corpora for the Binary and Compact protocol targets. Run it from the repository root:
108
109```bash
110cargo run --manifest-path lib/rs/test/fuzz/Cargo.toml --bin corpus_generator -- \
111 --output-dir <output_dir> \
112 --protocol <binary|compact> \
113 --generate <num_files> \
114 --buffer-size <buffer_size> \
115 --random-size <random_size>
116```
117
118Reasonable values (determined empirically):
119
120```bash
121cargo run --manifest-path lib/rs/test/fuzz/Cargo.toml --bin corpus_generator -- \
122 --output-dir ./lib/rs/test/fuzz/corpus/binary \
123 --protocol binary \
124 --generate 1000 \
125 --buffer-size 65536 \
126 --random-size 16384
127
128cargo run --manifest-path lib/rs/test/fuzz/Cargo.toml --bin corpus_generator -- \
129 --output-dir ./lib/rs/test/fuzz/corpus/compact \
130 --protocol compact \
131 --generate 1000 \
132 --buffer-size 16384 \
133 --random-size 16384
134```
135
136Then run a matching Ruby target:
137
138```bash
139make -C lib/rb/test/fuzz fuzz-parse-binary \
140 CORPUS=../rs/test/fuzz/corpus/binary
141```
142
143The Rust corpus generator does not emit JSON protocol inputs, so use it only with the Binary and Compact Ruby targets.
144
145## Troubleshooting
146
147If 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.
148
149If 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.