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

MEM35-C. オブジェクトに対して十分なメモリを割り当てる

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() が行われる。変数 lenBUFF_SIZE より小さいことが保証されている。しかし、lenint として宣言されているため、負の値をとりチェックをバイパスする可能性がある。memcpy() 関数は暗黙的に len を符号無し型 size_t に変換するため、結果の演算はバッファオーバーフローを引き起こす。

int len;
char *str;
char buf[BUFF_SIZE];

/* ... */
if (len < BUFF_SIZE){
  memcpy(buf, str, len);
}
/* ... */
適合コード (範囲検査)

以下のコード例では、lensize_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

 

 

mallocmemcpy() 関数に引数として渡されるサイズをチェックすることでこのルールの違反を検出できる。サイズ引数は 0 と SIZE_MAX の範囲に収まらなくてはならず、その型が size_t もしくは rsize_t でない限り、malloc() 呼び出し前に範囲チェックされなくてはならない。引数が式 a*b である場合、適切な範囲チェックは次のようになる:

if (a < SIZE_MAX / b && a > 0) ...

Coverity

6.5

BAD_ALLOC_STRLEN


SIZECHECK

文字列長がメモリ割り当ての目的で間違って計算された (計算された長さが意図した長さより短いことがある) 事例を検出できる。Coverity Prevent は、このルールの違反をすべて検出できるわけではないため、さらなる検証が必要である。

割り当てられたブロックより大きいオブジェクトを参照しているポインタに割り当てられるメモリ割り当てを検出する。

Fortify SCA

5.0

 

sizeof 演算子を含むものを除き、CERT C Rule Pack でこのルールへの違反を検出できる。

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)

Top へ

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