MEM34-C. 動的に割り当てられたメモリのみを解放する
C 標準 [ISO/IEC 9899:2011] 附属書 J 「未定義の動作」の 179 に記載の通り、以下の場合、プログラムの動作は未定義である。
freeまたはrealloc関数へのポインタ引数がメモリ管理関数によって以前に返されたポインタと一致しないか、もしくは、領域がfreeまたはreallocによって解放された。
動的に割り当てられていないメモリを解放すると、「MEM31-C. 動的に割り当てられたメモリは一度だけ解放する」で説明されているような深刻なエラーにつながる可能性がある。エラーの結果何が起こるかは処理系により異なる。何も起こらないかもしれないし、プログラムが異常終了するかもしれない。処理系に関係なく、malloc()、calloc()、realloc()、aligned_alloc などの動的メモリ割り当て関数によって変えさえたポインタ以外での free() の呼び出しは避ける。
realloc() に、動的に割り当てられていないメモリへのポインタが指定された場合も同様の状況が生じる。realloc() 関数を使い、動的メモリのブロックサイズを変更できるが、malloc() などのメモリ割り当て関数によって割り当てられたものでないメモリへのポインタが指定されると、プログラムは異常終了する場合がある。
違反コード
以下のコード例では、argc の値に応じて、動的に割り当てられたメモリもしくは静的に割り当てられた文字列リテラルを参照するように str の値をセットする。どちらの場合も str は free() に引数として渡される。str が動的に割り当てられたメモリ以外を参照している場合、free(str) を実行してはならない。
enum { MAX_ALLOCATION = 1000 };
int main(int argc, const char *argv[]) {
char *str = NULL;
size_t len;
if (argc == 2) {
len = strlen(argv[1])+1;
if (len > MAX_ALLOCATION) {
/* エラー処理 */
}
str = (char *)malloc(len);
if (str == NULL) {
/* 割り当てエラーの処理 */
}
strcpy(str, argv[1]);
}
else {
str = "usage: $>a.exe [string]";
printf("%s\n", str);
}
/* ... */
free(str);
return 0;
}
適合コード
以下の解決法では、str が free() に渡されたときに動的に割り当てられていないメモリを参照する可能性を排除している。
enum { MAX_ALLOCATION = 1000 };
int main(int argc, const char *argv[]) {
char *str = NULL;
size_t len;
if (argc == 2) {
len = strlen(argv[1])+1;
if (len > MAX_ALLOCATION) {
/* エラー処理 */
}
str = (char *)malloc(len);
if (str == NULL) {
/* 割り当てエラーの処理 */
}
strcpy(str, argv[1]);
}
else {
printf("%s\n", "usage: $>a.exe [string]");
return -1;
}
/* ... */
free(str);
return 0;
}
違反コード (realloc())
以下のコード例では、realloc()、buf を指すポインタパラメータは、動的に割り当てられたメモリを参照していない。
#define BUFSIZE 256
void f(void) {
char buf[BUFSIZE];
char *p;
/* ... */
p = (char *)realloc(buf, 2 * BUFSIZE); /* 違反 */
/* ... */
}
適合コード (realloc())
この適合コードでは、buf が動的に割り当てられたメモリを参照している。
#define BUFSIZE 256
void f(void) {
char *buf = (char *)malloc(BUFSIZE * sizeof(char));
char *p;
/* ... */
p = (char *)realloc(buf, 2 * BUFSIZE); /* 違反 */
/* ... */
}
例外
MEM34-EX1:一部のライブラリ処理系では、割り当てられていないメモリの解放を受け入れて無視する (そうでないと、代わりに実行時制約違反が引き起こされる)。プロジェクトで使用されているすべてのライブラリがこのように動作すると検証されている場合は、このルールを無視して構わない。
リスク評価
動的に割り当てられていないメモリの解放や再割り当てを行うと、そのメモリが malloc() によって再利用される場合に任意のコードを実行される可能性がある。
|
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
|---|---|---|---|---|---|
|
MEM34-C |
高 |
高 |
中 |
P18 |
L1 |
自動検出(最新の情報はこちら)
|
ツール |
バージョン |
チェッカー |
説明 |
|---|---|---|---|
|
Compass/ROSE |
このルールの違反を検出できる。 |
||
|
Coverity |
6.5 |
BAD_FREE |
引数が関数または配列へのポインタの場合に、 |
| V. 9.1 |
FNH.MIGHT |
||
|
LDRA tool suite |
V. 8.5.4 |
483 S |
実装済み |
関連するガイドライン
| CERT C++ Secure Coding Standard | MEM34-CPP. Only free memory allocated dynamically |
| ISO/IEC TS 17961 (ドラフト) | Reallocating or freeing memory that was not dynamically allocatied [xfree] |
| MITRE CWE | CWE-590, Free of invalid pointer not on the heap |
参考資料
| [Seacord 2013] | Chapter 4, "Dynamic Memory Management" |
翻訳元
これは以下のページを翻訳したものです。
MEM34-C. Only free memory allocated dynamically (revision 94)
