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

MEM09-C. メモリ割り当て関数がメモリを初期化すると仮定しない

MEM09-C. メモリ割り当て関数がメモリを初期化すると仮定しない

C 標準では、メモリ管理用に 4 つの関数が定義されている。メモリの割り当てと解放に加え、関数によっては部分的または完全にメモリを初期化できるものもある。各関数のセマンティクスを理解しないと、セキュリティリスクを伴うプログラムの欠陥を引き起こす原因となる。以下の表は、各メモリ管理関数の基本特性を示している。

関数

割り当て

解放

初期化

calloc()

(tick)

(error)

(tick)

free()

(error)

(tick)

(error)

malloc()

(tick)

(error)

(error)

realloc()

(tick)

(tick)

部分的

calloc()

calloc() 関数は以下のように宣言される。

void *calloc(size_t nmemb, size_t size);

関数は、nmemb オブジェクトの配列の領域を割り当て、各配列のサイズは size である。領域はすべてのビットがゼロに初期化されるが、浮動小数点ゼロ、または NULL ポインタ定数の表現と同じであるとは限らない。このため、calloc() によって割り当てられたメモリは、ポインタオブジェクトまたは浮動小数点型オブジェクトの格納を意図していないかぎり、再初期化することなく即座に使用可能となる。

free()

free() 関数は、3 つのメモリ割り当て関数、calloc()malloc() および realloc() のいずれかによって以前に割り当てられたメモリを解放する。関数はメモリが解放される前にメモリを上書きまたは消去することがある (必須ではない)。このため、それ以降の malloc() または realloc() の呼び出しによって変更されることなく、メモリの内容を返すことができるようになる。

malloc()

C 標準 [ISO/IEC 9899:2011] セクション 7.22.3.4 では、malloc() 関数は以下のように宣言される。

void *malloc(size_t size);

また、以下のように説明されている。

malloc 関数は、オブジェクト用の領域を割り当てる。サイズは size で指定され、値は不定である。

つまり、malloc() で割り当てられるメモリは初期化されず、値は不定である。最初に初期化せずにこのようなメモリを使用するプログラムの動作は未定義である。標準の附属書 J 「未定義の動作」の 180 を参照のこと。それどころか、このメモリにはプログラム内(および一部のシステムではまったく別のプログラム)の他の部分で使用されたデータなど、予想外の値が含まれることがある。

realloc()

realloc() 関数は以下のように宣言される。

void *realloc(void *ptr, size_t size);

関数は動的に割り当てられたメモリオブジェクトのサイズを変更する。返されたメモリオブジェクトの初期 size バイトは変更されないが、新たに追加された領域は未初期化で、値は不定である。malloc() の場合と同様に、元のオブジェクトのサイズを超えてメモリにアクセスすると未定義の動作となる。標準の附属書 J 「未定義の動作」の 181 を参照のこと。

malloc() および realloc() によって割り当てられたメモリが使用される前に、正しく初期化を行う責任は、プログラマにある。「EXP33-C. 未初期化のメモリを参照しない」で説明されている通り、値が不定の unsigned char 以外の型のオブジェクトを使用すると未定義の動作となる。calloc() によって割り当てられたメモリはゼロ初期化されるが、これは浮動小数点ゼロや NULL ポインタ定数の表現と同じである必要はないため、可搬性があるプログラムでは、calloc() によって返されたメモリをポインタのオブジェクトまたは浮動小数点型として使用する前に、再初期化する必要がある。

さらに、「MEM03-C. 再利用可能なリソースに格納された機密情報は消去する」に記載されているように、初期化されていないメモリは情報漏えいの原因になることがある。

違反コード

以下のコード例では、strncpy() 関数を使用して動的に割り当てたバッァに文字列 str をコピーしている。

enum { MAX_BUF_SIZE = 256 };

char *str = /* ユーザ指定データ */;

size_t len = strlen(str);
if (len >= MAX_BUF_SIZE - 1) {
  /* 長すぎる文字列のエラー処理 */
}
char *buf = (char *)malloc(MAX_BUF_SIZE);
if (buf == NULL) {
  /* 割り当てエラーの処理 */
}
strncpy(buf, str, len);

/* buf の処理 */

free(buf);
buf = NULL;

len が null 終端文字を含む str の全体の長さよりも短い場合 buf は終端されない。その結果、この例は「STR32-C. 文字列は null 終端させる」にも違反する。

適合コード

以下の解決法では、割り当てられたメモリがゼロに初期化されていると想定せず、文字列を明示的に null 終端している。

enum { MAX_BUF_SIZE = 256 };

char *str;

/* コピー対象の文字列を初期化 */

size_t len = strlen(str);
if (len >= MAX_BUF_SIZE - 1) {
  /* 長すぎる文字列のエラー処理 */
}
char *buf = (char *)malloc(MAX_BUF_SIZE);
if (buf == NULL) {
  /* 割り当てエラーの処理 */
}

strncpy(buf, str, len);

/* null 終端する */
buf[len] = '\0'

/* buf の処理 */

free(buf);
buf = NULL;
リスク評価

メモリ内容の消去や文字列の明示的な終端処理を行わないと、情報漏えいを引き起こす可能性がある。たとえば、実際には null 終端バイトがないのにあるとプログラマが仮定していると、バッファオーバーフローを引き起こすおそれもある。

ルール

深刻度

可能性

修正コスト

優先度

レベル

MEM09-C

P4

L3

自動検出

ツール

バージョン

チェッカー

説明

LDRA tool suite

V. 8.5.4

 

 

関連するガイドライン
CERT C++ Secure Coding Standard MEM09-CPP. Do not assume memory allocation routines initialize memory
ISO/IEC TR 17961 (ドラフト) Referencing uninitialized memory [uninitref]
MITRE CWE CWE-119, Failure to constrain operations within the bounds of an allocated memory buffer
CWE-665, Incorrect or incomplete initialization
参考資料
[Graff 2003]  
[[ISO/IEC 9899:2011] Section 7.22.3.4, "The malloc Function"
Section 7.22.3.5, "The realloc Function"
[Sun]  
翻訳元

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

MEM09-C. Do not assume memory allocation functions initialize memory (revision 97)

Top へ

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