整数オーバーフローの無害化

意図的でない整数オーバーフローは、メモリアクセスやメモリ割り当てに関連する変数において、メモリの破損または情報漏洩の脆弱性の原因となる可能性があります。これに対処するため、Clang の UndefinedBehaviorSanitizer(UBSan)の符号付き / 符号なし整数オーバーフロー サニタイザーを Android 7.0 で追加し、メディア フレームワークを強化しました。Android 9 では、より多くのコンポーネントをカバーするため UBSan の拡張を行い、それに対するビルドシステムのサポートを向上させました。

これは、オーバーフローが発生した場合にプロセスを安全に中止するため、オーバーフローする可能性がある算術演算 / 算術命令のチェックを追加するものです。 上記のサニタイザーにより、整数オーバーフローが根本原因であるメモリの破損と情報漏洩の脆弱性全般(最初の Stagefright 脆弱性など)を軽減できます。

例とソース

コンパイラによって提供される整数オーバーフロー サニタイズ(IntSan)は、算術オーバーフローを検出するため、コンパイル時にバイナリにインストゥルメンテーションを追加します。IntSan は、プラットフォーム全体のさまざまなコンポーネント(/platform/external/libnl/Android.bp など)で、デフォルトで有効になっています。

実装

IntSan では、UBSan の符号付き整数オーバーフロー サニタイザーと符号なし整数オーバーフロー サニタイザーを使用します。この緩和は、モジュール単位で有効になります。この機能は Android の重要なコンポーネントを安全に保護するものであり、無効にすることはできません。

追加のコンポーネントでも整数オーバーフロー サニタイズを有効にすることを強くおすすめします。権限を付与されたネイティブ コードや信頼できないユーザー入力を解析するネイティブ コードは、この機能を有効にするのに適しています。コードの使用量と算術演算の頻度に依存するサニタイザーには、わずかなパフォーマンス オーバーヘッドが伴います。オーバーヘッドの割合は少ないと予想されますが、パフォーマンスが懸念される場合はテストを実施してください。

makefile で IntSan をサポートする

makefile で IntSan を有効にするには、以下の行を追加します。

LOCAL_SANITIZE := integer_overflow
# Optional features
LOCAL_SANITIZE_DIAG := integer_overflow
LOCAL_SANITIZE_BLACKLIST := modulename_blacklist.txt
  • LOCAL_SANITIZE は、サニタイザーのカンマ区切りリストを受け取ります。個別の符号付き整数オーバーフロー サニタイザーと符号なし整数オーバーフロー サニタイザーには、デフォルトのブラックリストを備え、事前にパッケージ化されたオプション セットとして integer_overflow が用意されています。
  • LOCAL_SANITIZE_DIAG は、サニタイザーの診断モードを有効にします。診断モードはテスト時にのみ使用してください。診断モードではオーバーフローが発生してもプロセスが中止されず、セキュリティの脆弱性軽減のメリットが完全になくなるためです。詳細については、トラブルシューティングをご覧ください。
  • LOCAL_SANITIZE_BLACKLIST にはブラックリスト ファイルを指定して、関数とソースファイルのサニタイズを回避できます。詳細については、トラブルシューティングをご覧ください。

よりきめ細かい制御が必要な場合は、いずれか 1 つまたは両方のフラグを使用してサニタイザーを個別に有効にします。

LOCAL_SANITIZE := signed-integer-overflow, unsigned-integer-overflow
LOCAL_SANITIZE_DIAG := signed-integer-overflow, unsigned-integer-overflow

ブループリント ファイルで IntSan をサポートする

ブループリント ファイル(/platform/external/libnl/Android.bp など)で整数オーバーフロー サニタイズを有効にするには、以下の行を追加します。

   sanitize: {
      integer_overflow: true,
      diag: {
          integer_overflow: true,
      },
      blacklist: "modulename_blacklist.txt",
   },

make ファイルの場合と同様、integer_overflow プロパティは、個別の符号付き整数オーバーフロー サニタイザーと符号なし整数オーバーフロー サニタイザーを対象とする、デフォルトのブラックリストを備え、事前にパッケージ化されたオプションのセットです。

