KASAN と KCOV を有効にして Pixel カーネルをビルドする

KernelAddressSanitizer(KASAN)は、カーネルの開発とテストで、ランタイムのメモリ関連バグ(境界外の読み取り / 書き込みオペレーションや use-after-free 問題など)を検出するのに役立ちます。KASAN は、ランタイム パフォーマンスの低下とメモリ使用量の増加をもたらすため製品版ビルドでは有効になっていませんが、デバッグビルドをテストするには便利なツールです。

Kernel Coverage(KCOV)というもう 1 つのランタイム ツールを合わせて使用すると、デベロッパーとテスターは、KASAN でサニタイズされ KCOV でインストルメント化されたコードを使用して、ランタイム メモリエラーを検出し、コード カバレッジ情報を取得できます。たとえば、syzkaller を使用してカーネル ファズテストを実施する場合、KASAN はクラッシュの根本原因を特定するのに役立ちます。KCOV は、テストケースまたはコーパスの重複排除に便利なコード カバレッジ情報をファジング エンジンに提供します。

このページでは、KASAN の内部的な動作や仕組みについては説明しません。Android Open Soure Project(AOSP)と Pixel のカーネルソースをビルドおよび変更し、KASAN と KCOV を有効にして起動するためのガイドとしてご使用ください。

ビルド環境を設定する

ビルド環境を設定するには、ダウンロードとビルドのセクションに記載されている手順に従います。

AOSP をビルドする

Android ソースコードをダウンロードします。KASAN イメージをビルドするには、アクティブな開発中ではない安定したビルドを選択します。ほとんどの場合は、最新のリリース / 安定したブランチがおすすめです。 ビルドとブランチの詳細については、ソースコード タグとビルドをご覧ください。

ソースコードを正常にチェックアウトできたら、使用するデバイスとブランチに対応する必要なデバイス blob を、Nexus および Pixel デバイス用ドライバのバイナリからダウンロードします。ベンダー イメージとバイナリセットの両方をシステム オン チップ(SoC)メーカーからダウンロードします。次に、ダウンロードした tarball をアーカイブ解除し、それに含まれているスクリプトを実行して、ライセンスに同意します。

次に、ビルドの準備に記載されている手順に従ってビルド環境をクリーンアップして設定し、ビルド ターゲットを選択します。

作業ベースを確立するため、最初のビルドは変更なしで作成します。

make -j48

ビルド結果をテストデバイス(例: marlin)にフラッシュして、起動します。

cd out/target/product/marlin
ANDROID_PRODUCT_OUT=`pwd` fastboot flashall -w

ホーム画面が起動されると、次のようなポップアップが表示されます。

There's an internal problem with your device. Contact your manufacturer for details. このポップアップは、ベンダーのビルドのフィンガープリントとシステム パーティションが一致していない可能性を示しています。このビルドは開発とテスト用でありリリースビルドではないため、無視してください。

カーネルをビルドする

カーネルをビルドするには、正しいソースコードをチェックアウトしてクロスコンパイルし、正しい AOSP ディレクトリでカーネル イメージをビルドする必要があります。

カーネル ソースコードをチェックアウトする

カーネル ソースコードを格納するディレクトリを作成し、ローカル ストレージに AOSP カーネル git リポジトリのクローンを作成します。

mkdir ~/src/marlin-kernel-src
cd ~/src/marlin-kernel-src
git clone https://android.googlesource.com/kernel/msm

完了すると、msm という名前の空のディレクトリが作成されます。

msm ディレクトリに移動し、ビルドするソースコードに対応するブランチを git checkout します。使用可能なブランチとタグのリストについては、Android msm カーネルソース ツリーをご覧ください。

cd msm
git checkout TAG_NAME

このステップを完了すると、msm ディレクトリにコンテンツが格納されます。

クロスコンパイルを実行する

次に、Android カーネルをコンパイルする必要があります。

クロスコンパイラを設定する

カーネルをビルドするには、クロスコンパイラを設定する必要があります。現在推奨されているテスト済みのツールチェーンは、Android NDK ツールチェーンの最新の安定したバージョンです。Android NDK をダウンロードするには、公式の Android NDK ウェブサイトにアクセスします。プラットフォームに適した zip アーカイブをダウンロードして、解凍します。これにより、android-ndk-NDK_VERSION のようなディレクトリが作成されます。

LZ4c ツールをダウンロードする

Pixel カーネルは LZ4 圧縮を使用するため、カーネルをビルドする際に lz4c ツールが必要になります。Ubuntu を使用している場合は、次のコマンドで lz4c ツールをインストールします。

sudo apt-get install liblz4-tool

カーネルをビルドする

次のようにして、marlin-kernel-src/msm ディレクトリからビルド環境を設定します。

export ARCH=arm64
export CROSS_COMPILE=PATH_TO_NDK/android-ndk-NDK_VERSION/toolchains/aarch64-linux-android-TOOLCHAIN_VERSION/prebuilt/linux-x86_64/bin/aarch64-linux-android-

次に、作業ベースを確立するために、カーネルの変更なしバージョンをビルドします。

make marlin_defconfig
make -j48

ビルドプロセスの結果は arch/arm64/boot/Image.lz4-dtb で見つかります。

AOSP でブートイメージを再ビルドする

カーネル イメージのビルドが完了したら、次のようにして結果を AOSP の device/google/marlin-kernel ディレクトリにコピーします。

cp ${marlin-kernel-src}/msm/arch/arm64/boot/Image.lz4-dtb device/google/marlin-kernel
source build/envsetup.sh
lunch aosp_marlin-userdebug
make -j48

ビルドが成功したら、次のようにしてターゲット デバイスをフラッシュします。

