mcpField
R
ruzzy

ruzzy

github.com/trailofbitsテスト・品質7.9k5.2k 回インストール今日更新
リポジトリ

ruzzy について

ruzzyはlibFuzzerを基盤とする、Ruby向けのカバレッジガイド型ファザーです。純粋なRubyコードとRuby C拡張をテストでき、メモリ破損や未定義動作の検出にサニタイザーを利用します。

主な機能

  1. 01Ruby向けカバレッジガイド型ファジング
  2. 02純粋なRubyとC拡張のハーネス
  3. 03AddressSanitizerとUBSanのサポート
  4. 04libFuzzerオプションとコーパス入力

活用シーン

  1. 01Rubyアプリやライブラリのファジング
  2. 02Ruby C拡張のメモリ安全性の確認
  3. 03保存済み入力によるクラッシュの再現

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/trailofbits/skills --skill ruzzy

インストール後、Claude Code で/ruzzyを入力して読み込まれたか確認します。

同梱ファイルagents/openai.yamlassets/trail-of-bits-mark.svg

詳細

Ruzzy

Ruzzy は libFuzzer を基盤とする、Ruby 向けのカバレッジ誘導型ファザーです。純粋な Ruby コードと Ruby C 拡張の両方をファジングでき、サニタイザーを使ってメモリ破損や未定義動作を検出できます。

使用する場面

Ruzzy は現在、Ruby 向けの唯一の本番運用可能なカバレッジ誘導型ファザーです。

次の場合は Ruzzy を選択してください:

  • Ruby アプリケーションやライブラリをファジングする場合
  • Ruby C 拡張のメモリ安全性の問題をテストする場合
  • Ruby コードに対するカバレッジ誘導型ファジングが必要な場合
  • ネイティブ拡張を含む Ruby gem を扱う場合

クイックスタート

環境をセットアップします:

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 -e 'require "ruzzy"; Ruzzy.dummy'

これにより、Ruzzy が正常に動作していることを示すクラッシュがすぐに見つかります。

インストール

プラットフォームのサポート

Ruzzy は Linux x86-64 および AArch64/ARM64 をサポートしています。macOS または Windows では、Dockerfile または開発環境を使用してください。

前提条件
  • Linux x86-64 または AArch64/ARM64
  • 最近のバージョンの clang(14.0.0 までのバージョンでテスト済み。最新リリースを推奨)
  • gem がインストールされた Ruby
インストールコマンド

clang コンパイラーフラグを指定して Ruzzy をインストールします:

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

環境変数の説明:

  • MAKE:後続の環境変数が反映されるように make を上書きします
  • CC、CXX、LDSHARED、LDSHAREDXX:最新機能を利用できるよう、適切な clang バイナリが使われるようにします
インストール時のトラブルシューティング

インストールに失敗する場合は、デバッグ出力を有効にします:

RUZZY_DEBUG=1 gem install --verbose ruzzy
検証

トイサンプルを実行してインストールを検証します(「クイックスタート」セクションを参照)。

ハーネスの作成

純粋な Ruby コードのファジング

Ruby インタープリターの実装上の詳細により、純粋な Ruby のファジングには 2 つのスクリプトが必要です。

トレーサースクリプト(test_tracer.rb):

# frozen_string_literal: true

require 'ruzzy'

Ruzzy.trace('test_harness.rb')

ハーネススクリプト(test_harness.rb):

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

次のように実行します:

LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    ruby test_tracer.rb
Ruby C 拡張のファジング

C 拡張は、トレーサーを使わず、単一のハーネスファイルでファジングできます。

msgpack 用ハーネスの例(fuzz_msgpack.rb):

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

次のように実行します:

LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    ruby fuzz_msgpack.rb
ハーネスのルール
することしないこと
C 拡張をテストする場合は Ruby の例外を捕捉するRuby の例外でファザーをクラッシュさせる
test_one_input ラムダから 0 を返すほかの値を返す
ハーネスを決定論的に保つ乱数や時刻に依存するロジックを使う
純粋な Ruby にはトレーサースクリプトを使う純粋な Ruby コードでトレーサーを省略する

関連項目: ハーネスの詳細な作成手法、複雑な入力を処理するパターン、 高度な戦略については、fuzz-harness-writing 技術スキルを参照してください。

コンパイル

サニタイザーを使った gem のインストール

ファジング用に C 拡張を含む Ruby gem をインストールする場合は、サニタイザーフラグを指定してコンパイルします:

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>
ビルドフラグ
フラグ目的
-fsanitize=address,fuzzer-no-linkAddressSanitizer とファザーの計測を有効にする
-fno-omit-frame-pointerスタックトレースの品質を向上させる
-fno-commonサニタイザーとの互換性を高める
-fPIC共有ライブラリ向けの位置独立コードを生成する
-gデバッグシンボルを含める

ファジングキャンペーンの実行

環境のセットアップ

ファジングキャンペーンを実行する前に、ASAN_OPTIONS を設定します:

export ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0"

オプションの説明:

  1. allocator_may_return_null=1:影響の小さい一般的な割り当て失敗(DoS)を無視します
  2. detect_leaks=0:Ruby インタープリターはデータをリークするため、現時点では無視します
  3. use_sigaltstack=0:Ruby は ASan での sigaltstack の無効化を推奨しています
