MEM35-C. オブジェクトに対して十分なメモリを割り当てる
malloc()
、calloc()
、realloc()
、aligned_alloc
のサイズ引数として使用する整数値は、有効な値であり、かつオブジェクトを格納するのに十分大きな値でなくてはならない。サイズ引数が正しくない場合や、攻撃者によって操作が可能な場合、バッファオーバーフローが生じる可能性がある。不正なサイズ引数、不適切な範囲チェック、整数オーバーフロー、切り捨て、などが原因で不十分なサイズのバッファが割り当てられる結果となることもある。プログラマは、メモリ割り当て関数のサイズ引数が、十分なメモリを割り当てられる値であることを保証しなければならない。
違反コード (整数オーバーフロー)
以下のコードでは、num_blocks
に 16 を乗じ、その結果の値は alloc
に保存される。
enum { BLOCKSIZE = 16 }; /* ... */ void *alloc_blocks(size_t num_blocks) { if (num_blocks == 0) { return NULL; } unsigned long long alloc = num_blocks * BLOCKSIZE ; return (alloc < UINT_MAX) ? malloc(num_blocks * BLOCKSIZE ) : NULL; }
size_t
が32ビット符号なし値として表現され、unsigned long long
が64ビット符号なし値として表現される場合、この乗算は実際には32ビットの演算であるため、依然としてオーバーフローを引き起こす可能性がある。その結果、alloc
に格納される値は常に UINT_MAX
よりも小さくなる。
また、size_t
型と unsigned long long
型がどちらも64ビットの unsigned
値として表現される場合、乗算の結果は unsigned long long
値で表現できない場合がある。アップキャストに関する詳細は「INT35-C. 整数式をより大きなサイズの整数に対して比較や代入をする際には、事前に演算後のサイズで評価する」を参照のこと。
適合コード (整数オーバーフロー)
メモリ割り当て関数のサイズ引数として渡される整数値について、整数オーバーフロー(「INT32-C. 符号付き整数演算がオーバーフローを引き起こさないことを保証する」)や切り捨て(「INT31-C. 整数変換によってデータの消失や解釈間違いが発生しないことを保証する」)が発生しないことを確認している。
enum { BLOCKSIZE = 16 }; /* ... */ void *alloc_blocks(size_t num_blocks) { if (num_blocks == 0 || num_blocks > SIZE_MAX / BLOCKSIZE) return NULL; return malloc(num_blocks * BLOCKSIZE); }
このコード例では num_blocks
の値をチェックして、続く乗算演算で整数オーバーフローが発生しないことを確認している。また num_blocks
がゼロでないことも保証している(「MEM04-C. サイズ 0 のメモリ割り当てを行わない」を参照)。
違反コード (範囲検査)
以下のコード例では、str
が参照する文字列と文字列の長さを表す len
を信頼できない入力源から得ている。長さ len
を使って固定長の静的配列 buf
への memcpy()
が行われる。変数 len
は BUFF_SIZE
より小さいことが保証されている。しかし、len
は int
として宣言されているため、負の値をとりチェックをバイパスする可能性がある。memcpy()
関数は暗黙的に len
を符号無し型 size_t
に変換するため、結果の演算はバッファオーバーフローを引き起こす。
int len; char *str; char buf[BUFF_SIZE]; /* ... */ if (len < BUFF_SIZE){ memcpy(buf, str, len); } /* ... */
適合コード (範囲検査)
以下のコード例では、len
は size_t
型として宣言されており、負の値をとって範囲チェックをバイパスする可能性はない。
size_t len; char *str; char buf[BUFF_SIZE]; /* ... */ if (len < BUFF_SIZE){ memcpy(buf, str, len); } /* ... */
オブジェクトのサイズ表現に関する詳細は「INT01-C. オブジェクトのサイズを表現する整数値には rsize_t もしくは size_t を使用する」を参照のこと。
違反コード (サイズ計算)
以下のコード例では、long
型整数の配列が p
に割り当てられている。しかし、割り当てるべきメモリサイズを sizeof(int)
を使って求めている。sizeof(long)
が sizeof(int)
よりも大きい場合、十分なメモリ容量は割り当てられない。
void function(size_t len) { long *p; if (len == 0 || len > SIZE_MAX / sizeof(long)) { /* オーバーフローの処理 */ } p = (long *)malloc(len * sizeof(int)); if (p == NULL) { /* エラー処理 */ } /* ... */ free(p); }
このコードでは「INT32-C. 符号付き整数演算がオーバーフローを引き起こさないことを保証する」に適合するよう、符号無し整数演算のオーバーフローをチェックしている。
適合コード (サイズ計算)
上記のコードを修正するために、sizeof(long)
を使ってメモリ割り当てサイズを調べている。
void function(size_t len) { long *p; if (len == 0 || len > SIZE_MAX / sizeof(long)) { /* オーバーフローの処理 */ } p = (long *)malloc(len * sizeof(long)); if (p == NULL) { /* エラー処理 */ } /* ... */ free(p); }
あるいは、以下のように sizeof(*p)
を使って正しいサイズを割り当てることもできる。
void function(size_t len) { long *p; if (len == 0 || len > SIZE_MAX / sizeof(*p)) { /* オーバーフローの処理 */ } p = (long *)malloc(len * sizeof(*p)); if (p == NULL) { /* エラー処理 */ } /* ... */ free(p); }
また len
がゼロでないことも保証している(「MEM04-C. サイズ 0 のメモリ割り当てを行わない」を参照)。
リスク評価
メモリ割り当て関数に不適切なサイズ引数を指定すると、バッファオーバーフローを引き起こしたり、脆弱なプロセスの権限で任意のコードを実行される可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
MEM35-C |
高 |
中 |
高 |
P6 |
L2 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Compass/ROSE |
|
|
if (a < SIZE_MAX / b && a > 0) ... |
Coverity |
6.5 |
BAD_ALLOC_STRLEN
|
文字列長がメモリ割り当ての目的で間違って計算された (計算された長さが意図した長さより短いことがある) 事例を検出できる。Coverity Prevent は、このルールの違反をすべて検出できるわけではないため、さらなる検証が必要である。 割り当てられたブロックより大きいオブジェクトを参照しているポインタに割り当てられるメモリ割り当てを検出する。 |
Fortify SCA |
5.0 |
|
|
LDRA tool suite |
V. 8.5.4 |
487 S |
実装済み |
関連する脆弱性
CVE-2009-0587 はこのルールへの違反の結果である。バージョン 2.24.5 より前では、Evolution Data Server は、ユーザー入力文字列の長さに対して未チェック算術演算を実行し、その値を使用して新しいバッファに記憶域を割り当てていた。攻撃者は、長い文字列を入力し、その結果、不正な割り当てとバッファオーバーフローを引き起こすことで任意のコードを実行できる [xorl 2009]。
関連するガイドライン
CERT C++ Secure Coding Standard | MEM35-CPP. Allocate sufficient memory for an object |
ISO/IEC TR 24772:2013 | Buffer Boundary Violation (Buffer Overflow) [HCB] |
MITRE CWE | CWE-190, Integer overflow (wrap or wraparound) CWE-131, Incorrect calculation of buffer size |
参考資料
[Coverity 2007] | |
[Seacord 2013] | Chapter 4, "Dynamic Memory Management" Chapter 5, "Integer Security" |
[xorl 2009] | CVE-2009-0587: Evolution Data Server Base64 Integer Overflows |
翻訳元
これは以下のページを翻訳したものです。
MEM35-C. Allocate sufficient memory for an object (revision 122)