diag のプロパティ セットは、サニタイザーの診断モードを有効にします。診断モードはテスト時にのみ使用します。診断モードではオーバーフローが発生してもプロセスが中止されず、ユーザービルドにおけるセキュリティの脆弱性軽減のメリットが完全になくなるためです。詳細については、トラブルシューティングをご覧ください。

blacklist プロパティにはブラックリスト ファイルを指定できます。これにより、デベロッパーは関数とソースファイルのサニタイズを回避できます。詳細については、トラブルシューティングをご覧ください。

サニタイザーを個別に有効にするには、次のコマンドを使用します。

   sanitize: {
      misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow"],
      diag: {
          misc_undefined: ["signed-integer-overflow",
                           "unsigned-integer-overflow",],
      },
      blacklist: "modulename_blacklist.txt",
   },

トラブルシューティング

新しいコンポーネントで整数オーバーフロー サニタイズを有効にしている場合や、整数オーバーフロー サニタイズが行われたプラットフォーム ライブラリに依存している場合、無害な整数オーバーフローによりプロセスが中止される問題がまれに発生することがあります。無害なオーバーフローが確実に表面化するように、サニタイズを有効化したコンポーネントをテストしてください。

ユーザービルドのサニタイズで発生した中止を見つけるには、UBSan によってキャッチされたオーバーフローを示す Abort メッセージで SIGABRT クラッシュを検索します。例:

pid: ###, tid: ###, name: Binder:###  >>> /system/bin/surfaceflinger <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'ubsan: sub-overflow'

スタック トレースには中止を引き起こした関数が含まれているはずですが、インライン関数で発生したオーバーフローはスタック トレースでは必ずしも明らかになりません。

根本原因を簡単に特定するには、中止を引き起こしたライブラリで診断を有効にして、エラーの再現を試みます。診断を有効にするとプロセスは中止されず、引き続き実行されます。中止されないことにより、個々のバグを修正した後で再コンパイルする手間をかけずに、特定の実行パスで無害なオーバーフローが発生する回数を最大化できます。診断モードでは、中止の原因となった行番号とソースファイルを示すエラー メッセージが生成されます。

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:2188:32: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'size_t' (aka 'unsigned long')

問題のある算術演算が見つかったら、オーバーフローが無害で意図的なものである(セキュリティ上の影響がない)ことを確認します。サニタイザーの中止には次の方法で対処できます。

  • コードをリファクタリングしてオーバーフローを回避する(
  • Clang の __builtin_*_overflow 関数を使用して明示的にオーバーフローを発生させる(
  • no_sanitize 属性を指定して関数内のサニタイズを無効にする(
  • ブラックリスト ファイルを使用して関数またはソースファイルのサニタイズを無効にする(

できる限りきめ細かい解決策を使用する必要があります。たとえば、多数の算術演算と単一のオーバーフロー演算を含む大きな関数では、関数全体をブラックリストに登録するのではなく、単一の演算をリファクタリングします。

無害なオーバーフローが発生する一般的なパターンを次に示します。

  • 暗黙的なキャストで、符号付きの型にキャストされる前に符号なしオーバーフローが発生する(
  • リンクされたリストの削除で、削除時にループ インデックスをデクリメントする(
  • 実際の最大値を指定する代わりに、符号なしの型を -1 に割り当てる(
  • ループの条件内で符号なし整数をデクリメントする(

デベロッパーには、サニタイズを無効にする前に、意図しない副作用やセキュリティ上の影響がない、実際には無害なオーバーフローをサニタイザーが検出するケースを確認しておくことをおすすめします。

IntSan を無効にする

IntSan はブラックリストまたは関数属性で無効にできます。無効化は、コードのリファクタリングが割に合わない場合や、無視できないパフォーマンス オーバーヘッドが発生する場合に限って、控えめに実施してください。

関数属性ブラックリスト ファイル登録で IntSan を無効にする方法については、Clang のアップストリーム ドキュメントをご覧ください。ブラックリスト登録では、他のサニタイザーへの影響を避けるために、ターゲットのサニタイザーを指定するセクション名を使用して、特定のサニタイザーにスコープを限定する必要があります。

検証

現在のところ、整数オーバーフロー サニタイズ専用の CTS テストはありません。 代替策として、IntSan を有効 / 無効にして CTS テストにパスするかどうかを確認し、デバイスに影響しないことを検証してください。