atheris
Atherisは、libFuzzerをベースにしたカバレッジガイド型のPythonファジングツールです。純粋なPythonコードおよびPython C拡張モジュールに対するファジングテストに使用します。
description の原文を見る
> Atheris is a coverage-guided Python fuzzer based on libFuzzer. Use for fuzzing pure Python code and Python C extensions.
SKILL.md 本文
Atheris
Atheris は libFuzzer をベースに構築された coverage-guided Python ファザーです。AddressSanitizer が統合されており、純粋な Python コードと Python C 拡張のファジングの両方を実現でき、メモリ破損の問題検出に対応しています。
使用時期
| ファザー | 適用例 | 複雑度 |
|---|---|---|
| Atheris | Python コードと C 拡張 | 低~中 |
| Hypothesis | プロパティベーステスト | 低 |
| python-afl | AFL スタイルのファジング | 中 |
Atheris を選ぶべき場合:
- coverage ガイダンス付きで純粋な Python コードをファジングする場合
- Python C 拡張をメモリ破損でテストする場合
- libFuzzer エコシステムとの統合が必要な場合
- AddressSanitizer サポートが必要な場合
クイックスタート
import sys
import atheris
@atheris.instrument_func
def test_one_input(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, test_one_input)
atheris.Fuzz()
if __name__ == "__main__":
main()
実行:
python fuzz.py
インストール
Atheris は 32 ビットおよび 64 ビット Linux と macOS をサポートしています。Linux でのファジングをお勧めします。管理がシンプルで、多くの場合より高速です。
前提条件
- Python 3.7 以降
- 最新の clang バージョン(最新リリースを推奨)
- Docker ユーザー向け: Docker Desktop
Linux/macOS
uv pip install 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 test_one_input(data: bytes):
"""
ファジングエントリーポイント。ランダムなバイトシーケンスで呼ばれます。
Args:
data: ファザーが生成したランダムバイト
"""
# 必要に応じて入力検証を追加
if len(data) < 1:
return
# ターゲット関数を呼び出し
try:
your_target_function(data)
except ValueError:
# 予期された例外はキャッチする
pass
# 予期しない例外はクラッシュさせる(これが探すべき問題!)
def main():
atheris.Setup(sys.argv, test_one_input)
atheris.Fuzz()
if __name__ == "__main__":
main()
ハーネスルール
| する | しない |
|---|---|
coverage のために @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 test_one_input(data: bytes):
target_function(data)
atheris.Setup(sys.argv, test_one_input)
atheris.Fuzz()
インストルメンテーションオプション:
atheris.instrument_func- 単一関数インストルメンテーション用のデコレータatheris.instrument_imports()- すべてのインポートされたモジュールをインストルメント化するコンテキストマネージャatheris.instrument_all()- システム全体のすべての Python コードをインストルメント化
Python C 拡張のファジング
Python C 拡張には、インストルメンテーションと sanitizer サポート用の特定のフラグでのコンパイルが必要です。
環境設定
提供されている 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 python -m pip install --no-binary cbor2 cbor2==5.6.4
--no-binary フラグはインストルメンテーション付きで C 拡張がローカルでコンパイルされるようにします。
cbor2-fuzz.py を作成:
import sys
import atheris
# _cbor2 は C ライブラリがインポートされるようにします
from _cbor2 import loads
def test_one_input(data: bytes):
try:
loads(data)
except Exception:
# メモリ破損を探しており、Python 例外ではありません
pass
def main():
atheris.Setup(sys.argv, test_one_input)
atheris.Fuzz()
if __name__ == "__main__":
main()
実行:
python cbor2-fuzz.py
重要: ローカルで実行する場合(Docker ではなく)、
LD_PRELOADを手動で設定する必要があります。
コーパス管理
初期コーパスの作成
mkdir corpus
# シードの入力を追加
echo "test data" > corpus/seed1
echo '{"key": "value"}' > corpus/seed2
コーパスで実行:
python fuzz.py corpus/
コーパス最小化
Atheris は libFuzzer からコーパス最小化を継承しています:
python fuzz.py -merge=1 new_corpus/ old_corpus/
関連情報: コーパス作成戦略、辞書、シード選択については、 fuzzing-corpus テクニックスキルをご覧ください。
キャンペーンの実行
基本的な実行
python fuzz.py
コーパスディレクトリ付き
python fuzz.py corpus/
一般的なオプション
# 10 分間実行
python fuzz.py -max_total_time=600
# 入力サイズを制限
python fuzz.py -max_len=1024
# 複数のワーカーで実行
python fuzz.py -workers=4 -jobs=4
出力の解釈
| 出力 | 意味 |
|---|---|
NEW cov: X | 新しいカバレッジを検出、コーパスを拡張 |
pulse cov: X | 定期的なステータスアップデート |
exec/s: X | 1 秒あたりの実行数(スループット) |
corp: X/Yb | コーパスサイズ: X 入力、Y バイト合計 |
ERROR: libFuzzer | クラッシュを検出 |
Sanitizer 統合
AddressSanitizer (ASan)
AddressSanitizer は、提供されている Docker 環境を使用するか、適切なフラグでコンパイルする場合に自動的に統合されます。
ローカルセットアップの場合:
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"))')"
関連情報: 詳細な sanitizer 設定、一般的な問題、高度なフラグについては、 address-sanitizer と undefined-behavior-sanitizer テクニックスキルをご覧ください。
一般的な 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() を早期に使用する | すべてのインポートが coverage 用にインストルメント化されることを確保 |
小さい max_len で開始する | 初期ファジングが高速、段階的に増加 |
| 構造化フォーマットに辞書を使用する | ファザーはフォーマットトークンを理解するのに役立つ |
| 複数の並列インスタンスを実行する | より良い coverage 探索 |
カスタムインストルメンテーション
インストルメント化対象を微調整:
import atheris
# 特定のモジュールのみをインストルメント化
with atheris.instrument_imports():
import target_module
# テストハーネスコードはインストルメント化しない
def test_one_input(data: bytes):
target_module.parse(data)
パフォーマンスチューニング
| 設定 | 影響 |
|---|---|
-max_len=N | より小さい値 = より高速な実行 |
-workers=N -jobs=N | 並列ファジングでより高速な coverage |
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 パーサー
import sys
import atheris
import json
@atheris.instrument_func
def test_one_input(data: bytes):
try:
# Python の JSON パーサーをファジング
json.loads(data.decode('utf-8', errors='ignore'))
except (ValueError, UnicodeDecodeError):
pass
def main():
atheris.Setup(sys.argv, test_one_input)
atheris.Fuzz()
if __name__ == "__main__":
main()
例: HTTP リクエスト解析
import sys
import atheris
with atheris.instrument_imports():
from urllib3 import HTTPResponse
from io import BytesIO
def test_one_input(data: bytes):
try:
# HTTP レスポンス解析をファジング
fake_response = HTTPResponse(
body=BytesIO(data),
headers={},
preload_content=False
)
fake_response.read()
except Exception:
pass
def main():
atheris.Setup(sys.argv, test_one_input)
atheris.Fuzz()
if __name__ == "__main__":
main()
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
| カバレッジ増加なし | シードコーパスが悪い、またはターゲットがインストルメント化されていない | より良いシード追加、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-sanitizer | C 拡張で未定義の動作をキャッチ |
| coverage-analysis | コードカバレッジの測定と改善 |
| fuzzing-corpus | シードコーパスのビルドと管理 |
関連ファザー
| スキル | 検討時期 |
|---|---|
| hypothesis | 型対応生成によるプロパティベーステスト |
| python-afl | Atheris が利用できない場合の AFL スタイルのファジング |
リソース
主要な外部リソース
Atheris GitHub リポジトリ インストール手順、例、純粋な Python とネイティブ拡張の両方のファジングドキュメントを含むオフィシャルリポジトリ。
ネイティブ拡張ファジングガイド コンパイルフラグ、LD_PRELOAD セットアップ、sanitizer 設定、Python C 拡張のトラブルシューティングをカバーする包括的なガイド。
Python C 拡張の継続的なファジング CI/CD 統合、ClusterFuzzLite セットアップ、継続的統合パイプラインで Python C 拡張をファジングする実世界の例をカバーする Trail of Bits のブログ記事。
ClusterFuzzLite Python 統合 ClusterFuzzLite を使用した自動継続的ファジング用に Atheris ファジングを CI/CD パイプラインに統合するためのガイド。
ビデオリソース
ビデオチュートリアルは Atheris のメインドキュメントと libFuzzer リソースで利用可能です。
ライセンス: CC-BY-SA-4.0(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- trailofbits
- リポジトリ
- trailofbits/skills
- ライセンス
- CC-BY-SA-4.0
- 最終更新
- 不明
Source: https://github.com/trailofbits/skills / ライセンス: CC-BY-SA-4.0
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。