warning: double-‘free’ of ‘p’ [CWE-415]
警告:二重解放
[-Wanalyzer-double-free]
■free(ポインタ)でポインタはNULLになる?
#include <stdio.h>
#include <stdlib.h>
int main(void){
char *ptr = malloc(256);
if(ptr == NULL)
return 1;
free(ptr);//解放
if(ptr == NULL)
puts("freeしたらNULLになる");
else
puts("freeしてもNULLにはならない");
}
freeしたら
free関数がポインタをNULLにしてくれると
思っている人は多いですが
NULLにはならず元のままです。
参考:
■二重解放バグの例
#include <stdio.h>
#include <stdlib.h>
int main(void){
char *p = malloc(1024);
free(p);
free(p);//二重に解放している
}
二重解放はメモリが破壊され大きな問題になります。
gcc -fanalyzer
gcc -fsanitize=address
で問題検出可能です。
■単純な対策
#include <stdio.h>
#include <stdlib.h>
int main(void){
char *p = malloc(1024);
free(p);
p = NULL ; //free直後にNULLを設定する
free(p); //free(NULL)は問題ない二重解放にならない
p = NULL ; //これは無駄 CERT-C MEM01-EX1 参照
}
良く知られた対策に
free()直後にNULLを設定する手法があります。
■間違った対策(関数化1版)
#include <stdio.h>
#include <stdlib.h>
void MY_free(void *ptr){
free(ptr);
ptr = NULL ;//★NG:無駄意味が無い
}
int main(void){
char *p = malloc(1024);
MY_free(p);
MY_free(p);
}
毎回NULLを設定するのは面倒なので関数化したくなりますが、
MY_freeは初心者がよく書く間違った関数化です。
5行目でNULLを設定しても、
関数呼び元のpは何も変わりません。
■間違った対策(関数化2版)
#include <stdio.h>
#include <stdlib.h>
void MY_free(void **vp){//★NG:C-FAQ 4.9
free(*vp);
*vp = NULL;
}
int main(void){
char *p = malloc(1024);
MY_free(&p);
MY_free(&p);
}
コンパイラに怒られます。
warning: incompatible pointer types passing ‘char **’ to parameter of type ‘void **’
■間違った対策(関数化3版)
#include <stdio.h>
#include <stdlib.h>
void MY_free(void *vp){
void **vpp = vp;//★NG:C-FAQ 4.9
free(*vpp);
*vpp = NULL;
}
int main(void){
char *p = malloc(1024);
MY_free(&p);
MY_free(&p);
}
コンパイラの警告は消えましたが
void ** は移植性が無いと C-FAQ4.9 に書いてありました。
さらに呼び元の
MY_free(&p)で&を忘れる可能性が高いです。
■推奨する対策(マクロ版)
#include <stdio.h>
#include <stdlib.h>
//二重解放対策マクロ
#define free(x) (free(x),(x)=NULL)
int main(void){
char *p = malloc(1024);
free(p);
free(p);
}
マクロを使うと簡単です。
free呼び元の変更も不要です。
マクロ定義が再帰しそうですが大丈夫!
問題なくコンパイルできます。
■練習問題
次のうちで正しいのはどれでしょう?
(1) free(NULL)で
プログラムが異常終了する可能性がある
(2)fclose(NULL)で
プログラムが異常終了する可能性がある
(3)free(ptr)を実行すると
free関数がptrをNULLにしてくれる
(4)free()関数はメモリを開放する
(5)free()関数はメモリを解放する
参考: