Skip to content

Commit

Permalink
Add initial README, and other minor changes
Browse files Browse the repository at this point in the history
  • Loading branch information
mschwager committed Jan 24, 2024
1 parent 33c7a5e commit f95a03d
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 12 deletions.
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ FROM debian:12-slim
RUN apt update && apt install -y \
binutils \
gcc \
g++ \
libc-dev \
make \
ruby \
Expand Down Expand Up @@ -45,9 +46,9 @@ ENV ASAN_STRIPPED_LIB "/tmp/libclang_rt.asan.a"
ENV ASAN_MERGED_LIB "/tmp/asan_with_fuzzer.so"

# https://github.com/google/atheris/blob/master/native_extension_fuzzing.md#why-this-is-necessary
RUN cp "$ASAN_LIB" /tmp
RUN cp "$ASAN_LIB" "$ASAN_STRIPPED_LIB"
RUN ar d "$ASAN_STRIPPED_LIB" asan_preinit.cc.o asan_preinit.cpp.o
RUN "$CC" \
RUN "$CXX" \
-Wl,--whole-archive \
"$FUZZER_NO_MAIN_LIB" \
"$ASAN_STRIPPED_LIB" \
Expand Down
162 changes: 153 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,156 @@
# ruzzy
# Ruzzy

A Ruby C extension fuzzer
A Ruby C extension fuzzer.

# Reading list
Ruzzy is based on Google's [Atheris Python fuzzer](https://github.com/google/atheris). Unlike Atheris, Ruzzy is focused on fuzzing Ruby C extensions and not Ruby code itself. This may change in the future as the project gains traction.

- https://guides.rubygems.org/gems-with-extensions/
- https://www.rubyguides.com/2018/03/write-ruby-c-extension/
- https://rubyreferences.github.io/rubyref/advanced/extensions.html
- https://ruby-doc.org/3.3.0/stdlibs/mkmf/MakeMakefile.html
- https://github.com/ruby/ruby/blob/master/lib/mkmf.rb
- https://github.com/flavorjones/ruby-c-extensions-explained
# Installing

Ruzzy relies on Docker for both development and production fuzzer usage.

You can build the Ruzzy Docker image with the following command:

```bash
docker build --tag ruzzy .
```

_You may want to grab a cup of coffee, the initial build can take a while._

# Using

## Getting started

Ruzzy includes a [toy example](https://llvm.org/docs/LibFuzzer.html#toy-example) to demonstrate how it works.

You can run the example with the following command:

```bash
$ docker run -v $(pwd):/tmp/output/ ruzzy -artifact_prefix=/tmp/output/
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 1250491632
...
==2==ABORTING
MS: 1 CopyPart-; base unit: 253420c1158bc6382093d409ce2e9cff5806e980
0x48,0x49,0x48,0x49,
HIHI
artifact_prefix='/tmp/output/'; Test unit written to /tmp/output/crash-53551f97ce4b956f4bfcdeec9eb8d01b5d5533a7
Base64: SElISQ==
```

This should produce a crash relatively quickly. We can inspect the crash with the following command:

```bash
$ xxd crash-53551f97ce4b956f4bfcdeec9eb8d01b5d5533a7
00000000: 4849 4849 HIHI
```

The Docker volume and `-artifact_prefix` flag will persist any crashes within the container into the host's filesystem. This highlights one of Ruzzy's features: flags passed to the Docker container are then [sent to libFuzzer](https://llvm.org/docs/LibFuzzer.html#options). You can use this functionality to re-run crash files:

```bash
$ docker run -v $(pwd):/tmp/output/ ruzzy /tmp/output/crash-53551f97ce4b956f4bfcdeec9eb8d01b5d5533a7
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 1672214264
...
Running: /tmp/output/crash-53551f97ce4b956f4bfcdeec9eb8d01b5d5533a7
...
Executed /tmp/output/crash-53551f97ce4b956f4bfcdeec9eb8d01b5d5533a7 in 2 ms
***
*** NOTE: fuzzing was not performed, you have only
*** executed the target code on a fixed set of inputs.
***
```

You can also use this functionality to pass in a fuzzing corpus:

```bash
docker run -v $(pwd)/corpus:/tmp/corpus -v $(pwd):/tmp/output/ ruzzy /tmp/corpus
```

## Fuzzing third-party libraries

There are two primary ways you may want to fuzz third-party libraries: 1) modify the `Dockerfile` and `entrypoint.sh` script, and/or 2) shell into a Ruzzy container. This section will focus on (2).

