https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105087

            Bug ID: 105087
           Summary: fanalyzer double free false positive with vasprintf
           Product: gcc
           Version: 12.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: analyzer
          Assignee: dmalcolm at gcc dot gnu.org
          Reporter: bcl at redhat dot com
  Target Milestone: ---

Created attachment 52703
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=52703&action=edit
Minimal reproducer main.c

While building parted I've run into what seems like a bug. I've created a
minimal reproducer which I'll attach. In parted we have a wrapper around
vasprintf called zasprintf, and it looks like the analyzer doesn't understand
that vasprintf returns a new allocation on each call which is passed back out
of the function.

Compiling it with 'gcc -o main -g -fanalyzer -fdump-analyzer -Wall ./main.c'

results in a complaint that there is a double free of buf:

./main.c: In function ‘run_test’:
./main.c:48:5: warning: double-‘free’ of ‘bar’ [CWE-415]
[-Wanalyzer-double-free]
   48 |     free(bar);
      |     ^~~~~~~~~
  ‘main’: events 1-2
    |
    |   54 | int main(int argc, char **argv) {
    |      |     ^~~~
    |      |     |
    |      |     (1) entry to ‘main’
    |   55 |     return run_test();
    |      |            ~~~~~~~~~~
    |      |            |
    |      |            (2) calling ‘run_test’ from ‘main’
    |
    +--> ‘run_test’: events 3-4
           |
           |   21 | int run_test() {
           |      |     ^~~~~~~~
           |      |     |
           |      |     (3) entry to ‘run_test’
           |......
           |   29 |     buf = zasprintf("i = %d", i);
           |      |           ~~~~~~~~~~~~~~~~~~~~~~
           |      |           |
           |      |           (4) calling ‘zasprintf’ from ‘run_test’
           |
           +--> ‘zasprintf’: events 5-7
                  |
                  |   11 | zasprintf (const char *format, ...)
                  |      | ^~~~~~~~~
                  |      | |
                  |      | (5) entry to ‘zasprintf’
                  |......
                  |   18 |   return r < 0 ? NULL : resultp;
                  |      |          ~~~~~~~~~~~~~~~~~~~~~~
                  |      |                       |
                  |      |                       (6) following ‘true’ branch
(when ‘r >= 0’)...
                  |      |                       (7) ...to here
                  |
           <------+
           |
         ‘run_test’: events 8-9
           |
           |   29 |     buf = zasprintf("i = %d", i);
           |      |           ^~~~~~~~~~~~~~~~~~~~~~
           |      |           |
           |      |           (8) returning to ‘run_test’ from ‘zasprintf’
           |......
           |   34 |     bar = zasprintf("i = %d - %d", i, i - 13);
           |      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           |      |           |
           |      |           (9) calling ‘zasprintf’ from ‘run_test’
           |
           +--> ‘zasprintf’: events 10-12
                  |
                  |   11 | zasprintf (const char *format, ...)
                  |      | ^~~~~~~~~
                  |      | |
                  |      | (10) entry to ‘zasprintf’
                  |......
                  |   18 |   return r < 0 ? NULL : resultp;
                  |      |          ~~~~~~~~~~~~~~~~~~~~~~
                  |      |                       |
                  |      |                       (11) following ‘true’ branch
(when ‘r >= 0’)...
                  |      |                       (12) ...to here
                  |
           <------+
           |
         ‘run_test’: events 13-14
           |
           |   34 |     bar = zasprintf("i = %d - %d", i, i - 13);
           |      |           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           |      |           |
           |      |           (13) returning to ‘run_test’ from ‘zasprintf’
           |......
           |   40 |     baz = zasprintf("No i's here");
           |      |           ~~~~~~~~~~~~~~~~~~~~~~~~
           |      |           |
           |      |           (14) calling ‘zasprintf’ from ‘run_test’
           |
           +--> ‘zasprintf’: events 15-17
                  |
                  |   11 | zasprintf (const char *format, ...)
                  |      | ^~~~~~~~~
                  |      | |
                  |      | (15) entry to ‘zasprintf’
                  |......
                  |   18 |   return r < 0 ? NULL : resultp;
                  |      |          ~~~~~~~~~~~~~~~~~~~~~~
                  |      |                       |
                  |      |                       (16) following ‘true’ branch
(when ‘r >= 0’)...
                  |      |                       (17) ...to here
                  |
           <------+
           |
         ‘run_test’: events 18-20
           |
           |   40 |     baz = zasprintf("No i's here");
           |      |           ^~~~~~~~~~~~~~~~~~~~~~~~
           |      |           |
           |      |           (18) returning to ‘run_test’ from ‘zasprintf’
           |......
           |   47 |     free(buf);
           |      |     ~~~~~~~~~
           |      |     |
           |      |     (19) first ‘free’ here
           |   48 |     free(bar);
           |      |     ~~~~~~~~~
           |      |     |
           |      |     (20) second ‘free’ here; first ‘free’ was at (19)
           |

Reply via email to