mcpField
R
ruzzy

ruzzy

github.com/trailofbits测试与质量7.9k5.2k 次安装今天更新
代码仓库

关于 ruzzy

ruzzy 是基于 libFuzzer 的 Ruby 覆盖引导模糊测试工具,适用于 Ruby 应用和库。它可测试纯 Ruby 代码及 C 扩展,并通过 Sanitizer 检测内存破坏和未定义行为。

主要功能

  1. 01Ruby 覆盖引导模糊测试
  2. 02支持纯 Ruby 与 C 扩展测试框架
  3. 03支持 AddressSanitizer 和 UBSan
  4. 04传入 libFuzzer 选项和语料库

适用场景

  1. 01模糊测试 Ruby 应用或库
  2. 02检查 Ruby 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
验证

运行玩具示例以验证安装(请参阅“快速开始”部分)。

编写 Harness

对纯 Ruby 代码进行模糊测试

由于 Ruby 解释器的实现细节,纯 Ruby 模糊测试需要两个脚本。

跟踪器脚本(test_tracer.rb):

# frozen_string_literal: true

require 'ruzzy'

Ruzzy.trace('test_harness.rb')

Harness 脚本(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 扩展可使用单个 Harness 文件进行模糊测试,无需跟踪器。

msgpack 的 Harness 示例(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
Harness 规则
应做不要做
测试 C 扩展时捕获 Ruby 异常让 Ruby 异常导致模糊测试器崩溃
从 test_one_input lambda 返回 0返回其他值
保持 Harness 确定性使用随机性或基于时间的逻辑
对纯 Ruby 使用跟踪器脚本对纯 Ruby 代码跳过跟踪器

**另请参阅:**有关详细的 Harness 编写技巧、处理复杂输入的模式, 以及高级策略,请参阅 fuzz-harness-writing 技术技能。

编译

使用消毒器安装 Gems

为模糊测试安装带有 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-link启用 AddressSanitizer 和模糊测试器插桩
-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 exited发生 Ruby 异常
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 检测:

  • 有符号整数溢出
  • 空指针解引用
  • 未对齐的内存访问
  • 除以零
常见检测器问题
问题解决方案
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
示例:纯 Ruby 目标

使用自定义解析器对纯 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 file未设置 LD_PRELOAD在 ruby 命令中内联设置 LD_PRELOAD
模糊测试器立即退出缺少语料库目录创建语料库目录或将其作为参数传入
没有覆盖率进度纯 Ruby 需要跟踪器对纯 Ruby 代码使用跟踪器脚本
泄漏检测信息刷屏Ruby 解释器泄漏设置 ASAN_OPTIONS=detect_leaks=0
需要调试安装编译错误使用 RUZZY_DEBUG=1 gem install --verbose ruzzy

相关技能

技术技能
技能使用场景
fuzz-harness-writing编写有效测试桩的详细指导
address-sanitizer在模糊测试期间检测内存错误
undefined-behavior-sanitizer检测 C 扩展中的未定义行为
libfuzzer了解 libFuzzer 选项(Ruzzy 基于 libFuzzer 构建)
相关模糊测试器
技能考虑使用的时机
libfuzzer直接在 C/C++ 中对 Ruby C 扩展代码进行模糊测试时
aflpp通过插桩 Ruby 解释器对 Ruby 进行模糊测试的替代方案

资源

关键外部资源

介绍 Ruzzy:一款覆盖率引导的 Ruby 模糊测试工具 Trail of Bits 官方博客文章,宣布推出 Ruzzy,并介绍其设计动机、架构和初步成果。

Ruzzy GitHub 仓库 源代码、更多示例和开发说明。

libFuzzer 文档 由于 Ruzzy 基于 libFuzzer 构建,了解 libFuzzer 的选项和行为很有帮助。

对 Ruby C 扩展进行模糊测试 关于如何使用编译标志和示例对 C 扩展进行模糊测试的详细指南。

对纯 Ruby 代码进行模糊测试 关于纯 Ruby 模糊测试所需跟踪器模式的详细指南。

评论

支持 Markdown