mcpField
A
atheris

atheris

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

atheris について

AtherisはlibFuzzerを基盤とする、カバレッジガイド型のPythonファザーです。純粋なPythonコードとPython C拡張を対象とし、AddressSanitizerにも対応してメモリ破損の検出を支援するため、ライブラリやネイティブ拡張をテストする開発者に適しています。

主な機能

  1. 01Pythonコードのカバレッジガイド型ファジング
  2. 02Python C拡張のファジング
  3. 03AddressSanitizerとの統合
  4. 04FuzzedDataProviderによる構造化入力生成
  5. 05コーパスの管理と最小化

活用シーン

  1. 01Python関数やライブラリのファジング
  2. 02C拡張のメモリエラー検査
  3. 03シードコーパスを用いたパーサーのテスト

インストール

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

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

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

examples.md
structured-input.md
agents/
openai.yaml
assets/
trail-of-bits-mark.svg

詳細

Atheris

Atheris は libFuzzer を基盤とする、カバレッジガイド型の Python ファザーです。純粋な Python コードと Python C 拡張の両方をファジングでき、AddressSanitizer が統合されているため、メモリ破損の問題を検出できます。

使用する場面

ファザー最適な用途複雑さ
AtherisPython コードと C 拡張低~中
Hypothesisプロパティベーステスト低
python-aflAFL スタイルのファジング中

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

  • カバレッジガイドを使って純粋な Python コードをファジングする場合
  • メモリ破損の有無を調べるために Python C 拡張をテストする場合
  • libFuzzer エコシステムとの統合が必要な場合
  • AddressSanitizer のサポートが必要な場合

クイックスタート

import sys
import atheris

@atheris.instrument_func
def TestOneInput(data: bytes):
    if len(data) == 4:
        if data[0] == 0x46:  # "F"
            if data[1] == 0x55:  # "U"
                if data[2] == 0x5A:  # "Z"
                    if data[3] == 0x5A:  # "Z"
                        raise RuntimeError("You caught me")

def main():
    atheris.Setup(sys.argv, TestOneInput)
    atheris.Fuzz()

if __name__ == "__main__":
    main()

実行:

uv run python fuzz.py

インストール

Atheris は 32 ビットおよび 64 ビットの Linux と macOS をサポートしています。管理が簡単で、多くの場合より高速な Linux でのファジングをおすすめします。

前提条件
Linux/macOS
uv init --bare   # once, if the harness directory is not yet a uv project
uv add atheris
Docker 環境(推奨)

すべての依存関係が設定された、すぐに使用できる Linux 環境を用意するには:

# https://hub.docker.com/_/python
ARG PYTHON_VERSION=3.11

FROM python:$PYTHON_VERSION-slim-bookworm

RUN python --version

