skillby trailofbits
ruzzy
Ruzzy is a coverage-guided Ruby fuzzer by Trail of Bits. Use for fuzzing pure Ruby code and Ruby C extensions.
Installs: 0
Used in: 1 repos
Updated: 0mo ago
$
npx ai-builder add skill trailofbits/ruzzyInstalls to .claude/skills/ruzzy/
# Ruzzy
Ruzzy is a coverage-guided fuzzer for Ruby built on libFuzzer. It enables fuzzing both pure Ruby code and Ruby C extensions with sanitizer support for detecting memory corruption and undefined behavior.
## When to Use
Ruzzy is currently the only production-ready coverage-guided fuzzer for Ruby.
**Choose Ruzzy when:**
- Fuzzing Ruby applications or libraries
- Testing Ruby C extensions for memory safety issues
- You need coverage-guided fuzzing for Ruby code
- Working with Ruby gems that have native extensions
## Quick Start
Set up environment:
```bash
export ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0"
```
Test with the included toy example:
```bash
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby -e 'require "ruzzy"; Ruzzy.dummy'
```
This should quickly find a crash demonstrating that Ruzzy is working correctly.
## Installation
### Platform Support
Ruzzy supports Linux x86-64 and AArch64/ARM64. For macOS or Windows, use the [Dockerfile](https://github.com/trailofbits/ruzzy/blob/main/Dockerfile) or [development environment](https://github.com/trailofbits/ruzzy#developing).
### Prerequisites
- Linux x86-64 or AArch64/ARM64
- Recent version of clang (tested back to 14.0.0, latest release recommended)
- Ruby with gem installed
### Installation Command
Install Ruzzy with clang compiler flags:
```bash
MAKE="make --environment-overrides V=1" \
CC="/path/to/clang" \
CXX="/path/to/clang++" \
LDSHARED="/path/to/clang -shared" \
LDSHAREDXX="/path/to/clang++ -shared" \
gem install ruzzy
```
**Environment variables explained:**
- `MAKE`: Overrides make to respect subsequent environment variables
- `CC`, `CXX`, `LDSHARED`, `LDSHAREDXX`: Ensure proper clang binaries are used for latest features
### Troubleshooting Installation
If installation fails, enable debug output:
```bash
RUZZY_DEBUG=1 gem install --verbose ruzzy
```
### Verification
Verify installation by running the toy example (see Quick Start section).
## Writing a Harness
### Fuzzing Pure Ruby Code
Pure Ruby fuzzing requires two scripts due to Ruby interpreter implementation details.
**Tracer script (`test_tracer.rb`):**
```ruby
# frozen_string_literal: true
require 'ruzzy'
Ruzzy.trace('test_harness.rb')
```
**Harness script (`test_harness.rb`):**
```ruby
# frozen_string_literal: true
require 'ruzzy'
def fuzzing_target(input)
# Your code to fuzz here
if input.length == 4
if input[0] == 'F'
if input[1] == 'U'
if input[2] == 'Z'
if input[3] == 'Z'
raise
end
end
end
end
end
end
test_one_input = lambda do |data|
fuzzing_target(data)
return 0
end
Ruzzy.fuzz(test_one_input)
```
Run with:
```bash
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby test_tracer.rb
```
### Fuzzing Ruby C Extensions
C extensions can be fuzzed with a single harness file, no tracer needed.
**Example harness for msgpack (`fuzz_msgpack.rb`):**
```ruby
# frozen_string_literal: true
require 'msgpack'
require 'ruzzy'
test_one_input = lambda do |data|
begin
MessagePack.unpack(data)
rescue Exception
# We're looking for memory corruption, not Ruby exceptions
end
return 0
end
Ruzzy.fuzz(test_one_input)
```
Run with:
```bash
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby fuzz_msgpack.rb
```
### Harness Rules
| Do | Don't |
|----|-------|
| Catch Ruby exceptions if testing C extensions | Let Ruby exceptions crash the fuzzer |
| Return 0 from test_one_input lambda | Return other values |
| Keep harness deterministic | Use randomness or time-based logic |
| Use tracer script for pure Ruby | Skip tracer for pure Ruby code |
> **See Also:** For detailed harness writing techniques, patterns for handling complex inputs,
> and advanced strategies, see the **fuzz-harness-writing** technique skill.
## Compilation
### Installing Gems with Sanitizers
When installing Ruby gems with C extensions for fuzzing, compile with sanitizer flags:
```bash
MAKE="make --environment-overrides V=1" \
CC="/path/to/clang" \
CXX="/path/to/clang++" \
LDSHARED="/path/to/clang -shared" \
LDSHAREDXX="/path/to/clang++ -shared" \
CFLAGS="-fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g" \
CXXFLAGS="-fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g" \
gem install <gem-name>
```
### Build Flags
| Flag | Purpose |
|------|---------|
| `-fsanitize=address,fuzzer-no-link` | Enable AddressSanitizer and fuzzer instrumentation |
| `-fno-omit-frame-pointer` | Improve stack trace quality |
| `-fno-common` | Better compatibility with sanitizers |
| `-fPIC` | Position-independent code for shared libraries |
| `-g` | Include debug symbols |
## Running Campaigns
### Environment Setup
Before running any fuzzing campaign, set ASAN_OPTIONS:
```bash
export ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0"
```
**Options explained:**
1. `allocator_may_return_null=1`: Skip common low-impact allocation failures (DoS)
2. `detect_leaks=0`: Ruby interpreter leaks data, ignore these for now
3. `use_sigaltstack=0`: Ruby recommends disabling sigaltstack with ASan
### Basic Run
```bash
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby harness.rb
```
**Note:** `LD_PRELOAD` is required for sanitizer injection. Unlike `ASAN_OPTIONS`, do not export it as it may interfere with other programs.
### With Corpus
```bash
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby harness.rb /path/to/corpus
```
### Passing libFuzzer Options
All libFuzzer options can be passed as arguments:
```bash
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby harness.rb /path/to/corpus -max_len=1024 -timeout=10
```
See [libFuzzer options](https://llvm.org/docs/LibFuzzer.html#options) for full reference.
### Reproducing Crashes
Re-run a crash case by passing the crash file:
```bash
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby harness.rb ./crash-253420c1158bc6382093d409ce2e9cff5806e980
```
### Interpreting Output
| Output | Meaning |
|--------|---------|
| `INFO: Running with entropic power schedule` | Fuzzing campaign started |
| `ERROR: AddressSanitizer: heap-use-after-free` | Memory corruption detected |
| `SUMMARY: libFuzzer: fuzz target exited` | Ruby exception occurred |
| `artifact_prefix='./'; Test unit written to ./crash-*` | Crash input saved |
| `Base64: ...` | Base64 encoding of crash input |
## Sanitizer Integration
### AddressSanitizer (ASan)
Ruzzy includes a pre-compiled AddressSanitizer library:
```bash
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby harness.rb
```
Use ASan for detecting:
- Heap buffer overflows
- Stack buffer overflows
- Use-after-free
- Double-free
- Memory leaks (disabled by default in Ruzzy)
### UndefinedBehaviorSanitizer (UBSan)
Ruzzy also includes UBSan:
```bash
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::UBSAN_PATH') \
ruby harness.rb
```
Use UBSan for detecting:
- Signed integer overflow
- Null pointer dereferences
- Misaligned memory access
- Division by zero
### Common Sanitizer Issues
| Issue | Solution |
|-------|----------|
| Ruby interpreter leak warnings | Use `ASAN_OPTIONS=detect_leaks=0` |
| Sigaltstack conflicts | Use `ASAN_OPTIONS=use_sigaltstack=0` |
| Allocation failure spam | Use `ASAN_OPTIONS=allocator_may_return_null=1` |
| LD_PRELOAD interferes with tools | Don't export it; set inline with ruby command |
> **See Also:** For detailed sanitizer configuration, common issues, and advanced flags,
> see the **address-sanitizer** and **undefined-behavior-sanitizer** technique skills.
## Real-World Examples
### Example: msgpack-ruby
Fuzzing the msgpack MessagePack parser for memory corruption.
**Install with sanitizers:**
```bash
MAKE="make --environment-overrides V=1" \
CC="/path/to/clang" \
CXX="/path/to/clang++" \
LDSHARED="/path/to/clang -shared" \
LDSHAREDXX="/path/to/clang++ -shared" \
CFLAGS="-fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g" \
CXXFLAGS="-fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g" \
gem install msgpack
```
**Harness (`fuzz_msgpack.rb`):**
```ruby
# frozen_string_literal: true
require 'msgpack'
require 'ruzzy'
test_one_input = lambda do |data|
begin
MessagePack.unpack(data)
rescue Exception
# We're looking for memory corruption, not Ruby exceptions
end
return 0
end
Ruzzy.fuzz(test_one_input)
```
**Run:**
```bash
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 fuzz_msgpack.rb
```
### Example: Pure Ruby Target
Fuzzing pure Ruby code with a custom parser.
**Tracer (`test_tracer.rb`):**
```ruby
# frozen_string_literal: true
require 'ruzzy'
Ruzzy.trace('test_harness.rb')
```
**Harness (`test_harness.rb`):**
```ruby
# frozen_string_literal: true
require 'ruzzy'
require_relative 'my_parser'
test_one_input = lambda do |data|
begin
MyParser.parse(data)
rescue StandardError
# Expected exceptions from malformed input
end
return 0
end
Ruzzy.fuzz(test_one_input)
```
**Run:**
```bash
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_tracer.rb
```
## Troubleshooting
| Problem | Cause | Solution |
|---------|-------|----------|
| Installation fails | Wrong clang version or path | Verify clang path, use clang 14.0.0+ |
| `cannot open shared object file` | LD_PRELOAD not set | Set LD_PRELOAD inline with ruby command |
| Fuzzer immediately exits | Missing corpus directory | Create corpus directory or pass as argument |
| No coverage progress | Pure Ruby needs tracer | Use tracer script for pure Ruby code |
| Leak detection spam | Ruby interpreter leaks | Set `ASAN_OPTIONS=detect_leaks=0` |
| Installation debug needed | Compilation errors | Use `RUZZY_DEBUG=1 gem install --verbose ruzzy` |
## Related Skills
### Technique Skills
| Skill | Use Case |
|-------|----------|
| **fuzz-harness-writing** | Detailed guidance on writing effective harnesses |
| **address-sanitizer** | Memory error detection during fuzzing |
| **undefined-behavior-sanitizer** | Detecting undefined behavior in C extensions |
| **libfuzzer** | Understanding libFuzzer options (Ruzzy is built on libFuzzer) |
### Related Fuzzers
| Skill | When to Consider |
|-------|------------------|
| **libfuzzer** | When fuzzing Ruby C extension code directly in C/C++ |
| **aflpp** | Alternative approach for fuzzing Ruby by instrumenting Ruby interpreter |
## Resources
### Key External Resources
**[Introducing Ruzzy, a coverage-guided Ruby fuzzer](https://blog.trailofbits.com/2024/03/29/introducing-ruzzy-a-coverage-guided-ruby-fuzzer/)**
Official Trail of Bits blog post announcing Ruzzy, covering motivation, architecture, and initial results.
**[Ruzzy GitHub Repository](https://github.com/trailofbits/ruzzy)**
Source code, additional examples, and development instructions.
**[libFuzzer Documentation](https://llvm.org/docs/LibFuzzer.html)**
Since Ruzzy is built on libFuzzer, understanding libFuzzer options and behavior is valuable.
**[Fuzzing Ruby C extensions](https://github.com/trailofbits/ruzzy#fuzzing-ruby-c-extensions)**
Detailed guide on fuzzing C extensions with compilation flags and examples.
**[Fuzzing pure Ruby code](https://github.com/trailofbits/ruzzy#fuzzing-pure-ruby-code)**
Detailed guide on the tracer pattern required for pure Ruby fuzzing.Quick Install
$
npx ai-builder add skill trailofbits/ruzzyDetails
- Type
- skill
- Author
- trailofbits
- Slug
- trailofbits/ruzzy
- Created
- 0mo ago