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
では符号なし整数としての比較が行われるからだ。i
が INT_MAX
を超えてインクリメントされると、i
は INT_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
が負の数になるとループは停止し、i
は 0
から 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)
(この例では)i
を rsize_t
型として宣言することで、整数オーバーフロー条件が発生する可能性は排除される。また、引数 n
が rsize_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)
length
と alloc()
の引数である blocksize
をどちらも rsize_t
として宣言することで、切捨てが発生する可能性はなくなる。この適合コードでは、read_integer_from_network()
の引数 length
は rsize_t
へのポインタ型に、read_network_data()
の引数 length
は rsize_t
型に、それぞれ変更できることを仮定している。これらの関数が外部ライブラリの一部であり変更できない場合、length
を unsigned 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 |
|
|
このレコメンデーションの違反を検出できる。特に、ひとつのオペランドの型が |
Fortify SCA |
5.0 |
|
オーバーフローを引き起こす整数演算を検出するが、 |
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)