cd out/target/product/marlin
fastboot flashall -w

フラッシュすると、デバイスが起動します。デバイスの起動が完了したら、Settings -> System -> About phone に移動して Kernel version をチェックし、デバイスにフラッシュしたイメージがビルドしたカーネル イメージであることを確認します。

カーネルを変更する

KASAN および KCOV のコンパイル オプションを有効にする

KASAN および KCOV のコードは、通常のビルドでは有効にならないコンパイル フラグで保護されています。それらを有効にするには、KASAN および KCOV のオプションを構成ファイルに追加する一方で、LZ4 の構成を削除します。

そのためには、デフォルトの構成ファイルのコピー(例: marlin_defconfig)を作成します。

cd arch/arm64/configs
cp marlin_defconfig marlin-kasan_defconfig

新しい構成ファイルで、フラグ CONFIG_KERNEL_LZ4=y を削除し、以下のフラグを追加します。

CONFIG_KASAN=y
CONFIG_KASAN_INLINE=y
CONFIG_KCOV=y
CONFIG_SLUB=y
CONFIG_SLUB_DEBUG=y

新しい構成でカーネルを再コンパイルする

構成ファイルのコピーの変更が完了したら、カーネルを再コンパイルします。

カーネルを再構成する

ビルド環境を設定します。変更した defconfig をビルドし、生成された .config ファイルに、新しく追加されたフラグが存在することを確認します。

make marlin-kasan_defconfig
grep KASAN .config
CONFIG_HAVE_ARCH_KASAN=y
CONFIG_KASAN=y
# CONFIG_KASAN_OUTLINE is not set
CONFIG_KASAN_INLINE=y

KASAN フラグが存在するはずです。カーネルをコンパイルします。

make -j48

変更したカーネル イメージをチェックする

コンパイルが正常に完了したら、arch/arm64/boot ディレクトリに移動して、コンパイル結果を確認します。通常、Image.gz-dtb のサイズは標準ビルドより約 23 MB 大きくなります。

cd arch/arm64/boot
ls -lh Image.gz-dtb
-rw-r--r-- 1 username groupname 23M Aug 11 13:59 Image.gz-dtb

KCOV が適切にコンパイルされたかどうかを確認するには、カーネルソース ツリーのルートで、生成された vmlinux の分析を追加で行います。vmlinux で objdump を実行すると、__sanitizer_cov_trace_pc() の呼び出しが多数見つかります。

sh -c '${CROSS_COMPILE}objdump -d vmlinux' | grep sanitizer
ffffffc000082030:    94040658    bl    ffffffc000183990 <__sanitizer_cov_trace_pc>
ffffffc000082050:    94040650    bl    ffffffc000183990 <__sanitizer_cov_trace_pc>
ffffffc000082078:    94040646    bl    ffffffc000183990 <__sanitizer_cov_trace_pc>
ffffffc000082080:    94040644    bl    ffffffc000183990 <__sanitizer_cov_trace_pc>
ffffffc0000820ac:    94040639    bl    ffffffc000183990 <__sanitizer_cov_trace_pc>

AOSP コードを変更する

新しいブートイメージを接続する前に、デバイスの起動方法を制御する AOSP のソースコード内にある特定のパラメータを調整する必要があります。これは、主として新しい(インフレートされた)イメージが適切に起動するために必要です。

ボード パラメータを調整する

デバイスの BoardConfig.mk ファイルで定義されている起動パラメータを調整します。このファイルは、AOSP ソースコードのルートからの相対パスである device/google/marlin/marlin にあります。

cd device/google/marlin/marlin
vim BoardConfig.mk

BoardConfig.mk ファイルを変更したくない場合は、marlin_kasan という名前を含む新しい起動ターゲットを作成することもできます。このプロセスの詳細については、新しいデバイスを追加するをご覧ください。

ローカル Makefile でカーネル ターゲットを調整する

新しいカーネルは高速化のために LZ4 圧縮を使用しますが、KASAN は圧縮率を上げるために gzip を必要とします。これに対応するには、device/google/marlin/device-common.mkLOCAL_KERNEL 変数が指している場所を変更することにより、どのカーネルを最終ターゲットにバンドルするかをビルド機構に伝えます。

ブートイメージを再ビルドする

ブートイメージを再ビルドするには、新しいカーネル イメージをデバイス固有のフォルダ(device/google/marlin-kernel など)にある AOSP ツリーにコピーします。その場所が、カーネル ターゲット イメージが存在するとビルドシステムが想定している場所であることを(その場所を以前どのように変更したかに従って)確認してください。

次に、フラッシュ可能なイメージを再ビルドします。これは、前に AOSP をビルドした方法と似ています。ビルドが成功すると、通常どおり、すべてのビルドされたイメージがフラッシュされます。

カーネル イメージを変更したデバイスを起動する

以上で、起動後にホーム画面を表示するビルドが作成されました。ホーム画面から、起動の最も初期の段階で、デバイスの dmesg 出力に「KernelAddressSanitizer initialized」というメッセージが表示されることを確認します。このメッセージは、起動時に KASAN が初期化されたことを示しています。また、/sys/kernel/debug/kcov がデバイスに存在することも確認できます(そのためには root 権限が必要です)。

トラブルシューティング

KASAN および KCOV のコンパイル オプションを有効にする前に、標準ビルドを作業ベースとして、別のカーネル バージョンを試すことができます。問題が発生したら、デバイスのブートローダーとベースバンド バージョンが新しいビルドの要件と一致しているかどうかを最初に確認してください。カーネル バージョンをかなり上げた場合、最終的には Android ツリーのより新しいブランチを全面的に試す必要があるかもしれません。