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)
|