fuzzing について
libFuzzerやAFL++などを使ったカバレッジ誘導型ファジングの設定と実行をエージェントに案内するスキルです。ターゲット作成、サニタイザー、クラッシュ再現、コーパス管理、CIを扱い、堅牢性を検証する開発者向けです。
主な機能
- 01libFuzzerとAFL++の設定
- 02ファズターゲットの作成とビルド
- 03サニタイザー連携
- 04クラッシュ再現とコーパス管理
- 05CIとOSS-Fuzzの案内
- 06構造認識型ファジングとデータフロー追跡
活用シーン
- 01パーサーやデシリアライザーのファジング
- 02ファザーが検出したクラッシュの再現とデバッグ
- 03CIで短時間のファジングを実行
- 04テストコーパスの統合や最小化
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill fuzzingインストール後、Claude Code で/fuzzingを入力して読み込まれたか確認します。
同梱ファイルreferences/targets.md
詳細
ファジング
目的
カバレッジガイド付きファズテストの設定と実行について、エージェントをガイドします。サニタイザーとの統合と CI パイプラインの設定を含む、libFuzzer(プロセス内)と AFL++(フォークベース)を扱います。
トリガー
- 「パーサー/デシリアライザーをファズテストするにはどうすればよいですか?」
- 「ファズターゲットとは何ですか? どのように記述しますか?」
- 「libFuzzer を設定するにはどうすればよいですか?」
- 「プログラムで AFL++ を使うにはどうすればよいですか?」
- 「CI でファジングを実行するにはどうすればよいですか?」
- 「ファザーがクラッシュを見つけました。再現するにはどうすればよいですか?」
ワークフロー
1. ファズターゲットを記述する(libFuzzer)
ファズターゲットは、任意のバイト列を受け取り、テスト対象のコードを実行する関数です。
// fuzz_parser.c
#include <stdint.h>
#include <stddef.h>
#include "myparser.h"
// Entry point called by libFuzzer with random data
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// Must not abort/exit on invalid input (that's expected)
// Must not read outside [data, data+size)
MyParser *p = parser_create();
if (p) {
parser_feed(p, (const char *)data, size);
parser_destroy(p);
}
return 0; // Always return 0 (non-zero means discard input)
}
主なルール:
abort()、exit()を呼び出したり、呼び出し間で保持されるグローバル状態を使用したりしないでください- すべての入力を適切に処理してください(クラッシュはバグが見つかったことを意味します)
- ターゲットは高速に保ってください。ファザーはこれを何百万回も呼び出します
2. libFuzzer を使ってビルドする
# Clang (libFuzzer is built into Clang)
clang -fsanitize=fuzzer,address -g -O1 \
fuzz_parser.c myparser.c -o fuzz_parser
# With UBSan too
clang -fsanitize=fuzzer,address,undefined -g -O1 \
fuzz_parser.c myparser.c -o fuzz_parser
-fsanitize=fuzzer は libFuzzer をリンクし、main() を提供します。ファズターゲットに独自の main() を定義しないでください。
3. libFuzzer を実行する
# Create corpus directory
mkdir -p corpus
# Seed with known-good inputs (greatly accelerates coverage)
cp tests/inputs/* corpus/
# Run the fuzzer
./fuzz_parser corpus/ -max_len=65536 -timeout=10
# Run for a time limit
./fuzz_parser corpus/ -max_total_time=3600
# Run with specific number of jobs (parallel)
./fuzz_parser corpus/ -jobs=4 -workers=4
# Minimise a corpus (remove redundant inputs)
./fuzz_parser -merge=1 corpus_min/ corpus/
一般的なフラグ:
| フラグ | デフォルト | 効果 |
|---|---|---|
-max_len=N | 4096 | 最大入力サイズ(バイト単位) |
-timeout=N | 1200 | 1 回の実行に N 秒を超えた場合に停止 |
-max_total_time=N | 0(無期限) | ファジングの合計時間 |
-runs=N | -1(無制限) | 実行回数の合計 |
-dict=file | なし | 興味深いトークンの辞書 |
-jobs=N | 1 | 並列ジョブ数(各ジョブは独自のログを書き込みます) |
-merge=1 | オフ | マージモード:コーパスを最小化 |
4. クラッシュを再現する
libFuzzer は、クラッシュ入力を crash-<hash>、oom-<hash>、timeout-<hash> という名前のファイルに書き込みます。
# Reproduce
./fuzz_parser crash-abc123
# Debug with GDB
gdb ./fuzz_parser
(gdb) run crash-abc123
5. AFL++ の設定
AFL++ はフォークベースのファザーで、ファズ用のエントリポイントがあるプログラムに限らず、任意のプログラムで動作します。
# Install
apt install afl++ # or build from source
# Instrument the target
CC=afl-clang-fast CXX=afl-clang-fast++ \
cmake -S . -B build-afl -DCMAKE_BUILD_TYPE=Debug
cmake --build build-afl
# Or compile directly
afl-clang-fast -g -O1 -o prog_afl main.c myparser.c
# Create input corpus
mkdir -p afl-input afl-output
echo "hello" > afl-input/seed1
# Run
afl-fuzz -i afl-input -o afl-output -- ./prog_afl @@
# @@ is replaced with the input file path
# For stdin-based programs: remove @@
afl-fuzz -i afl-input -o afl-output -- ./prog_afl
6. 永続モードで AFL++ を使う(高速化)
永続モードでは入力ごとの fork() を回避できるため、ライブラリのファジングが大幅に高速になります。
// In your harness:
#include "myparser.h"
int main(int argc, char **argv) {
while (__AFL_LOOP(1000)) {
// Read input
unsigned char *buf = NULL;
ssize_t len = read(0, &buf, MAX_SIZE); // or use afl_custom_mutator
parser_feed((char*)buf, len);
free(buf);
}
return 0;
}
7. コーパスの管理
# AFL++ corpus minimisation
afl-cmin -i afl-output/default/queue -o corpus_min -- ./prog_afl @@
# Merge libFuzzer corpora from multiple runs
./fuzz_parser -merge=1 merged_corpus/ run1_corpus/ run2_corpus/
# Show coverage (libFuzzer)
./fuzz_parser corpus/ -runs=0 -print_coverage=1
8. CI との統合
# GitHub Actions example
- name: Build fuzz targets
run: |
clang -fsanitize=fuzzer,address,undefined -g -O1 \
fuzz_parser.c myparser.c -o fuzz_parser
- name: Short fuzz run (regression check)
run: |
./fuzz_parser corpus/ -max_total_time=60 -error_exitcode=1
# Also run known crash inputs if any:
ls known_crashes/ 2>/dev/null | xargs -I{} ./fuzz_parser known_crashes/{}
長時間のファジングには、OSS-Fuzz または ClusterFuzz のインフラストラクチャを使用してください。
9. 構造を認識したファジング(libFuzzer)
// Custom mutator hook — preserve format invariants
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed) {
// Delegate to default mutator then fix up structure
Size = LLVMFuzzerMutate(Data, Size, MaxSize);
if (Size >= 4)
fix_checksum(Data, Size);
return Size;
}
// Register custom crossover for structured inputs
extern "C" size_t LLVMFuzzerCustomCrossOver(
const uint8_t *Data1, size_t Size1,
const uint8_t *Data2, size_t Size2,
uint8_t *Out, size_t MaxOutSize, unsigned int Seed);
単純なビット反転では、深いコードパスに到達する前にチェックサムやヘッダーが壊れてしまう場合に使用します。
10. Atheris(Python のファジング)
import atheris
import sys
with atheris.instrument_imports():
import myparser
def TestOneInput(data: bytes) -> None:
fdp = atheris.FuzzedDataProvider(data)
try:
myparser.parse(fdp.ConsumeString(sys.maxsize))
except myparser.ParseError:
pass
if __name__ == "__main__":
atheris.Setup(sys.argv, TestOneInput)
atheris.Fuzz()
pip install atheris
python fuzz_myparser.py corpus/ -max_total_time=300
11. データフローのトレース
# Track tainted bytes through execution (LLVM dataflow sanitizer + libFuzzer)
clang -fsanitize=fuzzer,dataflow -g -O1 fuzz.c target.c -o fuzz
LIBFUZZER_DATAFLOW_TRACE=1 ./fuzz corpus/
入力バイトがどの分岐に影響したかを示すトレースを生成し、辞書や構造を認識したミューテーターの作成に役立てます。
12. OSS-Fuzz との統合
OSS-Fuzz workflow
├── Add project/ in google/oss-fuzz repo (Dockerfile + build.sh)
├── Fuzz targets linked with -fsanitize=fuzzer,address
├── ClusterFuzz runs continuously on GCE
└── Crash reproducers uploaded to issue tracker
# project/Dockerfile (minimal)
FROM gcr.io/oss-fuzz-base/base-builder
RUN git clone --depth 1 https://github.com/you/yourproject
WORKDIR yourproject
COPY build.sh $SRC/
# Local OSS-Fuzz repro
python infra/helper.py build_image yourproject
python infra/helper.py build_fuzzers yourproject
python infra/helper.py run_fuzzer yourproject fuzz_target
13. Zig のファズテスト
# Zig 0.11+ built-in fuzzing
zig build test --fuzz
# fuzz target in build.zig
# .root_module.fuzz_tests = &.{
# .{ .name = "parser", .path = "src/fuzz/parser.zig" },
# };
Zig のファジングは、ネイティブターゲット向けの zig test およびサニタイザービルドと統合されています。
14. 辞書ファイル
辞書には、ミューテーションを誘導するための興味深いトークンが含まれます。
# parser.dict
kw1="<"
kw2=">"
kw3="</"
kw4='="'
kw5="\x00"
kw6="\xff\xfe"
./fuzz_parser corpus/ -dict=parser.dict
参考資料
ファズターゲットのテンプレート、コーパスのシード例、OSS-Fuzz の統合ガイダンスについては、references/targets.md を参照してください。
関連スキル
- ASan/UBSan をファズビルドに追加するには、
skills/runtimes/sanitizersを使用してください - Clang 固有の libFuzzer フラグについては、
skills/compilers/clangを使用してください - ファザーが見つけたクラッシュ入力のデバッグには、
skills/debuggers/gdbを使用してください - Zig の
build test --fuzzワークフローには、skills/zig/zig-testingを使用してください - syzkaller によるカーネルファジングには、
skills/security/kernel-securityを使用してください