基本的な実行
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    ruby harness.rb

注: サニタイザーを注入するには LD_PRELOAD が必要です。ASAN_OPTIONS とは異なり、ほかのプログラムに干渉する可能性があるため、エクスポートしないでください。

コーパスを指定した実行
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    ruby harness.rb /path/to/corpus
libFuzzer オプションの指定

すべての libFuzzer オプションを引数として渡せます:

LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    ruby harness.rb /path/to/corpus -max_len=1024 -timeout=10

詳細については、libFuzzer のオプションを参照してください。

クラッシュの再現

クラッシュファイルを渡して、クラッシュしたケースを再実行します:

LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    ruby harness.rb ./crash-253420c1158bc6382093d409ce2e9cff5806e980
出力の解釈
出力意味
INFO: Running with entropic power scheduleファジングキャンペーンが開始されました
ERROR: AddressSanitizer: heap-use-after-freeメモリ破損が検出されました
SUMMARY: libFuzzer: fuzz target exitedRuby の例外が発生しました
artifact_prefix='./'; Test unit written to ./crash-*クラッシュ入力が保存されました
Base64: ...クラッシュ入力の Base64 エンコード

サニタイザーとの統合

AddressSanitizer(ASan)

Ruzzy には、コンパイル済みの AddressSanitizer ライブラリが含まれています。

LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    ruby harness.rb

次の検出には ASan を使用します。

  • ヒープバッファーオーバーフロー
  • スタックバッファーオーバーフロー
  • 解放後使用
  • 二重解放
  • メモリリーク(Ruzzy ではデフォルトで無効)
UndefinedBehaviorSanitizer(UBSan)

Ruzzy には UBSan も含まれています。

LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::UBSAN_PATH') \
    ruby harness.rb

次の検出には UBSan を使用します。

  • 符号付き整数のオーバーフロー
  • Null ポインターの参照外し
  • アラインメントされていないメモリアクセス
  • ゼロ除算
よくあるサニタイザーの問題
問題解決策
Ruby インタープリターのリーク警告ASAN_OPTIONS=detect_leaks=0 を使用します
Sigaltstack の競合ASAN_OPTIONS=use_sigaltstack=0 を使用します
アロケーション失敗の警告が大量に出るASAN_OPTIONS=allocator_may_return_null=1 を使用します
LD_PRELOAD がツールに干渉するエクスポートせず、ruby コマンド内で指定します

関連項目: 詳細なサニタイザーの設定、よくある問題、上級者向けのフラグについては、 address-sanitizer と undefined-behavior-sanitizer のテクニックスキルを参照してください。

実例

例:msgpack-ruby

メモリ破損を検出するために、msgpack MessagePack パーサーをファジングします。

サニタイザーを使用してインストール:

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

ハーネス(fuzz_msgpack.rb):

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

実行:

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
例:Pure Ruby ターゲット

カスタムパーサーを使って Pure Ruby コードをファジングします。

トレーサー(test_tracer.rb):

# frozen_string_literal: true

require 'ruzzy'

Ruzzy.trace('test_harness.rb')

ハーネス(test_harness.rb):

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

実行:

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

トラブルシューティング

問題原因解決策
インストールに失敗するclang のバージョンまたはパスが正しくないclang のパスを確認し、clang 14.0.0 以降を使用します
cannot open shared object fileLD_PRELOAD が設定されていないruby コマンド内で LD_PRELOAD を指定します
ファザーがすぐに終了するコーパスディレクトリがないコーパスディレクトリを作成するか、引数として渡します
カバレッジの進捗がないPure Ruby ではトレーサーが必要Pure Ruby コードにはトレーサースクリプトを使用します
リーク検出の警告が大量に出るRuby インタープリターのリークASAN_OPTIONS=detect_leaks=0 を設定します
インストールのデバッグが必要コンパイルエラーRUZZY_DEBUG=1 gem install --verbose ruzzy を使用します

関連スキル

テクニックスキル
スキル使用例
fuzz-harness-writing効果的なハーネスの作成に関する詳しいガイダンス
address-sanitizerファジング中のメモリエラー検出
undefined-behavior-sanitizerC 拡張機能における未定義動作の検出
libfuzzerlibFuzzer のオプションを理解する(Ruzzy は libFuzzer を基盤としています)
関連ファザー
スキル検討するタイミング
libfuzzerRuby の C 拡張機能を C/C++ で直接ファジングする場合
aflppRuby インタープリターに計測を組み込んで Ruby をファジングする代替手法

リソース

主な外部リソース

Ruzzy の紹介:カバレッジガイド型 Ruby ファザー Ruzzy を発表する Trail of Bits 公式ブログ記事です。開発の動機、アーキテクチャ、初期の成果について説明しています。

Ruzzy GitHub リポジトリ ソースコード、追加のサンプル、開発手順を確認できます。

libFuzzer のドキュメント Ruzzy は libFuzzer をベースに構築されているため、libFuzzer のオプションや動作を理解しておくと役立ちます。

Ruby の C 拡張をファジングする コンパイルフラグやサンプルを含む、C 拡張のファジングに関する詳細なガイドです。

純粋な Ruby コードをファジングする 純粋な Ruby コードのファジングに必要なトレーサーパターンに関する詳細なガイドです。

コメント