
libFuzzer ターゲットと互換性のある分散カバレッジガイドファジングエンジン。数千の並行ジョブにスケーリングし、サニタイザーとコーパス蒸留を活用してバグを発見します。
なぜかって? 私たちは現在、libFuzzer や AFL などでは必ずしもうまくスケールしない、非常に大規模で非常に遅いターゲットをファジングしようとしているからです。動機の一例については SiliFuzz を参照してください。Centipede に取り組みながら、既存のファジングエンジンが試みていない、大規模な差分ファジングへの新しいアプローチを実験する予定です。
主な特徴:
libFuzzer ベースのファズターゲットをそのままサポート。お気に入りの LLVMFuzzerTestOneInput() を使用するには、Centipede のコンパイラおよびリンカオプションでターゲットをビルドするだけで済みます。
開発中(Work-in-progress)。私たちは小規模なチームで数個のターゲットについて Centipede をテストしています。Centipede プロジェクトのメンバーでない限り、または手伝いたいと思わない限り、今はまだ先を読む必要はないでしょう。
スケール。任意の数のジョブを、ごくわずかな通信オーバーヘッドで並行実行できることを意図しています。現在はローカルで100ジョブ、クラスタ上で1万ジョブをテストしています。
アウトオブプロセス。ターゲットは別のプロセスで実行されます。ターゲット内でクラッシュが発生しても、ファザーには影響しません。Centipede はインプロセスでも使用できますが、このモードは主な目的ではありません。ターゲットが小さく高速な場合は、やはり libFuzzer を使いたいと思うでしょう。
サニタイザとの統合は、個別のビルドによって実現されます。ファジング中に ASAN、MSAN、または TSAN でバグを発見したい場合は、各サニタイザ用の個別バイナリに加えて、Centipede 自体用のメインバイナリを1つ用意する必要があります。メインバイナリはどのサニタイザも使用しないでください。
内部インターフェースのどの部分も安定していません。この段階では何もかも変更される可能性があります。
ターゲットへの入力の無限ストリームを生成し、実行を統括するプログラム。
バイナリ、ライブラリ、API、あるいはむしろ、入力としてバイト列を消費し、出力としていくつかのカバレッジデータを生成できるものであれば何でも該当します。libFuzzer のターゲットは Centipede のターゲットにもなり得ます。詳細はこちらをお読みください。
ターゲットに与えることのできるバイト列。入力は任意のバイトの集まりでも、例えばシリアライズされた proto のような構造化データでも構いません。
ターゲットの何らかの固有の挙動を表す数値。例えば、フィーチャー 1234567 は、ターゲット内の基本ブロック番号 987 が7回実行されたという事実を表すかもしれません。ファザーは、入力をターゲットで実行するとき、実行中に観測されたフィーチャーを収集します。
特定の1つの入力に関連付けられたフィーチャーの集合。
特定の入力を実行したときのターゲットの挙動に関する何らかの情報。カバレッジは通常、その入力がターゲット内でトリガーしたフィーチャーセットとして表されます。
バイト列を入力として受け取り、その入力に小さなランダムな変異を加えたものを出力する関数。関連項目: 構造認識ファジング。
入力をターゲットに与え、その見返りとしてカバレッジを取得する方法を知っている関数(すなわち、実行する関数)。
ユーザーがミューテータとエグゼキュータを差し替えることを可能にする、カスタマイズ可能なファジングエンジン。
Centipede ファザーが期待するエグゼキュータインターフェースを実装するライブラリ。ランナーは、sancov で計測されたターゲットを実行し、結果のカバレッジを収集して Centipede に渡す方法を知っています。Centipede での使用を予定しているファズターゲットは、このライブラリとリンクすることで Centipede から実行可能になります。
入力の集合。
より大きなコーパスからサブセットを選び出すプロセスで、そのサブセットが元のコーパスと同じカバレッジフィーチャーを持つようにします。
コーパスのサブセットを表すファイルと、その同じコーパスサブセットのフィーチャーセットを表すもう1つのファイル。
シャード B をシャード A にマージするとは、シャード A にないフィーチャーを持つシャード B 内のすべての入力について、その入力を A に追加することを意味します。
単一のファザープロセス。1つのジョブは1つのシャードにのみ書き込みますが、複数のシャードを読み取ることはできます。
ファザーが生成または消費するデータを含む、ローカルまたはリモートのディレクトリ。
git clone https://github.com/google/centipede.git
cd centipede
CENTIPEDE_SRC=`pwd`
BIN_DIR=$CENTIPEDE_SRC/bazel-bin
bazel build -c opt :all
以降の手順で必要になるもの:
$BIN_DIR/centipede - エンジン(ファザー)のバイナリ。$BIN_DIR/libcentipede_runner.pic.a - ファズターゲットとリンクする必要があるライブラリ(ランナー)。$CENTIPEDE_SRC/clang-flags.txt - ターゲット用に推奨される clang コンパイルフラグ。これらのファイルはそのままの場所に置いておくことも、どこかにコピーすることもできます。
ターゲットのビルド例を2つ提供しています。1つは小さな単一ファイルのターゲット、もう1つは libpng です。ターゲットをビルドしたら、ファズターゲットの実行手順に進んでください。
この例では、Centipede リポジトリに含まれるシンプルなサンプルファズターゲットの1つ(別名 パズル)を使用します。
注: 以下のコマンドは $CENTIPEDE_SRC/clang-flags.txt のフラグを使用します。他の計測フラグのセットを選択しても構いません。clang-flags.txt は単純なデフォルトオプションを提供するだけです。
FUZZ_TARGET=byte_cmp_4 # or any other source under $CENTIPEDE_SRC/puzzles
clang++ @$CENTIPEDE_SRC/clang-flags.txt -c $CENTIPEDE_SRC/puzzles/$FUZZ_TARGET.cc -o $BIN_DIR/$FUZZ_TARGET.o
この手順では、ビルドしたばかりのファズターゲットを libcentipede_runner.pic.a およびその他の必要なライブラリとリンクします。
clang++ $BIN_DIR/$FUZZ_TARGET.o $BIN_DIR/libcentipede_runner.pic.a \
-ldl -lrt -lpthread -o $BIN_DIR/$FUZZ_TARGET
実行手順に進んでください。
LIBPNG_BRANCH=v1.6.37 # You can experiment with other branches if you'd like
git clone --branch $LIBPNG_BRANCH --single-branch https://github.com/glennrp/libpng.git
cd libpng
CC=clang CFLAGS=@$CENTIPEDE_SRC/clang-flags.txt ./configure --disable-shared
make -j
FUZZ_TARGET=libpng_read_fuzzer
clang++ -include cstdlib \
./contrib/oss-fuzz/$FUZZ_TARGET.cc \
./.libs/libpng16.a \
$BIN_DIR/libcentipede_runner.pic.a \
-ldl -lrt -lpthread -lz \
-o $BIN_DIR/$FUZZ_TARGET
ローカルでの実行では完全なスケールは得られませんが、ファザー開発段階では役立つかもしれません。ネットワークファイルシステムに負荷をかけないよう、実行前にファザーとターゲットの両方をローカルディレクトリにコピーすることをお勧めします。
WD=$HOME/centipede_run
mkdir -p $WD
注: Centipede の一部の機能を動作させるには、llvm-symbolizer を $PATH に追加する必要がある場合があります。シンボライザは LLVM ディストリビューションの一部としてインストールできます:
sudo apt install llvm
which llvm-symbolizer # normally /usr/bin/llvm-symbolizer
rm -rf $WD/*
$BIN_DIR/centipede --binary=$BIN_DIR/$FUZZ_TARGET --workdir=$WD --num_runs=100
ワーキングディレクトリの中身を確認します:
tree $WD
...
├── <fuzz target name>-d9d90139ee2ccc687f7c9d5821bcc04b8a847df5
│ └── features.0
└── corpus.0
警告: --j フラグには、お使いのマシンのコア数を超える値を指定しないでください。
rm -rf $WD/*
$BIN_DIR/centipede --binary=$BIN_DIR/$FUZZ_TARGET --workdir=$WD --num_runs=100 --j=5
ワーキングディレクトリの中身を確認します:
tree $WD
...
├── <fuzz target name>-d9d90139ee2ccc687f7c9d5821bcc04b8a847df5
│ ├── features.0
│ ├── features.1
│ ├── features.2
│ ├── features.3
│ └── features.4
├── corpus.0
├── corpus.1
├── corpus.2
├── corpus.3
└── corpus.4
各 Centipede シャードは通常、コーパス全体がカバーするすべてのフィーチャーをカバーしているわけではありません。コーパスを蒸留するには、Centipede プロセスがすべてのシャードを読み取る必要があります。現在、蒸留は次のように機能します:
--distill_shards=N を指定して実行します。これにより、最初の N 個のジョブが N 個の独立した蒸留コーパスファイル(ジョブごとに1つ)を生成します。各蒸留コーパスは完全なコーパスと同じフィーチャーを持つはずですが、これらの蒸留コーパス間で入力は大きく異なる可能性があります。蒸留コーパスを libFuzzer スタイルのディレクトリ(入力ごとに1ファイルのローカルディレクトリ)にもエクスポートする必要がある場合は、--corpus_dir=DIR を追加してください。
Centipede はテキストファイル形式のシンプルなカバレッジレポートを生成します。シャード 123 は、実際のファジングが始まる前に workdir/coverage-report-BINARY.000123.txt ファイルを生成します。つまり、レポートはコーパスをロードした後にシャード 123 が観測したカバレッジを反映します。
レポートには、完全にカバーされた関数(すべての制御フローエッジが少なくとも1回観測された)、カバーされていない関数、または部分的にカバーされた関数が表示されます。部分的にカバーされた関数については、レポートにカバー済みおよび未カバーのすべてのエッジのシンボル情報が含まれます。
レポートは次のようになります:
FULL: FUNCTION_A a.cc:1:0
NONE: FUNCTION_BB bb.cc:1:0
PARTIAL: FUNCTION_CCC ccc.cc:1:0
+ FUNCTION_CCC ccc.cc:1:0
- FUNCTION_CCC ccc.cc:2:0
- FUNCTION_CCC ccc.cc:3:0
未定