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