RUN apt update && apt install -y \
    ca-certificates \
    wget \
    && rm -rf /var/lib/apt/lists/*

# LLVM builds version 15-19 for Debian 12 (Bookworm)
# https://apt.llvm.org/bookworm/dists/
ARG LLVM_VERSION=19

RUN echo "deb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-$LLVM_VERSION main" > /etc/apt/sources.list.d/llvm.list
RUN echo "deb-src http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-$LLVM_VERSION main" >> /etc/apt/sources.list.d/llvm.list
RUN wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key > /etc/apt/trusted.gpg.d/apt.llvm.org.asc

RUN apt update && apt install -y \
    build-essential \
    clang-$LLVM_VERSION \
    && rm -rf /var/lib/apt/lists/*

ENV APP_DIR "/app"
RUN mkdir $APP_DIR
WORKDIR $APP_DIR

ENV VIRTUAL_ENV "/opt/venv"
RUN python -m venv $VIRTUAL_ENV
ENV PATH "$VIRTUAL_ENV/bin:$PATH"

# https://github.com/google/atheris/blob/master/native_extension_fuzzing.md#step-1-compiling-your-extension
ENV CC="clang-$LLVM_VERSION"
ENV CFLAGS "-fsanitize=address,fuzzer-no-link"
ENV CXX="clang++-$LLVM_VERSION"
ENV CXXFLAGS "-fsanitize=address,fuzzer-no-link"
ENV LDSHARED="clang-$LLVM_VERSION -shared"
ENV LDSHAREDXX="clang++-$LLVM_VERSION -shared"
ENV ASAN_SYMBOLIZER_PATH="/usr/bin/llvm-symbolizer-$LLVM_VERSION"

# Allow Atheris to find fuzzer sanitizer shared libs
# https://github.com/google/atheris#building-from-source
RUN LIBFUZZER_LIB=$($CC -print-file-name=libclang_rt.fuzzer_no_main-$(uname -m).a) \
    python -m pip install --no-binary atheris atheris

# https://github.com/google/atheris/blob/master/native_extension_fuzzing.md#option-a-sanitizerlibfuzzer-preloads
ENV LD_PRELOAD "$VIRTUAL_ENV/lib/python3.11/site-packages/asan_with_fuzzer.so"

# 1. Skip memory allocation failures for now, they are common, and low impact (DoS)
# 2. https://github.com/google/atheris/blob/master/native_extension_fuzzing.md#leak-detection
ENV ASAN_OPTIONS "allocator_may_return_null=1,detect_leaks=0"

CMD ["/bin/bash"]

ビルドして実行:

docker build -t atheris .
docker run -it atheris
動作確認
python -c "import atheris; print(atheris.__version__)"

ハーネスの作成

純粋な Python 用のハーネス構造
import sys
import atheris

@atheris.instrument_func
def TestOneInput(data: bytes):
    """
    Fuzzing entry point. Called with random byte sequences.

    Args:
        data: Random bytes generated by the fuzzer
    """
    # Add input validation if needed
    if len(data) < 1:
        return

    # Call your target function
    try:
        your_target_function(data)
    except ValueError:
        # Expected exceptions should be caught
        pass
    # Let unexpected exceptions crash (that's what we're looking for!)

def main():
    atheris.Setup(sys.argv, TestOneInput)
    atheris.Fuzz()

if __name__ == "__main__":
    main()
FuzzedDataProvider による構造化入力

複数の型付き引数を受け取るターゲットで、ハーネスが data を手作業でスライスすると、ファザーの入力の大半が無駄になります。ミューテーションのたびに、それ以降のすべてのバイトオフセットがずれるためです。atheris.FuzzedDataProvider を使うと、1 つの bytes 入力を型付きの値に分割できます。

fdp = atheris.FuzzedDataProvider(data)
name = fdp.ConsumeUnicodeNoSurrogates(fdp.ConsumeIntInRange(0, 64))
strict = fdp.ConsumeBool()

すべてのメソッドのリファレンス、固定された読み取り順序のルール、バッファが空になったときに各メソッドが返す値については、structured-input.md を参照してください。

ハーネスのルール
することしないこと
カバレッジ計測には @atheris.instrument_func を使うターゲットコードの計測を忘れる
想定される例外を捕捉する無差別にすべての例外を捕捉する
ライブラリには atheris.instrument_imports() を使うatheris.Setup() の後にモジュールをインポートする
ハーネスの動作を決定的に保つ乱数や時刻に依存する動作を使う

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

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

アプリケーションやライブラリのより広い範囲をファジングするには、計測関数を使います。

import atheris
with atheris.instrument_imports():
    import your_module
    from another_module import target_function

def TestOneInput(data: bytes):
    target_function(data)

atheris.Setup(sys.argv, TestOneInput)
atheris.Fuzz()

計測オプション:

  • atheris.instrument_func - 単一の関数を計測するデコレーター
  • atheris.instrument_imports() - インポートされるすべてのモジュールを計測するコンテキストマネージャー
  • atheris.instrument_all() - システム全体のすべての Python コードを計測

Python C 拡張のファジング

Python C 拡張の計測とサニタイザーのサポートには、特定のフラグを使ったコンパイルが必要です。

環境設定

付属の Dockerfile を使う場合、これらはすでに設定されています。ローカル環境で設定する場合:

export CC="clang"
export CFLAGS="-fsanitize=address,fuzzer-no-link"
export CXX="clang++"
export CXXFLAGS="-fsanitize=address,fuzzer-no-link"
export LDSHARED="clang -shared"
例:cbor2 のファジング

ソースから拡張機能をインストールします:

CBOR2_BUILD_C_EXTENSION=1 uv add --no-binary-package cbor2 'cbor2==5.6.4'

--no-binary-package フラグを指定すると、C 拡張がビルド済みの wheel から取得されず、計測を有効にしてローカルでコンパイルされます。この設定を維持するには、pyproject.toml の [tool.uv] の下に no-binary-package = ["cbor2"] を設定してください。そうしないと、後続の uv sync により、計測されていない wheel に気付かないまま置き換えられることがあります。

cbor2-fuzz.py を作成します:

import sys
import atheris

# _cbor2 ensures the C library is imported
from _cbor2 import loads

def TestOneInput(data: bytes):
    try:
        loads(data)
    except Exception:
        # We're searching for memory corruption, not Python exceptions
        pass

def main():
    atheris.Setup(sys.argv, TestOneInput)
    atheris.Fuzz()

if __name__ == "__main__":
    main()

実行:

uv run python cbor2-fuzz.py

重要: ローカルで実行する場合(Docker 内ではない場合)は、LD_PRELOAD を手動で設定する必要があります。

コーパスの管理

初期コーパスの作成
mkdir corpus
# Add seed inputs
echo "test data" > corpus/seed1
echo '{"key": "value"}' > corpus/seed2

コーパスを指定して実行:

uv run python fuzz.py corpus/
コーパスの最小化

Atheris は libFuzzer のコーパス最小化機能を引き継いでいます:

uv run python fuzz.py -merge=1 new_corpus/ old_corpus/

関連項目: コーパスの作成方法、辞書、シードの選定については、fuzzing-corpus 技術スキルを参照してください。

キャンペーンの実行

基本的な実行
uv run python fuzz.py
コーパスディレクトリを指定して実行
uv run python fuzz.py corpus/
よく使うオプション
# Run for 10 minutes
uv run python fuzz.py -max_total_time=600

# Limit input size
uv run python fuzz.py -max_len=1024

# Run with multiple workers
uv run python fuzz.py -workers=4 -jobs=4
出力の読み方
出力意味
NEW cov: X新しいカバレッジが見つかり、コーパスが拡張されました
pulse cov: X定期的なステータス更新
exec/s: X1 秒あたりの実行数(スループット)
corp: X/Ybコーパスのサイズ:入力 X 件、合計 Y バイト
ERROR: libFuzzerクラッシュが検出されました

サニタイザーの統合

AddressSanitizer(ASan)

付属の Docker 環境を使用する場合、または適切なフラグを指定してコンパイルする場合、AddressSanitizer は自動的に統合されます。

ローカル環境を設定するには:

export CFLAGS="-fsanitize=address,fuzzer-no-link"
export CXXFLAGS="-fsanitize=address,fuzzer-no-link"

ASan の動作を設定します:

export ASAN_OPTIONS="allocator_may_return_null=1,detect_leaks=0"
LD_PRELOAD の設定

ネイティブ拡張のファジングでは:

export LD_PRELOAD="$(python -c 'import atheris; import os; print(os.path.join(os.path.dirname(atheris.__file__), "asan_with_fuzzer.so"))')"

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

よくあるサニタイザーの問題
問題解決策
LD_PRELOAD が設定されていないLD_PRELOAD をエクスポートし、asan_with_fuzzer.so を指定します
メモリ割り当ての失敗ASAN_OPTIONS=allocator_may_return_null=1 を設定します
リーク検出によるノイズASAN_OPTIONS=detect_leaks=0 を設定します
シンボライザーが見つからないASAN_SYMBOLIZER_PATH に llvm-symbolizer を設定します

高度な使い方

ヒントとコツ
ヒント効果
atheris.instrument_imports() を早い段階で使用するすべてのインポートがカバレッジ計測用にインストルメントされます
小さな max_len から始める最初のファジングが速くなり、段階的に増やせます
構造化形式には辞書を使用するファザーが形式のトークンを理解しやすくなります
複数のインスタンスを並行して実行するカバレッジの探索が向上します
カスタムインストルメンテーション

インストルメントする対象を細かく調整します:

import atheris

# Instrument only specific modules
with atheris.instrument_imports():
    import target_module
# Don't instrument test harness code

def TestOneInput(data: bytes):
    target_module.parse(data)
パフォーマンスの調整
設定影響
-max_len=N値が小さいほど実行が速くなります
-workers=N -jobs=N並列ファジングにより、カバレッジの獲得が速くなります
ASAN_OPTIONS=fast_unwind_on_malloc=0スタックトレースの精度が向上しますが、実行は遅くなります
UndefinedBehaviorSanitizer(UBSan)

UBSan を追加して、さらに多くのバグを検出します:

export CFLAGS="-fsanitize=address,undefined,fuzzer-no-link"
export CXXFLAGS="-fsanitize=address,undefined,fuzzer-no-link"

注:コンテナ化された環境を使用する場合は、Dockerfile 内のフラグを変更してください。

実際の使用例

純粋な Python パーサーと HTTP レスポンスパーサーの、完全なハーネスが 2 つ、 examples.md にあります。

トラブルシューティング

問題原因解決策
カバレッジが増加しないシードコーパスの質が低い、またはターゲットがインストルメントされていないより良いシードを追加し、instrument_imports() を確認します
実行が遅いASan のオーバーヘッド、または入力が大きいmax_len を小さくし、ASAN_OPTIONS=fast_unwind_on_malloc=1 を使用します
インポートエラーインストルメンテーションより前にモジュールがインポートされたインポートを instrument_imports() コンテキスト内に移動します
ASan の出力なしでセグメンテーション違反が発生するLD_PRELOAD がないLD_PRELOAD に asan_with_fuzzer.so のパスを設定します
ビルドに失敗するコンパイラーが間違っている、またはフラグが不足しているCC、CFLAGS、および clang のバージョンを確認します

関連スキル

テクニックスキル
スキル用途
fuzz-harness-writing効果的なハーネスの作成に関する詳細なガイダンス
address-sanitizerファジング中のメモリエラー検出
undefined-behavior-sanitizerC 拡張における未定義動作の検出
coverage-analysisコードカバレッジの測定と改善
fuzzing-corpusシードコーパスの構築と管理
関連ファザー
スキル検討するタイミング
hypothesis型を考慮した生成によるプロパティベーステスト
python-aflAtheris が利用できない場合の、Python 向け AFL 形式のファジング

リソース

主な外部リソース

Atheris GitHub リポジトリ 純粋な Python とネイティブ拡張の両方をファジングするための、インストール手順、サンプル、ドキュメントを掲載した公式リポジトリです。

ネイティブ拡張のファジングガイド Python C 拡張向けのコンパイルフラグ、LD_PRELOAD の設定、サニタイザーの構成、トラブルシューティングを網羅した包括的なガイドです。

Python C 拡張を継続的にファジングする CI/CD との統合、ClusterFuzzLite のセットアップ、継続的インテグレーションパイプラインで Python C 拡張をファジングする実例を紹介する、Trail of Bits のブログ記事です。

ClusterFuzzLite の Python 統合 ClusterFuzzLite を使用して Atheris のファジングを CI/CD パイプラインに統合し、ファジングを自動化するためのガイドです。

動画リソース

動画やチュートリアルは、Atheris のメインドキュメントおよび libFuzzer のリソースでご覧いただけます。

コメント