[英]gcc -fanalyzer complains about double free, I don't understand why
編輯:這實際上是一個已知錯誤。
我希望在這方面得到一些幫助,因為我就是不明白。 希望這只是我不明白的事情。
我有幾個函數來初始化和釋放兩種類型的 memory:單數foo
類型和復數foos_list
類型。 這是一個完整的程序,簡化為基本的:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void *
xalloc (size_t len)
{
void *mem = malloc (len);
if (!mem)
{
fprintf (stderr, "can't allocated memory");
exit (1);
}
return mem;
}
void *
xrealloc (void *mem, size_t msize)
{
mem = realloc (mem, msize);
if (!mem)
{
fprintf (stderr, "can't allocate %zu bytes of memory\n", msize);
exit (1);
}
return mem;
}
typedef struct {
char *name;
} foo;
typedef struct {
foo **foos;
size_t foos_len;
} foos_list;
foo *
new_foo ()
{
foo *f = xalloc (sizeof (foo));
f->name = NULL;
return f;
}
void
free_foo (foo *f)
{
if (!f) return;
if (f->name) free (f->name);
free (f);
}
foos_list *
new_foos_list ()
{
foos_list *l = xalloc (sizeof (foos_list));
l->foos = NULL;
l->foos_len = 0;
return l;
}
void
free_foos_list (foos_list *l)
{
if (!l) return;
if (l->foos)
{
for (size_t i = 0; i < l->foos_len; i++)
free_foo (l->foos[i]);
free (l->foos);
}
free (l);
}
int main()
{
}
如果我嘗試編譯它,gcc 會拋出一個 analyzer-use-after-free 錯誤和一個 analyzer-double-free 錯誤。
$ gcc -Wall -Werror -fanalyzer -o test main.c
main.c: In function ‘free_foo’:
main.c:56:8: error: use after ‘free’ of ‘f’ [CWE-416] [-Werror=analyzer-use-after-free]
56 | if (f->name) free (f->name);
| ~^~~~~~
‘free_foos_list’: events 1-8
|
| 72 | free_foos_list (foos_list *l)
| | ^~~~~~~~~~~~~~
| | |
| | (1) entry to ‘free_foos_list’
| 73 | {
| 74 | if (!l) return;
| | ~
| | |
| | (2) following ‘false’ branch (when ‘l’ is non-NULL)...
| 75 |
| 76 | if (l->foos)
| | ~~~~~~~~
| | | |
| | | (3) ...to here
| | (4) following ‘true’ branch...
| 77 | {
| 78 | for (size_t i = 0; i < l->foos_len; i++)
| | ~~~ ~
| | | |
| | | (5) ...to here
| | (6) following ‘true’ branch...
| 79 | free_foo (l->foos[i]);
| | ~~~~~~~~~~~~~~~~~~~~~
| | | |
| | | (7) ...to here
| | (8) calling ‘free_foo’ from ‘free_foos_list’
|
+--> ‘free_foo’: events 9-10
|
| 53 | free_foo (foo *f)
| | ^~~~~~~~
| | |
| | (9) entry to ‘free_foo’
|......
| 58 | free (f);
| | ~~~~~~~~
| | |
| | (10) freed here
|
<------+
|
‘free_foos_list’: events 11-14
|
| 78 | for (size_t i = 0; i < l->foos_len; i++)
| | ~~~
| | |
| | (12) following ‘true’ branch...
| 79 | free_foo (l->foos[i]);
| | ^~~~~~~~~~~~~~~~~~~~~
| | | |
| | | (13) ...to here
| | (11) returning to ‘free_foos_list’ from ‘free_foo’
| | (14) calling ‘free_foo’ from ‘free_foos_list’
|
+--> ‘free_foo’: events 15-20
|
| 53 | free_foo (foo *f)
| | ^~~~~~~~
| | |
| | (15) entry to ‘free_foo’
| 54 | {
| 55 | if (!f) return;
| | ~
| | |
| | (16) following ‘false’ branch (when ‘f’ is non-NULL)...
| 56 | if (f->name) free (f->name);
| | ~~~~~~~~ ~~~~~~~
| | | | |
| | | | (19) ...to here
| | | (17) ...to here
| | (18) following ‘true’ branch...
| 57 |
| 58 | free (f);
| | ~~~~~~~~
| | |
| | (20) freed here
|
<------+
|
‘free_foos_list’: events 21-25
|
| 78 | for (size_t i = 0; i < l->foos_len; i++)
| | ~~~
| | |
| | (22) following ‘true’ branch...
| 79 | free_foo (l->foos[i]);
| | ^~~~~~~~~~~~~~~~~~~~~
| | | |
| | | (23) ...to here
| | | (24) freed here
| | (21) returning to ‘free_foos_list’ from ‘free_foo’
| | (25) calling ‘free_foo’ from ‘free_foos_list’
|
+--> ‘free_foo’: events 26-29
|
| 53 | free_foo (foo *f)
| | ^~~~~~~~
| | |
| | (26) entry to ‘free_foo’
| 54 | {
| 55 | if (!f) return;
| | ~
| | |
| | (27) following ‘false’ branch (when ‘f’ is non-NULL)...
| 56 | if (f->name) free (f->name);
| | ~~~~~~~
| | |
| | (28) ...to here
| | (29) use after ‘free’ of ‘f’; freed at (24)
|
main.c:56:16: error: double-‘free’ of ‘<unknown>’ [CWE-415] [-Werror=analyzer-double-free]
56 | if (f->name) free (f->name);
| ^~~~~~~~~~~~~~
‘free_foos_list’: events 1-10
|
| 72 | free_foos_list (foos_list *l)
| | ^~~~~~~~~~~~~~
| | |
| | (1) entry to ‘free_foos_list’
| 73 | {
| 74 | if (!l) return;
| | ~
| | |
| | (2) following ‘false’ branch (when ‘l’ is non-NULL)...
| 75 |
| 76 | if (l->foos)
| | ~~~~~~~~
| | | |
| | | (3) ...to here
| | (4) following ‘true’ branch...
| 77 | {
| 78 | for (size_t i = 0; i < l->foos_len; i++)
| | ~~~ ~
| | | |
| | | (5) ...to here
| | (6) following ‘true’ branch...
| | (8) following ‘true’ branch...
| 79 | free_foo (l->foos[i]);
| | ~~~~~~~~~~~~~~~~~~~~~
| | | |
| | | (7) ...to here
| | | (9) ...to here
| | (10) calling ‘free_foo’ from ‘free_foos_list’
|
+--> ‘free_foo’: events 11-15
|
| 53 | free_foo (foo *f)
| | ^~~~~~~~
| | |
| | (11) entry to ‘free_foo’
| 54 | {
| 55 | if (!f) return;
| | ~
| | |
| | (12) following ‘false’ branch (when ‘f’ is non-NULL)...
| 56 | if (f->name) free (f->name);
| | ~~~~~~~~ ~~~~~~~
| | | | |
| | | | (15) ...to here
| | | (13) ...to here
| | (14) following ‘true’ branch...
|
<------+
|
‘free_foos_list’: events 16-19
|
| 78 | for (size_t i = 0; i < l->foos_len; i++)
| | ~~~
| | |
| | (17) following ‘true’ branch...
| 79 | free_foo (l->foos[i]);
| | ^~~~~~~~~~~~~~~~~~~~~
| | | |
| | | (18) ...to here
| | (16) returning to ‘free_foos_list’ from ‘free_foo’
| | (19) calling ‘free_foo’ from ‘free_foos_list’
|
+--> ‘free_foo’: events 20-25
|
| 53 | free_foo (foo *f)
| | ^~~~~~~~
| | |
| | (20) entry to ‘free_foo’
| 54 | {
| 55 | if (!f) return;
| | ~
| | |
| | (21) following ‘false’ branch (when ‘f’ is non-NULL)...
| 56 | if (f->name) free (f->name);
| | ~~~~~~~~ ~~~~~~~~~~~~~~
| | | | | |
| | | | | (24) ...to here
| | | | (25) second ‘free’ here
| | | (22) ...to here
| | (23) following ‘true’ branch...
|
cc1: all warnings being treated as errors
如果我理解正確,它會抱怨我在釋放f
后使用f->name
,除了當我按照它的說明操作時,釋放的 foo 是l->foos[0]
而分析器認為已經釋放的是l->foos[1]
(它還沒有被釋放)。
更讓我困惑的是,如果我真的使用這些功能,錯誤就會消失。 這是一個主要的 function 使用它們,如果我在以前的文件中用它替換主要的 function,它編譯得很好:
int main()
{
foos_list *l = new_foos_list ();
foo *f = new_foo ();
foo *f2 = new_foo ();
l->foos_len++;
l->foos = xrealloc (l->foos, sizeof(foo) * l->foos_len);
l->foos[0] = f;
l->foos_len++;
l->foos = xrealloc (l->foos, sizeof(foo) * l->foos_len);
l->foos[1] = f2;
free_foos_list (l);
}
我的問題是,當然,我不想分配和釋放 foos,我在一個更復雜的程序中遇到了這個問題,在這個程序中,函數在聲明的不同文件中使用,並且觸發了相同的 fanalyzer 錯誤。
所以我的問題是:這是一個 fanalyzer 錯誤(如果是,是否有解決方法),或者這是我這邊的錯誤(以及如何修復)? 謝謝。
這似乎是一個-fanalyzer
問題。
我檢查了你的代碼,沒有發現泄漏。
我的gcc
版本似乎不支持-fanalyzer
,所以我無法對其進行測試。
我下載並構建了您的程序並在valgrind
下運行它。 我為您的原始main
和我使用更廣泛的診斷創建的 main 做了這個。
他們都沒有報告任何類型的錯誤。
請注意,您分配了太多空間(浪費,但無害):
l->foos = xrealloc(l->foos,sizeof(foo) * l->foos_len);
應該:
l->foos = xrealloc(l->foos,sizeof(foo *) * l->foos_len);
或者,更好的是:
l->foos = xrealloc(l->foos,sizeof(*l->foos) * l->foos_len);
這是我的版本:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void *
xalloc(size_t len)
{
void *mem = malloc(len);
if (!mem) {
fprintf(stderr, "can't allocated memory");
exit(1);
}
return mem;
}
void *
xrealloc(void *mem, size_t msize)
{
mem = realloc(mem, msize);
if (!mem) {
fprintf(stderr, "can't allocate %zu bytes of memory\n", msize);
exit(1);
}
return mem;
}
typedef struct {
char *name;
} foo;
typedef struct {
foo **foos;
size_t foos_len;
} foos_list;
foo *
new_foo()
{
foo *f = xalloc(sizeof(foo));
f->name = NULL;
return f;
}
void
free_foo(foo *f)
{
if (!f)
return;
if (f->name)
free(f->name);
free(f);
}
foos_list *
new_foos_list(void)
{
foos_list *l = xalloc(sizeof(foos_list));
l->foos = NULL;
l->foos_len = 0;
return l;
}
void
free_foos_list(foos_list *l)
{
if (! l)
return;
if (l->foos) {
for (size_t i = 0; i < l->foos_len; i++)
free_foo(l->foos[i]);
free(l->foos);
}
free(l);
}
int
main()
{
char name[100];
foos_list *l = new_foos_list();
for (int idx = 0; idx < 30; ++idx) {
foo *f = new_foo();
sprintf(name,"N%d",idx);
f->name = strdup(name);
size_t count = l->foos_len++;
l->foos = xrealloc(l->foos,sizeof(*l->foos) * (count + 1));
l->foos[count] = f;
}
int totlen = 0;
for (int idx = 0; idx < l->foos_len; ++idx) {
foo *f = l->foos[idx];
totlen += printf(" %s",f->name);
if (totlen > 70) {
printf("\n");
totlen = 0;
}
}
printf("\n");
free_foos_list(l);
return 0;
}
這是我的版本的 output:
N0 N1 N2 N3 N4 N5 N6 N7 N8 N9 N10 N11 N12 N13 N14 N15 N16 N17 N18 N19 N20
N21 N22 N23 N24 N25 N26 N27 N28 N29
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.