MEM30-C. 解放済みメモリにアクセスしない
算術演算のオペランドとしての逆参照または動作、型のキャスト、または代入の右手側としてのポインタの使用を含め、動的記憶域に割り当てられていたブロックがメモリ管理関数によって解放された後、解放されたメモリを指すポインタは評価しない。
C 標準によれば、free()
または realloc()
関数の呼び出しによって解放された領域を指すポインタの値を使用しているプログラム動作は未定義である。 (附属書 J 「未定義の動作」の 177 を参照。)
解放されたメモリを指すポインタの値を読み取ると、ポインタ値は不定であり、さらにトラップ表現の場合があるため、未定義の動作となる。 後者の場合、それによってハードウェアトラップを引き起こす可能性がある。
一度解放されたメモリにアクセスすると、ヒープの管理に使用されているデータ構造を壊す可能性がある。割り当てを解除されたメモリを参照するポインタはダングリングポインタと呼ばれる。 ダングリングポインタにアクセスすると、攻撃可能な脆弱性を引き起こす可能性がある。
メモリが解放されても、メモリの内容がそのまま残りアクセス可能な場合がある。これは、解放されたブロックをいつ再割り当てし再使用するかは、メモリマネージャが決定するためである。解放された位置にあるデータは有効であるかのように見えるかもしれない。しかし、このデータは予期せず変更される可能性があり、意図しないプログラム動作を引き起こしうる。そのため、一度解放したメモリへの書き込みや、メモリの読み取りを行わないことを保証する必要がある。
違反コード
K & R [Kernighan 1988](邦題:『プログラミング言語C』)のこの例では、リンクリストから項目を削除する誤った手法と正しい手法の両方を示している。 誤った手法は、p->next
が参照される前に p
を解放しており、p->next
はすでに解放されたメモリを読み取るため、本の中でも明白に誤りだと記されている。
for (p = head; p != NULL; p = p->next) free(p);
適合コード
K&Rは正しい解決法も示している。 このエラーを修正するために、p
を解放する前に p->next
への参照を q
に格納している。
for (p = head; p != NULL; p = q) { q = p->next; free(p); } head = NULL;
違反コード
以下のコード例では、buff
が解放されたあとに、buff
への書き込みが行われている。 この手の脆弱性の悪用は容易であり、脆弱なプロセスの権限で任意のコードが実行される可能性があるが、この例ほどわかりやすいことはまれである。一般に、割り当てと解放が行われる場所は遠く離れており、問題に気付き原因をつきとめることはむずかしい。
int main(int argc, const char *argv[]) { char *buff; buff = (char *)malloc(BUFFERSIZE); if (!buff) { /* エラー条件の処理 */ } /* ... */ free(buff); /* ... */ strncpy(buff, argv[1], BUFFERSIZE-1); }
適合コード
以下の解決法では、メモリが不要になってからメモリを解放している。
int main(int argc, const char *argv[]) { char *buff; buff = (char *)malloc(BUFFERSIZE); if (!buff) { /* エラー条件の処理 */ } /* ... */ strncpy(buff, argv[1], BUFFERSIZE-1); /* ... */ free(buff); }
違反コード
libwmf
バージョン 0.2.8.4 の以下のコード例 (CVE-2009-1364) では、返り値 gdRealloc
(im->clip->list
が指す領域を再割り当てするシンプルなラップアラウンド realloc
) は more
に設定される。 ただし、im->clip->list
の値は後から直接コードで使用され、C 標準では realloc
が指す領域を移動した場合、元の領域は解放されると規定されている。 攻撃者は、再割り当て (十分な im->clip->count
) を強制的に実行し、解放されたメモリにアクセスすることで任意のコードを実行できる [xorl 2009]。
void gdClipSetAdd(gdImagePtr im,gdClipRectanglePtr rect) { gdClipRectanglePtr more; if (im->clip == 0) { ... } if (im->clip->count == im->clip->max) { more = gdRealloc (im->clip->list,(im->clip->max + 8) * sizeof (gdClipRectangle)); if (more == 0) return; //if the realloc fails, then we have not lost the im->clip->list value im->clip->max += 8; } im->clip->list[im->clip->count] = (*rect); im->clip->count++;
適合コード
適合コードでは、realloc
を呼び出した後、im->clip->list
を値 more
に再割り当てする。
void gdClipSetAdd(gdImagePtr im,gdClipRectanglePtr rect) { gdClipRectanglePtr more; if (im->clip == 0) { ... } if (im->clip->count == im->clip->max) { more = gdRealloc (im->clip->list,(im->clip->max + 8) * sizeof (gdClipRectangle)); if (more == 0) return; im->clip->max += 8; im->clip->list = more; } im->clip->list[im->clip->count] = (*rect); im->clip->count++;
リスク評価
解放済みのメモリを読み取ると、プログラムの異常終了やサービス運用妨害(DoS)攻撃を引き起こす可能性がある。解放済みのメモリに書き込むと、バッファオーバーフローや、脆弱なプロセスの権限を使った任意のコード実行につながる可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
MEM30-C |
高 |
高 |
中 |
P18 |
L1 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Compass/ROSE |
|
|
|
Coverity |
6.5 |
USE_AFTER_FREE |
メモリが複数回解放されるか、または解放されたポインタの対象に対して読み取り/書き込みを行う特定の事例を検出できる。 |
Fortify SCA |
5.0 |
|
|
V. 9.1 |
UFM.DEREF.MIGHT |
|
|
LDRA tool suite |
V. 8.5.4 |
51 D |
実装済み |
Splint |
V. 3.1.1 |
|
|
関連するガイドライン
CERT C++ Secure Coding Standard | MEM30-CPP. Do not access freed memory |
ISO/IEC TR 24772:2013 | Dangling References to Stack Frames [DCM] Dangling Reference to Heap [XYK] |
ISO/IEC TS 17961 (ドラフト) | Accessing freed memory [accfree] |
MISRA-C | Rule 17.6 |
MITRE CWE | CWE-416, Use after free |
参考資料
[Kernighan 1988] | Section 7.8.5, "Storage Management" |
[OWASP Freed Memory] | |
[Seacord 2013] | Chapter 4, "Dynamic Memory Management" |
[Viega 2005] | Section 5.2.19, "Using Freed Memory" |
[xorl 2009] | CVE-2009-1364: LibWMF Pointer Use after free() |
翻訳元
これは以下のページを翻訳したものです。
MEM30-C. Do not access freed memory (revision 122)