
warning: Use After Free
警告:メモリの解放後参照
[-Wanalyzer-use-after-free]
■use-after-freeのバグ例
void freeChain(Node* head) {
// 順番に解放
for(Node* p = head; p != NULL; p = p->next){
free(p);
}
}
free(p) で
p を解放した後に
for 文第3式で
p->next を参照しているので解放後参照のバグとなります。
このチェーン解放のバグは昔からよくある間違いで
聖典 プログラミング言語C 7.8.5章でも紹介されています。
■use-after-freeの修正例
void freeChain(Node* head) {
Node *tmp;
for(Node* p = head; p != NULL; p = tmp){
tmp = p->next;
free(p);
}
}
■リスト構造(コンパイル可能)
#include <stdio.h>
#include <stdlib.h>
// ノードの構造体
typedef struct Node {
int data;
struct Node *next;
} Node ;
// 新しいノードを作成する関数
Node *createNode(int data) {
Node *newNode = malloc(sizeof(Node));
if(newNode == NULL){
perror(NULL);
exit(1);
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// チェーンにノードを追加する関数
void appendNode(Node** head, int data) {
Node* newNode = createNode(data);
// チェーンが空の場合
if (*head == NULL) {
*head = newNode;
return;
}
Node* currentNode = *head;
// チェーンの末尾まで移動
while (currentNode->next != NULL) {
currentNode = currentNode->next;
}
// 新しいノードを末尾に追加
currentNode->next = newNode;
}
// チェーンの要素を表示する関数
void printChain(Node* head) {
// チェーンの要素を順番に表示
for(Node* p = head; p != NULL; p = p->next){
printf("%d\n", p->data);
}
}
// チェーンの解放
#ifdef BUG
void freeChain(Node* head) {
// 順番に解放
for(Node* p = head; p != NULL; p = p->next){
free(p);
}
}
#else
void freeChain(Node* head) {
Node *tmp;
for(Node* p = head; p != NULL; p = tmp){
tmp = p->next;
free(p);
}
}
#endif
int main(void) {
// 空のチェーンを作成
Node* head = NULL;
// ノードを追加
appendNode(&head, 1);
appendNode(&head, 2);
appendNode(&head, 3);
// チェーンの要素を表示
printChain(head);
// チェーンの解放
freeChain(head);
}
リスト構造のチェーンの解放処理で
Use-After-Freeの問題がよく発生します。
この問題は
gcc -fanalyzer
gcc -fsanitize=address
等で検出可能です。