You can get a shell in the Ruzzy environment with the following command:

```bash
docker run -it -v $(pwd):/app/ruzzy --entrypoint /bin/bash ruzzy
```

Let's fuzz the [`msgpack-ruby`](https://github.com/msgpack/msgpack-ruby) library as an example. First, install the gem:

```bash
gem install --verbose msgpack
```

Next, we need a fuzzing target for `msgpack`.

The following is a basic example that should be familiar to those with [libFuzzer experience](https://llvm.org/docs/LibFuzzer.html#fuzz-target):

```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)
```

Let's call this file `fuzz_msgpack.rb`.

You can run this file and start fuzzing with the following command:

```bash
LD_PRELOAD=${ASAN_MERGED_LIB} ruby -Ilib fuzz_msgpack.rb
```

_`LD_PRELOAD` is required for the same reasons [as Atheris](https://github.com/google/atheris/blob/master/native_extension_fuzzing.md#option-a-sanitizerlibfuzzer-preloads)._

# Developing

Development is done primarily within the Docker container.

First, shell into the container using the `docker run ... --entrypoint` command above.

## Testing

We use `rake` unit tests to test Ruby code.

You can run the tests within the container with the following command:

```bash
rake test
```

## Linting

We use `rubocop` to lint Ruby code.

You can run `rubocop` within the container with the following command:

```bash
rubocop
```

# Recommended reading

- Ruby C extensions
- https://guides.rubygems.org/gems-with-extensions/
- https://www.rubyguides.com/2018/03/write-ruby-c-extension/
- https://rubyreferences.github.io/rubyref/advanced/extensions.html
- https://ruby-doc.org/3.3.0/stdlibs/mkmf/MakeMakefile.html
- https://github.com/flavorjones/ruby-c-extensions-explained
- https://github.com/ruby/ruby/blob/v3_1_2/lib/mkmf.rb
- Atheris
- https://github.com/google/atheris/blob/master/native_extension_fuzzing.md
- https://security.googleblog.com/2020/12/how-atheris-python-fuzzer-works.html
- https://github.com/google/atheris/blob/2.3.0/setup.py
- https://github.com/google/atheris/blob/2.3.0/src/native/core.cc
2 changes: 2 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ require 'rake/testtask'
require 'rake/extensiontask'

Rake::TestTask.new do |t|
# This is required for tests that use cruzzy functionality
ENV['LD_PRELOAD'] = ENV['ASAN_MERGED_LIB']
t.verbose = true
end

Expand Down
2 changes: 1 addition & 1 deletion ext/cruzzy/cruzzy.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ static VALUE c_dummy_test_one_input(VALUE self, VALUE data)

void Init_cruzzy()
{
VALUE ruzzy = rb_const_get(rb_cObject, rb_intern("Ruzzy"));;
VALUE ruzzy = rb_const_get(rb_cObject, rb_intern("Ruzzy"));
rb_define_module_function(ruzzy, "c_fuzz", &c_fuzz, 2);
rb_define_module_function(ruzzy, "c_libfuzzer_is_loaded", &c_libfuzzer_is_loaded, 0);
rb_define_module_function(ruzzy, "c_dummy_test_one_input", &c_dummy_test_one_input, 1);
Expand Down
1 change: 1 addition & 0 deletions ruzzy.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ Gem::Specification.new do |s|

s.add_development_dependency 'rake', '~> 13.0'
s.add_development_dependency 'rake-compiler', '~> 1.2'
s.add_development_dependency 'rubocop', '~> 1.60'
end

0 comments on commit f95a03d

Please sign in to comment.