{"id":18894119,"url":"https://github.com/trailofbits/ruzzy","last_synced_at":"2026-03-07T11:02:41.438Z","repository":{"id":230408065,"uuid":"745559279","full_name":"trailofbits/ruzzy","owner":"trailofbits","description":"A coverage-guided fuzzer for pure Ruby code and Ruby C extensions","archived":false,"fork":false,"pushed_at":"2026-02-19T20:22:30.000Z","size":75,"stargazers_count":112,"open_issues_count":11,"forks_count":7,"subscribers_count":5,"default_branch":"main","last_synced_at":"2026-02-19T23:34:15.530Z","etag":null,"topics":["fuzzer","fuzzing","libfuzzer","ruby","ruby-extension","ruby-gem"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/trailofbits.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-01-19T15:47:13.000Z","updated_at":"2026-02-19T20:22:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"8dc481a5-d0db-42de-9edf-92750074896e","html_url":"https://github.com/trailofbits/ruzzy","commit_stats":null,"previous_names":["trailofbits/ruzzy"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/trailofbits/ruzzy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fruzzy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fruzzy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fruzzy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fruzzy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trailofbits","download_url":"https://codeload.github.com/trailofbits/ruzzy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fruzzy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30212103,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T09:02:10.694Z","status":"ssl_error","status_checked_at":"2026-03-07T09:02:08.429Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fuzzer","fuzzing","libfuzzer","ruby","ruby-extension","ruby-gem"],"created_at":"2024-11-08T08:18:07.304Z","updated_at":"2026-03-07T11:02:41.432Z","avatar_url":"https://github.com/trailofbits.png","language":"Ruby","readme":"# Ruzzy\n\n[![Test](https://github.com/trailofbits/ruzzy/actions/workflows/test.yml/badge.svg)](https://github.com/trailofbits/ruzzy/actions/workflows/test.yml)\n[![Gem Version](https://img.shields.io/gem/v/ruzzy)](https://rubygems.org/gems/ruzzy)\n\nA coverage-guided fuzzer for pure Ruby code and Ruby [C extensions](https://ruby-doc.org/3.3.0/extension_rdoc.html).\n\nRuzzy is heavily inspired by Google's [Atheris](https://github.com/google/atheris), a Python fuzzer. Like Atheris, Ruzzy uses [libFuzzer](https://llvm.org/docs/LibFuzzer.html) for its coverage instrumentation and fuzzing engine. Ruzzy also supports [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) and [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) when fuzzing C extensions. If you'd like to learn more about the inspiration behind Ruzzy, see our paper: [Design and Implementation of a Coverage-Guided Ruby Fuzzer](https://dl.acm.org/doi/10.1145/3675741.3675749).\n\nTable of contents:\n\n- [Installing](#installing)\n- [Using](#using)\n  - [Getting started](#getting-started)\n  - [Fuzzing pure Ruby code](#fuzzing-pure-ruby-code)\n  - [Fuzzing Ruby C extensions](#fuzzing-ruby-c-extensions)\n- [Trophy case](#trophy-case)\n- [Developing](#developing)\n  - [Compiling](#compiling)\n  - [Testing](#testing)\n  - [Linting](#linting)\n  - [Releasing](#releasing)\n- [Further reading](#further-reading)\n\n# Installing\n\nCurrently, Ruzzy only supports Linux x86-64 and AArch64/ARM64. If you'd like to run Ruzzy on a Mac or Windows, you can build the [`Dockerfile`](https://github.com/trailofbits/ruzzy/blob/main/Dockerfile) and/or use the [development environment](#developing). Ruzzy requires a recent version of `clang` (tested back to `14.0.0`), preferably the [latest release](https://github.com/llvm/llvm-project/releases).\n\nInstall Ruzzy with the following command:\n\n```bash\nMAKE=\"make --environment-overrides V=1\" \\\nCC=\"/path/to/clang\" \\\nCXX=\"/path/to/clang++\" \\\nLDSHARED=\"/path/to/clang -shared\" \\\nLDSHAREDXX=\"/path/to/clang++ -shared\" \\\n    gem install ruzzy\n```\n\nThere's a lot going on here, so let's break it down:\n\n- The `MAKE` environment variable overrides the `make` command when compiling the Ruzzy C extension. This tells `make` to respect subsequent environment variables when compiling the extension.\n- The rest of the environment variables are used during compilation to ensure we're using the proper `clang` binaries. This ensures we have the latest `clang` features, which are necessary for proper fuzzing.\n\nIf you run into issues installing, then you can run the following command to get debugging output:\n\n```bash\nRUZZY_DEBUG=1 gem install --verbose ruzzy\n```\n\n# Using\n\n## Getting started\n\nRuzzy includes a [toy example](https://llvm.org/docs/LibFuzzer.html#toy-example) to demonstrate how it works. First, set the following environment variable:\n\n```bash\nexport ASAN_OPTIONS=\"allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0\"\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eUnderstanding these options isn't necessary, but if you're curious click here.\u003c/summary\u003e\n\n### `ASAN_OPTIONS`\n\n1. Memory allocation failures are common and low impact (DoS), so skip them for now.\n1. Like Python, the Ruby interpreter [leaks data](https://github.com/google/atheris/blob/master/native_extension_fuzzing.md#leak-detection), so ignore these for now.\n1. Ruby recommends [disabling sigaltstack](https://github.com/ruby/ruby/blob/master/doc/contributing/building_ruby.md#building-with-address-sanitizer).\n\n\u003c/details\u003e\n\nYou can then run the example with the following command:\n\n```bash\nLD_PRELOAD=$(ruby -e 'require \"ruzzy\"; print Ruzzy::ASAN_PATH') \\\n    ruby -e 'require \"ruzzy\"; Ruzzy.dummy'\n```\n\n_`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). However, unlike `ASAN_OPTIONS`, you probably do not want to `export` it as it may interfere with other programs._\n\nIt should quickly produce a crash like the following:\n\n```\nINFO: Running with entropic power schedule (0xFF, 100).\nINFO: Seed: 2527961537\n...\n==45==ERROR: AddressSanitizer: heap-use-after-free on address 0x50c0009bab80 at pc 0xffff99ea1b44 bp 0xffffce8a67d0 sp 0xffffce8a67c8\n...\nSUMMARY: AddressSanitizer: heap-use-after-free /var/lib/gems/3.1.0/gems/ruzzy-0.7.0/ext/dummy/dummy.c:18:24 in _c_dummy_test_one_input\n...\n==45==ABORTING\nMS: 4 EraseBytes-CopyPart-CopyPart-ChangeBit-; base unit: 410e5346bca8ee150ffd507311dd85789f2e171e\n0x48,0x49,\nHI\nartifact_prefix='./'; Test unit written to ./crash-253420c1158bc6382093d409ce2e9cff5806e980\nBase64: SEk=\n```\n\nWe can see that it correctly found the input (`\"HI\"`) that produced a memory violation. For more information, see [`dummy.c`](https://github.com/trailofbits/ruzzy/blob/main/ext/dummy/dummy.c) to see why this violation occurred.\n\nYou can re-run the crash case with the following command:\n\n```bash\nLD_PRELOAD=$(ruby -e 'require \"ruzzy\"; print Ruzzy::ASAN_PATH') \\\n    ruby -e 'require \"ruzzy\"; Ruzzy.dummy' \\\n    ./crash-253420c1158bc6382093d409ce2e9cff5806e980\n```\n\nThe following sanitizers are available:\n\n- `Ruzzy::ASAN_PATH` for [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html)\n- `Ruzzy::UBSAN_PATH` for [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html)\n\n## Fuzzing pure Ruby code\n\nLet's fuzz a small Ruby script as an example. Fuzzing pure Ruby code requires two Ruby scripts: a tracer script and a fuzzing harness. The tracer script is required due to an implementation detail of the Ruby interpreter. Understanding the details of this interaction, other than the fact that it's necessary, is not required.\n\nFirst, the tracer script, let's call it `test_tracer.rb`:\n\n```ruby\n# frozen_string_literal: true\n\nrequire 'ruzzy'\n\nRuzzy.trace('test_harness.rb')\n```\n\nNext, the fuzzing harness, let's call it `test_harness.rb`:\n\n```ruby\n# frozen_string_literal: true\n\nrequire 'ruzzy'\n\ndef fuzzing_target(input)\n  if input.length == 4\n    if input[0] == 'F'\n      if input[1] == 'U'\n        if input[2] == 'Z'\n          if input[3] == 'Z'\n            raise\n          end\n        end\n      end\n    end\n  end\nend\n\ntest_one_input = lambda do |data|\n  fuzzing_target(data) # Your fuzzing target would go here\n  return 0\nend\n\nRuzzy.fuzz(test_one_input)\n```\n\nYou can run this file and start fuzzing with the following command:\n\n```bash\nLD_PRELOAD=$(ruby -e 'require \"ruzzy\"; print Ruzzy::ASAN_PATH') \\\n    ruby test_tracer.rb\n```\n\nIt should quickly produce a crash like the following:\n\n```\nINFO: Running with entropic power schedule (0xFF, 100).\nINFO: Seed: 2311041000\n...\n/app/ruzzy/bin/test_harness.rb:12:in `block in \u003ctop (required)\u003e': unhandled exception\n\tfrom /var/lib/gems/3.1.0/gems/ruzzy-0.7.0/lib/ruzzy.rb:15:in `c_fuzz'\n\tfrom /var/lib/gems/3.1.0/gems/ruzzy-0.7.0/lib/ruzzy.rb:15:in `fuzz'\n\tfrom /app/ruzzy/bin/test_harness.rb:35:in `\u003ctop (required)\u003e'\n\tfrom bin/test_tracer.rb:7:in `require_relative'\n\tfrom bin/test_tracer.rb:7:in `\u003cmain\u003e'\n...\nSUMMARY: libFuzzer: fuzz target exited\nMS: 1 CopyPart-; base unit: 24b4b428cf94c21616893d6f94b30398a49d27cc\n0x46,0x55,0x5a,0x5a,\nFUZZ\nartifact_prefix='./'; Test unit written to ./crash-aea2e3923af219a8956f626558ef32f30a914ebc\nBase64: RlVaWg==\n```\n\nWe can see that it correctly found the input (`\"FUZZ\"`) that produced an exception.\n\nTo fuzz your own target, modify the `test_one_input` `lambda` to call your target function.\n\n## Fuzzing Ruby C extensions\n\nLet's fuzz the [`msgpack-ruby`](https://github.com/msgpack/msgpack-ruby) library as an example. First, install the gem:\n\n```bash\nMAKE=\"make --environment-overrides V=1\" \\\nCC=\"/path/to/clang\" \\\nCXX=\"/path/to/clang++\" \\\nLDSHARED=\"/path/to/clang -shared\" \\\nLDSHAREDXX=\"/path/to/clang++ -shared\" \\\nCFLAGS=\"-fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g\" \\\nCXXFLAGS=\"-fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g\" \\\n    gem install msgpack\n```\n\nIn addition to the environment variables used when compiling Ruzzy, we're specifying `CFLAGS` and `CXXFLAGS`. These flags aid in the fuzzing process. They enable helpful functionality like an address sanitizer, and improved stack trace information. For more information see [AddressSanitizerFlags](https://github.com/google/sanitizers/wiki/AddressSanitizerFlags).\n\nNext, we need a fuzzing harness for `msgpack`. The following may be familiar to those with [libFuzzer experience](https://llvm.org/docs/LibFuzzer.html#fuzz-target):\n\n```ruby\n# frozen_string_literal: true\n\nrequire 'msgpack'\nrequire 'ruzzy'\n\ntest_one_input = lambda do |data|\n  begin\n    MessagePack.unpack(data)\n  rescue Exception\n    # We're looking for memory corruption, not Ruby exceptions\n  end\n  return 0\nend\n\nRuzzy.fuzz(test_one_input)\n```\n\nLet's call this file `fuzz_msgpack.rb`. You can run this file and start fuzzing with the following command:\n\n```bash\nLD_PRELOAD=$(ruby -e 'require \"ruzzy\"; print Ruzzy::ASAN_PATH') \\\n    ruby fuzz_msgpack.rb\n```\n\nlibFuzzer options can be passed to the Ruby script like so:\n\n```bash\nLD_PRELOAD=$(ruby -e 'require \"ruzzy\"; print Ruzzy::ASAN_PATH') \\\n    ruby fuzz_msgpack.rb /path/to/corpus\n```\n\nSee [libFuzzer options](https://llvm.org/docs/LibFuzzer.html#options) for more information.\n\nTo fuzz your own target, modify the `test_one_input` `lambda` to call your target function.\n\n# Trophy case\n\nBugs found using Ruzzy:\n\n- `toml` gem: [#76](https://github.com/jm/toml/issues/76)\n- `toml-rb` gem: [#150](https://github.com/emancu/toml-rb/issues/150)\n- `ox` gem: [#351](https://github.com/ohler55/ox/issues/351)\n- Ruby `Marshal` garbage collector crash: [#20941](https://bugs.ruby-lang.org/issues/20941)\n- XML parser differential: [REXML vs. Nokogiri](https://github.blog/security/sign-in-as-anyone-bypassing-saml-sso-authentication-with-parser-differentials/)\n\n# Developing\n\nDevelopment can be done locally, or using the `Dockerfile` provided in this repository.\n\nYou can build the Ruzzy Docker image with the following command:\n\n```bash\ndocker build --tag ruzzy .\n```\n\nThen, you can shell into the container using the following command:\n\n```\ndocker run -it -v $(pwd):/app/ruzzy --entrypoint /bin/bash ruzzy\n```\n\n## Compiling\n\nWe use [`rake-compiler`](https://github.com/rake-compiler/rake-compiler) to compile Ruzzy's C extensions.\n\nYou can compile the C extensions within the container with the following command:\n\n```bash\nrake compile\n```\n\n## Testing\n\nWe use `rake` unit tests to test Ruby code.\n\nYou can run the tests within the container with the following command:\n\n```bash\nLD_PRELOAD=$(ruby -e 'require \"ruzzy\"; print Ruzzy::ASAN_PATH') \\\n    rake test\n```\n\n## Linting\n\nWe use `rubocop` to lint Ruby code.\n\nYou can run `rubocop` within the container with the following command:\n\n```bash\nrubocop\n```\n\n## Releasing\n\nRuzzy is automatically [released](https://github.com/trailofbits/ruzzy/actions/workflows/release.yml) to [RubyGems](https://rubygems.org/gems/ruzzy) when a new git tag is pushed.\n\nTo release a new version run the following commands:\n\n```bash\ngit tag vX.X.X\n```\n\n```bash\ngit push --tags\n```\n\n# Further reading\n\n- Ruby C extensions\n  - https://guides.rubygems.org/gems-with-extensions/\n  - https://www.rubyguides.com/2018/03/write-ruby-c-extension/\n  - https://rubyreferences.github.io/rubyref/advanced/extensions.html\n  - https://silverhammermba.github.io/emberb/c/\n  - https://ruby-doc.org/3.3.0/extension_rdoc.html\n  - https://ruby-doc.org/3.3.0/stdlibs/mkmf/MakeMakefile.html\n  - https://github.com/flavorjones/ruby-c-extensions-explained\n  - https://github.com/ruby/ruby/blob/v3_3_0/lib/mkmf.rb\n- Ruby fuzzing\n  - https://github.com/twistlock/kisaten\n  - https://github.com/richo/afl-ruby\n  - https://github.com/krypt/FuzzBert\n  - https://z2-2z.github.io/2024/jan/16/fuzzing-ruby-c-extensions-with-coverage-and-asan.html\n  - https://bsidessf2018.sched.com/event/E6jC/fuzzing-ruby-and-c-extensions\n- Atheris\n  - https://github.com/google/atheris/blob/master/native_extension_fuzzing.md\n  - https://security.googleblog.com/2020/12/how-atheris-python-fuzzer-works.html\n  - https://github.com/google/atheris/blob/2.3.0/setup.py\n  - https://github.com/google/atheris/blob/2.3.0/src/native/core.cc\n  - https://github.com/google/atheris/blob/2.3.0/src/native/tracer.cc\n  - https://github.com/google/atheris/blob/2.3.0/src/native/counters.cc\n  - https://github.com/google/atheris/blob/2.3.0/src/instrument_bytecode.py\n- Coverage\n  - https://calabi-yau.space/blog/sanitizer-coverage-interface.html\n  - https://carstein.github.io/2020/05/21/writing-simple-fuzzer-4.html\n  - https://h0mbre.github.io/Fuzzing-Like-A-Caveman-5/\n  - https://github.com/mirrorer/afl/blob/master/docs/technical_details.txt\n  - https://lcamtuf.coredump.cx/afl/historical_notes.txt\n  - https://www.code-intelligence.com/blog/the-magic-behind-feedback-based-fuzzing\n  - https://blog.includesecurity.com/2024/04/coverage-guided-fuzzing-extending-instrumentation/\n  - https://git.sr.ht/~myrrc/ba-thesis/blob/master/thesis.pdf\n  - https://www.politesi.polimi.it/bitstream/10589/173614/3/2021_04_Frighetto.pdf\n  - https://wcventure.github.io/FuzzingPaper/Paper/SP18_ColLAFL.pdf\n  - https://www.ndss-symposium.org/wp-content/uploads/2020/02/24422.pdf\n  - https://mboehme.github.io/paper/ICSE22.pdf\n  - https://www.usenix.org/system/files/raid2019-wang-jinghan.pdf\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrailofbits%2Fruzzy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrailofbits%2Fruzzy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrailofbits%2Fruzzy/lists"}