Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

libFuzzer によるファジング

ファジングは、プログラムへの入力として潜在的に無効なデータ、予期しないデータ、またはランダムなデータを提供するだけの手法ですが、大規模なソフトウェア システムのバグを見つけるには非常に効果的であり、ソフトウェア開発ライフサイクルの重要な構成要素になっています。

Android のビルドシステムは、LLVM コンパイラ インフラストラクチャ プロジェクトからの libFuzzer を組み込むことで、ファジングをサポートします。LibFuzzer は、テスト対象の関数にリンクされ、ファジング セッション中に発生するすべての入力選択、変換、クラッシュ レポートを処理します。LLVM のサニタイザーは、メモリ破損の検出とコード カバレッジ指標に援用されます。

この記事では、Android での libFuzzer の使用方法とインストゥルメント化ビルドの実行方法を紹介します。また、ファザーの作成、実行、カスタマイズの手順についても説明します。

設定とビルド

デバイス上で正常なイメージが実行されていることを確認するには、ファクトリー イメージをダウンロードしてフラッシュします。または、AOSP ソースコードをダウンロードして、以下のセットアップとビルドの例に従うこともできます。

設定例

この例では、ターゲット デバイスが Pixel(taimen)であり、すでに USB デバッグ(aosp_taimen-userdebug)用に準備されていることを前提としています。他の Pixel バイナリは、ドライバ バイナリからダウンロードできます。

mkdir ~/bin
export PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
repo init -u https://android.googlesource.com/platform/manifest -b master
repo sync -c -j8
wget https://dl.google.com/dl/android/aosp/google_devices-taimen-qq1a.191205.008-f4537f93.tgz
tar xvf google_devices-taimen-qq1a.191205.008-f4537f93.tgz
./extract-google_devices-taimen.sh
wget https://dl.google.com/dl/android/aosp/qcom-taimen-qq1a.191205.008-760afa6e.tgz
tar xvf qcom-taimen-qq1a.191205.008-760afa6e.tgz
./extract-qcom-taimen.sh
. build/envsetup.sh
lunch aosp_taimen-userdebug

ビルド例

ファズ ターゲットを実行する最初のステップは、最新のシステム イメージを取得することです。Android の最新版の開発をご利用の際は、

  1. 次のコマンドを実行して、最初のビルドを実行します。
    m
  2. デバイスにフラッシュするには、適切なキーの組み合わせを使用して、デバイスを fastboot モードで起動します。
  3. 次のコマンドにより、ブートローダーのロックを解除して、新しくコンパイルされたイメージをフラッシュします
    fastboot oem unlock
    fastboot flashall
    

これで、ターゲット デバイスで libFuzzer ファジングを行う準備が整いました。

ファザーを作成する

Android で libFuzzer を使用してエンドツーエンドのファザーを作成する方法を説明するため、テストケースとして次の脆弱なコードを使用します。これにより、ファザーをテストして、すべてが正常に機能していることを確認し、クラッシュ データがどのように表示されるかを示します。

テスト関数は次のとおりです。

#include <stdint.h>
#include <stddef.h>
bool FuzzMe(const char *Data, size_t DataSize) {
    return DataSize >= 3  &&
           Data[0] == 'F' &&
           Data[1] == 'U' &&
           Data[2] == 'Z' &&
           Data[3] == 'Z';  // ← Out of bounds access
}

このテスト用ファザーをビルドして実行するには:

  1. ファズ ターゲットは、ビルド ファイルとファジング ターゲット ソースコードの 2 つのファイルで構成されます。 拡張するライブラリの横の場所にファイルを作成します。ファザーには、ファザーが何をするのかを説明する名前を付けます。
  2. libFuzzer を使用してファズ ターゲットを作成します。ファズ ターゲットは、指定されたサイズのデータの blob を受け取って、ファジング対象の関数に渡す関数です。脆弱なテスト関数の基本的なファザーは以下のようになります。
    #include <stddef.h>
    #include <stdint.h>
    
    extern "C" int LLVMFuzzerTestOneInput(const char *data, size_t size) {
      // ...
      // Use the data to call the library you are fuzzing.
      // ...
      return FuzzMe(data, size);
    }
    
  3. Android のビルドシステムに、ファザー バイナリの作成を指示します。 ファザーをビルドするには、次のコードを Android.bp ファイルに追加します。
    cc_fuzz {
      name: "fuzz_me_fuzzer",
      srcs: [
        "fuzz_me_fuzzer.cpp",
      ],
      // If the fuzzer has a dependent library, uncomment the following section and
      // include it.
      // static_libs: [
      //   "libfoo", // Dependent library
      // ],
      //
      // The advanced features below allow you to package your corpus and
      // dictionary files during building. You can find more information about
      // these features at:
      //  - Corpus: https://llvm.org/docs/LibFuzzer.html#corpus
      //  - Dictionaries: https://llvm.org/docs/LibFuzzer.html#dictionaries
      // These features are not required for fuzzing, but are highly recommended
      // to gain extra coverage.
      // To include a corpus folder, uncomment the following line.
      // corpus: ["corpus/*"],
      // To include a dictionary, uncomment the following line.
      // dictionary: "fuzz_me_fuzzer.dict",
    }
    
  4. ターゲット(デバイス)で実行するためのファザーを作成するには:
    SANITIZE_TARGET=hwaddress m fuzz_me_fuzzer
    
  5. ホスト上でファザーを実行するには:
    SANITIZE_HOST=address m fuzz_me_fuzzer
    

便宜上、ファジー ターゲットへのパスとバイナリ名(先ほど作成したビルドファイルから)を含むシェル変数を定義します。

export FUZZER_NAME=your_fuzz_target

以上のステップを完了すると、ファザーがビルドされます。ファザーのデフォルトの場所は、(この例の Pixel ビルドでは) です。

  • $ANDROID_PRODUCT_OUT/data/fuzz/$TARGET_ARCH/$FUZZER_NAME/$FUZZER_NAME になります。
  • ホスト $ANDROID_HOST_OUT/fuzz/$TARGET_ARCH/$FUZZER_NAME/$FUZZER_NAME
  • ホストでファザーを実行する

  • Android.bp ビルドファイルに追加します。
    host_supported: true,
    これは、融合するライブラリがホストでサポートされている場合にのみ適用できます。
  • ビルドされたファザー バイナリを実行するだけで、ファザーを fuzzer で実行します。
    $ANDROID_HOST_OUT/fuzz/x86_64/$FUZZER_NAME/$FUZZER_NAME
  • デバイスで Fuzzer を実行する

    adb を使ってデバイスにこのコピーをコピーします。

    1. これらのファイルをデバイスのディレクトリにアップロードするには、次のコマンドを実行します。
      adb root
      adb sync data
       
    2. 次のコマンドを使用して、デバイスでテストファザーを実行します。
      adb shell /data/fuzz/$(get_build_var TARGET_ARCH)/$FUZZER_NAME/$FUZZER_NAME \
        /data/fuzz/$(get_build_var TARGET_ARCH)/$FUZZER_NAME/corpus

    これにより、次のような出力が得られます。