JPCERT コーディネーションセンター

INT01-C. オブジェクトのサイズを表現するすべての整数値に rsize_t もしくは size_t を使用する

INT01-C. オブジェクトのサイズを表現するすべての整数値に rsize_t もしくは size_t を使用する

size_t 型は sizeof 演算子の結果の符号なし整数型である。size_t 型の変数は、そのオブジェクトのサイズを表現するのに十分な精度を持つことが保証されている。size_t の最大値は SIZE_MAX マクロで規定されている。

size_t 型は一般にすべてのアドレス空間をカバーする。ISO/IEC TR 24731-1-2007 では新しい型として rsize_t が提案されている。rsize_t 型は size_t 型と同じ大きさであり、明示的に単一のオブジェクトのサイズを保持するために使われるものとして定義されている[Meyers 2004]。rsize_t 型を明示的に使用することで、オブジェクトのサイズが RSIZE_MAX、つまり通常の単一オブジェクトの最大サイズを超えないことを確認できる。TR 24731-1 について詳しくは、「STR07-C. 境界チェックインタフェースを使用し、文字列操作を行う既存のコードの脅威を緩和する」を参照のこと。

オブジェクトのサイズ(サイズとして使用する整数値を含む)、インデックス、ループカウンタ、長さを表すために用いられる変数はすべて、利用可能であれば rsize_t として定義すべきである。rsize_t が利用可能でない場合には size_t として定義すべきである。

違反コード

以下のコード例において、p が参照する動的に確保されたバッファは n > INT_MAX のときオーバーフローする。

char *copy(size_t n, const char *str) {
  int i;
  char *p;

  if (n == 0) {
    /* オブジェクトのサイズが異常なエラーの際の処理 */
  }
  p = (char *)malloc(n);
  if (p == NULL) {
    /* malloc が失敗した際の処理 */
  }
  for ( i = 0; i < n; ++i ) {
    p[i] = *str++;
  }
  return p;
}

/* ... */

char str[] = "hi there";
char *p = copy(sizeof(str), str);

符号付き整数のオーバーフローは未定義の動作を引き起こす。このコードが深刻な脆弱性となる可能性として、以下の2通りが存在する。

sizeof(size_t) == sizeof(int)

符号なし整数の n には INT_MAX より大きな値が入るかもしれない。符号付き整数がオーバーフローしたとき黙ってラップアラウンドするような実行環境では、ループは n 回実行される。i < n では符号なし整数としての比較が行われるからだ。iINT_MAX を超えてインクリメントされると、iINT_MIN から始まる負の値をとる。それゆえ p[i] が参照するメモリ位置は p が参照するメモリより前に位置することになり、配列の範囲外書き込みが発生する。

sizeof(size_t) > sizeof(int)

0 < n <= INT_MAX を満たす n の値については、ループは期待通り n 回実行される。

INT_MAX < n <= (size_t)INT_MIN となる n の値については、ループは INT_MAX 回実行される。i が負の数になるとループは停止し、i0 から INT_MAX の範囲にとどまる。

(size_t)INT_MIN < n <= SIZE_MAX を満たす n の値に対しては i はラップアラウンドして INT_MIN から INT_MIN + (n - (size_t)INT_MIN - 1) の範囲の値をとる。これにより、p[INT_MIN] から p[INT_MIN + (n - (size_t)INT_MIN - 1)] の範囲のメモリが上書きされる。

適合コード (TR 24731-1)

(この例では)irsize_t 型として宣言することで、整数オーバーフロー条件が発生する可能性は排除される。また、引数 nrsize_t 型に変更されることで、RSIZE_MAX を超えないことがコード上に表現されている。

char *copy(rsize_t n, const char *str) {
  rsize_t i;
  char *p;

  if (n == 0 || n > RSIZE_MAX) {
    /* オブジェクトのサイズが異常なエラーの際の処理 */
  }
  p = (char *)malloc(n);
  if (p == NULL) {
    /* malloc が失敗した際の処理 */
  }
  for (i = 0; i < n; ++i) {
    p[i] = *str++;
  }
  return p;
}

/* ... */

char str[] = "hi there";
char *p = copy(sizeof(str), str);
違反コード

以下のコード例では、length の値がネットワーク接続経由で読み込まれ、malloc() のラッパー関数に引数として渡され、適切なデータブロックが割り当てられる。unsigned long のサイズと unsigned int のサイズが等しく、かつ両サイズが size_t と等しいかより小さい、と仮定すると、このコードは期待通り実行される。しかし、unsigned long のサイズが unsigned int のサイズより大きい場合、length の値は alloc() に引数として渡される際に切捨てが発生しているかもしれない。

void *alloc(unsigned int blocksize) {
  return malloc(blocksize);
}

int read_counted_string(int fd) {
  unsigned long length;
  unsigned char *data;

  if (read_integer_from_network(fd, &length) < 0) {
    return -1;
  }

  data = (unsigned char*)alloc(length);
  if (data == NULL) {
    /* エラー処理 */
  }

  if (read_network_data(fd, data, length) < 0) {
    free(data);
    return -1;
  }
  data[length-1] = '\0';

  /* ... */
  free( data);
  return 0;
}
適合コード (TR 24731-1)

lengthalloc() の引数である blocksize をどちらも rsize_t として宣言することで、切捨てが発生する可能性はなくなる。この適合コードでは、read_integer_from_network() の引数 lengthrsize_t へのポインタ型に、read_network_data() の引数 lengthrsize_t 型に、それぞれ変更できることを仮定している。これらの関数が外部ライブラリの一部であり変更できない場合、lengthunsigned long にキャストする際には整数の切捨てが発生しないよう注意しなければならない。

void *alloc(rsize_t blocksize) {
  if (blocksize == 0 || blocksize > RSIZE_MAX) {
    /* エラー処理 */
  }
  return malloc(blocksize);
}

int read_counted_string(int fd) {
  rsize_t length;
  unsigned char *data;

  if (read_integer_from_network(fd, &length) < 0) {
    return -1;
  }

  data = (unsigned char*)alloc(length);
  if (data == NULL) {
    /* エラー処理 */
  }

  if (read_network_data(fd, data, length) < 0) {
    free(data);
    return -1;
  }
  data[length-1] = '\0';

  /* ... */
  free( data);
  return 0;
}
リスク評価

オブジェクトのサイズの計算や関連する操作を正しく行わないと、攻撃可能な脆弱性につながる恐れがある。

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

INT01-C

P8

L2

自動検出

ツール

バージョン

チェッカー

説明

Compass/ROSE

 

 

このレコメンデーションの違反を検出できる。特に、ひとつのオペランドの型が size_t もしくは rsize_t であり、もう一方がそうでないような比較や演算を検出する。

Fortify SCA

5.0

 

オーバーフローを引き起こす整数演算を検出するが、size_t が使用されていないすべての場合ではない。

LDRA tool suite

V. 8.5.4

93 S

実装済み

Splint

V. 3.1.1

 

 

関連するガイドライン
CERT C++ Secure Coding Standard INT01-CPP. Use rsize_t or size_t for all integer values representing the size of an object
ISO/IEC TR 24731-1:2007  
参考資料
[Meyers 2004]  
翻訳元

これは以下のページを翻訳したものです。

INT01-C. Use rsize_t or size_t for all integer values representing the size of an object (revision 105)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter