LLVMライブラリとツールのファジング¶
はじめに¶
LLVMツリーには、さまざまなコンポーネント用の多くのファザーが含まれています。これらはLibFuzzerの上に構築されています。これらのファザーをビルドおよび実行するには、ファザーをビルドするためのLLVMの構成を参照してください。
利用可能なファザー¶
clang-fuzzer¶
テキスト入力をC++コードとしてコンパイルしようとする汎用ファザー。このファザーが報告したバグの一部は、bugzillaとOSS Fuzzのトラッカーにあります。
clang-proto-fuzzer¶
C++言語のサブセットを記述するprotobufクラスから生成された有効なC++プログラムをコンパイルする、libprotobuf-mutatorベースのファザー。
このファザーは、ignore_remaining_args=1の後にclangコマンドラインオプションを受け入れます。たとえば、次のコマンドは、より高い最適化レベルでclangをファジングします
% bin/clang-proto-fuzzer <corpus-dir> -ignore_remaining_args=1 -O3
clang-format-fuzzer¶
C++テキストフラグメントでclang-formatを実行する汎用ファザー。このファザーが報告したバグの一部は、bugzillaとOSS Fuzzのトラッカーにあります。
llvm-as-fuzzer¶
テキストをLLVMアセンブリとして解析しようとする汎用ファザー。このファザーが報告したバグの一部はbugzillaにあります。
llvm-dwarfdump-fuzzer¶
入力をオブジェクトファイルとして解釈し、それらに対してllvm-dwarfdumpを実行する汎用ファザー。このファザーが報告したバグの一部は、OSS Fuzzのトラッカーにあります
llvm-demangle-fuzzer¶
さまざまなLLVMツールで使用されているItaniumデマングラー用の汎用ファザー。__cxa_demangleを徹底的にファジングしたので、同じ関数のLLVM実装をファジングしてみましょう!
llvm-isel-fuzzer¶
命令選択のバグを見つけることを目的とした構造化LLVM IRファザー。
このファザーは、ignore_remaining_args=1の後にフラグを受け入れます。フラグはllcのフラグと一致し、トリプルが必要です。たとえば、次のコマンドは、グローバル命令選択を使用してAArch64をファジングします
% bin/llvm-isel-fuzzer <corpus-dir> -ignore_remaining_args=1 -mtriple aarch64 -global-isel -O0
一部のフラグは、必須引数に問題があるOSS Fuzzをサポートするために、バイナリ名自体で指定することもできます。これを行うには、llvm-isel-fuzzer
をllvm-isel-fuzzer--x-y-z
にコピーまたは移動し、オプションをバイナリ名から「--」を使用して分離します。有効なオプションは、アーキテクチャ名(aarch64
、x86_64
)、最適化レベル(O0
、O2
)、またはグローバル命令選択を有効にするためのgisel
などの特定のキーワードです。このモードでは、同じ例を次のように実行できます
% bin/llvm-isel-fuzzer--aarch64-O0-gisel <corpus-dir>
llvm-opt-fuzzer¶
最適化パスのバグを見つけることを目的とした構造化LLVM IRファザー。
最適化パイプラインを受け取り、各ファザー入力に対して実行します。
このファザーのインターフェースは、llvm-isel-fuzzer
をほぼ直接ミラーリングしています。mtriple
引数とpasses
引数の両方が必要です。パスは、新しいパスマネージャーに適した形式で指定されます。この形式に関するドキュメントは、PassBuilder::parsePassPipeline
のdoxygenにあります。
% bin/llvm-opt-fuzzer <corpus-dir> -ignore_remaining_args=1 -mtriple x86_64 -passes instcombine
llvm-isel-fuzzer
の引数と同様に、一部の事前定義された構成をバイナリファイル名に直接埋め込むことができます
% bin/llvm-opt-fuzzer--x86_64-instcombine <corpus-dir>
llvm-mc-assemble-fuzzer¶
入力をターゲット固有のアセンブリとして扱うことにより、MCレイヤーのアセンブラーをファジングする汎用ファザー。
このファザーには、libFuzzerのすべての機能と完全には互換性のない異常なコマンドラインインターフェースがあることに注意してください。ファザーの引数は--fuzzer-args
の後に渡す必要があり、llc
フラグは2つのダッシュを使用する必要があります。たとえば、AArch64アセンブラーをファジングするには、次のコマンドを使用できます
llvm-mc-fuzzer --triple=aarch64-linux-gnu --fuzzer-args -max_len=4
このスキームは将来変更される可能性があります。
llvm-mc-disassemble-fuzzer¶
入力をアセンブルされたバイナリデータとして扱うことにより、MCレイヤーのディスアセンブラーをファジングする汎用ファザー。
このファザーには、libFuzzerのすべての機能と完全には互換性のない異常なコマンドラインインターフェースがあることに注意してください。詳細については、上記のllvm-mc-assemble-fuzzer
に関するメモを参照してください。
lldb-target-fuzzer¶
入力をオブジェクトファイルとして解釈し、それらを使用してlldbにターゲットを作成する汎用ファザー。
ミューテーターと入力ジェネレーター¶
ファズターゲットの入力は、コーパスのランダムな変異によって生成されます。LLVMのファザーで必要になる可能性のある変異の種類には、いくつかのオプションがあります。
ジェネリックランダムファジング¶
入力変異の最も基本的な形式は、LibFuzzerの組み込みミューテーターを使用することです。これらは単に入力コーパスをビットの袋として扱い、ランダムな変異を行います。このタイプのファザーは、プログラムの表面レイヤーにストレスを与えるのに適しており、レクサー、パーサー、バイナリプロトコルなどのテストに適しています。
このタイプのミューテーターを使用するインツリーファザーの一部は、clang-fuzzer、clang-format-fuzzer、llvm-as-fuzzer、llvm-dwarfdump-fuzzer、llvm-mc-assemble-fuzzer、およびllvm-mc-disassemble-fuzzerです。
libprotobuf-mutator
を使用した構造化ファジング¶
libprotobuf-mutatorを使用して、構造化ファジングを実行し、プログラムのより深いレイヤーにストレスを与えることができます。これは、任意のデータを構造的に興味深い入力に変換するprotobufクラスを定義することによって機能します。具体的には、これを使用してC++言語のサブセットを操作し、パーサーエラー処理よりも興味深いclangの部分を実行するために有効なC++プログラムを生成する変異を実行します。
この種のファザーを構築するには、protobufとその依存関係がインストールされている必要があります。また、CMakeでビルドを構成する際に、追加のフラグを指定する必要があります。たとえば、clang-proto-fuzzerは、ファザーをビルドするためのLLVMの構成で説明されているフラグに-DCLANG_ENABLE_PROTO_FUZZER=ON
を追加することで有効にできます。
現在、libprotobuf-mutator
を使用しているインツリーファザーはclang-proto-fuzzerのみです。
LLVM IRの構造化ファジング¶
また、LLVM IRを入力として受け取るファザーには、より直接的な形式の構造化ファジングを使用します。これは、FuzzMutate
ライブラリを通じて実現されます。このライブラリは、EuroLLVM 2017で議論されました。
FuzzMutate
ライブラリは、llvm-isel-fuzzerでバックエンドを構造的にファジングするために使用されます。
ビルドと実行¶
ファザーをビルドするためのLLVMの構成¶
サニタイザーカバレッジを有効にしてLLVMをビルドすると、デフォルトでファザーがビルドされ、libFuzzerにリンクされます。通常、バグをより迅速に見つけるために、少なくとも1つのサニタイザーも有効にします。ファザーをビルドする最も一般的な方法は、CMakeの呼び出しに次の2つのフラグを追加することです。-DLLVM_USE_SANITIZER=Address -DLLVM_USE_SANITIZE_COVERAGE=On
。
注記
サニタイザーを使用してビルドするときに、LLVMツリーにcompiler-rt
をチェックアウトしている場合は、サニタイザー自体をサニタイザーを有効にしてビルドすることを避けるために、-DLLVM_BUILD_RUNTIME=Off
を指定する必要があります。
注記
多くのUnixシステムでデフォルトのリンカーであるBFD ldを使用してビルドすると、問題が発生する可能性があります。これらの問題は、https://llvm.dokyumento.jp/PR34636で追跡されています。
継続的な実行とバグの発見¶
以前は、LLVMファザーを継続的に実行するパブリックビルドボットがありましたが、これは問題を検出しましたが、実用的な方法で問題を報告するための優れた方法がありませんでした。このため、代わりにOSS Fuzzを使用する方向に移行しています。
LLVMプロジェクトの問題リストで、OSS FuzzのLLVMによって発見されたバグを参照できます。これらは、llvm-bugsメーリングリストにもメールで送信されます。
ファザーを記述するためのユーティリティ¶
LLVMでファザーを記述するために利用できるユーティリティがいくつかあります。
コマンドラインインターフェースを処理するためのいくつかのヘルパーは、include/llvm/FuzzMutate/FuzzerCLI.h
で利用できます。これには、コマンドラインオプションを一貫した方法で解析する関数や、libFuzzerに対してビルドされていないときにファザーをビルドおよびテストできるようにスタンドアロンのメイン関数を実装する関数が含まれます。
ファザーのCMake構成の処理もいくつかあり、ファザーターゲットを設定するにはadd_llvm_fuzzer
を使用する必要があります。この関数は、add_llvm_tool
などの関数と同様に動作しますが、適切な場合はLibFuzzerへのリンクを処理し、スタンドアロンのテストを有効にするためにDUMMY_MAIN
引数を渡すことができます。