簡體   English   中英

gcc -fanalyzer 抱怨 double free,我不明白為什么

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM