ABV.GENERAL

バッファオーバーフロー - 配列インデックスが範囲外

バッファオーバーフローは、オーバーランとも呼ばれ、データをバッファに書き込むプログラムがバッファの境界でオーバーランし、近くのメモリに上書きする異常な状態を指します。通常、この問題は、プログラムが文字列をバッファにコピーするときに発生します。

C および C++ では、データへのアクセスまたはメモリの任意の場所へのデータの上書きに対する組み込み保護が用意されていません。また、配列 (その言語の組み込みバッファタイプ) に書き込まれたデータが配列の境界内にあるかを自動的にチェックしません。

ABV.GENERAL チェッカーは配列範囲の違反、つまり、配列の範囲外の配列要素へのアクセスを検出する汎用のチェッカーです。

脆弱性とリスク

バッファオーバーフローは、コードを実行するように設計された、またはプログラムの動作方法の変更のために設計された入力によりトリガーされることがあります。これにより、メモリアクセスエラー、不正確な結果、クラッシュ、またはシステムセキュリティ違反など、異常なプログラムの動作を引き起こすことがあります。

バッファオーバーフローの結果には、有効なデータの上書きや、恣意的なコードや潜在的に悪意のあるコードの実行なども含まれます。たとえば、バッファオーバーフローによって、以下に示すように、プログラムが操作されることがあります。

  • メモリ内のバッファの近くにあるローカル変数を上書きし、攻撃者の都合の良いようにプログラムの動作を変更する
  • スタックフレームの戻りアドレスを上書きし、攻撃者が指定した戻りアドレス (通常はユーザー入力を格納しているバッファ) から実行が再開されるようにする
  • 関数ポインターまたは例外ハンドラーを上書きして、後で実行する

脆弱コード例 1

コピー
  int main()
  {
      char fixed_buf[10];
      sprintf(fixed_buf,"Very long format string\n"); // Line 4.ABV.GENERAL
      return 0;
  }

Klocwork は 4 行目について、'fixed_buf' の配列インデックスが範囲外になる可能性を指摘する、バッファオーバーフローレポートを生成します:サイズ 10 の配列 'fixed_buf' はインデックス値 0..24 を許容しているためです。ABV.GENERAL チェッカーは配列範囲の違反を検索し、この場合は配列要素 'fixed_buf' へのアクセスがその配列の範囲外であることを検出します。

修正コード例 1

コピー
  int main()
  {
      char fixed_buf[10];
      snprintf(fixed_buf, sizeof(fixed_buf), "Very long format string\n"); 
      return 0;
  }

修正コード例では、sprintf 関数が snprintf 関数で置換されました。前者は出力バッファが結果の文字列を維持する十分な大きさであることを単純に仮定しているのに対して、後者は最大バイト数をバッファに作成ます。この例はバッファオーバーフローを防止する 1 つの方法にすぎません。そして、この修正により文字列の打ち切りが発生しますが、アプリケーションの状況によっては、対策を考慮する必要がある場合があります。

脆弱コード例 2

コピー
  void foo()
  {
      char a[8]; // holds two 4-byte ints
      for (int i = 0; i < sizeof(a); i++)
      {
          ((int*)a)[i] = i;
      }
  }

配列または割り当て済みバッファを指すポインターが、その要素にアクセスする前に、異なるタイプにキャストされる状況があります。この例では、4 行目のループの上限が文字である配列 'a' のサイズにとられています。しかし、配列 'a' は 6 行目で整数の配列としてアクセスされます。Klocwork は、6 行目での 1 バイト文字と 4 バイト整数との違いのため、'a' の配列インデックスが範囲外になる可能性を示す、小さなバッファオーバーフローレポートを生成します:サイズ 2 の配列 'a' がインデックス値 2 ~ 7 を使用する可能性があるためです。問題をトレースバックすると、'char[8]' として宣言された配列 'a' がサイズ 2 の配列として扱われていることがわかります。

修正コード例 2

コピー
  void foo()
  {
      char a[8]; // holds two 4-byte ints
      for (int i = 0; i < sizeof(a) / sizeof(int); i++)
      {
          ((int*)a)[i] = i;
      }
  }

修正コード例では、4 行目のループの上限が変更され、整数配列として扱われる配列 'a' 内で、要素の総数を探すようになっています。

関連チェッカー

拡張機能

このチェッカーは、Klocwork knowledge base (ナレッジベース) を利用して拡張できます。詳細については、C/C++ 解析のチューニングを参照してください。