メモリのゼロ初期化

信頼性の問題やメモリ安全性のバグ、情報漏洩によく見られる原因は、C と C++ で、メモリが初期化されていないことです。この問題を回避するために、Android は可能な限り多くのメモリを初期化します。

ユーザー空間メモリのゼロ初期化

Android 12 以降、すべてのプラットフォーム ネイティブ コード(JNI を含む)でスタックメモリがゼロ初期化され、すべてのプラットフォーム ネイティブ プロセス(netd など)でヒープメモリがゼロ初期化されますが、zygote またはアプリではゼロ初期化されません。

NDK でビルドしたファースト パーティ アプリとサードパーティ アプリでは、-ftrivial-auto-var-init=zero コンパイラ フラグを使用してスタック ローカル変数をゼロ初期化することを強くおすすめします。コンパイラは、不要なゼロ化をなくすように最適化します。これはたとえば、ローカル変数が明示的に初期化される場合(int x = 123; 変数 x が 1 回だけ初期化されるなど)です。パフォーマンス ホットスポットでプログラムのスタック バッファが大きい場合、デベロッパーは次のコンパイラ属性を使用して初期化を無効にできます。

__attribute__((__uninitialized__)) char buf[BUFSIZ];

アプリは android:nativeHeapZeroInitialized マニフェスト属性を使用することで、ヒープのゼロ初期化を有効にすることもできます。あるいは、次のようにしてヒープのゼロ初期化を実行時に制御することもできます。

int mallopt(M_BIONIC_ZERO_INIT, level)

ここで、level は 0 または 1 です。

カーネルメモリのゼロ初期化

GKI カーネルについては、カーネル スタックとヒープがゼロ初期化されます。これは CDD で強く推奨されています。

スタック初期化では、GKI は CONFIG_INIT_STACK_ALL_ZERO 構成を使用します。これにより、-ftrivial-auto-var-init=zero コンパイラ フラグを使用してカーネルがビルドされます。ヒープ初期化では、GKI は CONFIG_INIT_ON_ALLOC_DEFAULT_ON を使用します。これにより、すべてのページヒープ、SLAB、SLUB の割り当てが作成時にゼロ初期化されます。このオプションは、カーネルの起動時オプションとして init_on_alloc=1 を渡すこととほぼ同じです。

バグレポート

Google のツールは、デバッグに役立つ補足情報を含む、情報量の多いバグレポートを生成します。追加の割り当てと割り当て解除のスタック トレースにより、特定の割り当てのライフサイクルについてよく理解し、根本原因であるメモリ安全性のバグを大幅に早く発見できます。

メモリ安全性ツールによって生成されたバグレポートの例
図 1: メモリ安全性ツールによって生成されたバグレポート

開発時に、ベンダーはネイティブ クラッシュについて /data/tombstoneslogcat を確認し、バグがないか注視する必要があります。Android ネイティブ コードのデバッグについて詳しくは、こちらをご覧ください。